diff --git a/insight.sln b/insight.sln
index 4cde53e..d27bbe4 100644
--- a/insight.sln
+++ b/insight.sln
@@ -41,6 +41,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Remote.Windows", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Infrastructure.Web", "src\Core\Insight.Infrastructure.Web\Insight.Infrastructure.Web.csproj", "{39B81A0D-A88C-44D3-9624-1A19C78A4310}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Server2", "src\Server\Insight.Server2\Insight.Server2.csproj", "{9F7E88B2-7415-410C-9C31-7720596B0607}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -91,6 +93,10 @@ Global
{39B81A0D-A88C-44D3-9624-1A19C78A4310}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39B81A0D-A88C-44D3-9624-1A19C78A4310}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39B81A0D-A88C-44D3-9624-1A19C78A4310}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9F7E88B2-7415-410C-9C31-7720596B0607}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9F7E88B2-7415-410C-9C31-7720596B0607}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9F7E88B2-7415-410C-9C31-7720596B0607}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9F7E88B2-7415-410C-9C31-7720596B0607}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -107,6 +113,7 @@ Global
{5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B} = {D4D7BF4A-B2E3-470A-A14C-FC658FF7461D}
{AF313B47-3079-407F-91D1-9989C1E1AF2A} = {D4D7BF4A-B2E3-470A-A14C-FC658FF7461D}
{39B81A0D-A88C-44D3-9624-1A19C78A4310} = {88B03853-2215-4E52-8986-0E76602E5F21}
+ {9F7E88B2-7415-410C-9C31-7720596B0607} = {038C3821-E554-496D-B585-A3BC193B7913}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F376A326-7590-4E7E-AB9B-76CED8527AB0}
diff --git a/src/Agent/Insight.Agent/Insight.Agent.csproj b/src/Agent/Insight.Agent/Insight.Agent.csproj
index 2dc4de2..27e5746 100644
--- a/src/Agent/Insight.Agent/Insight.Agent.csproj
+++ b/src/Agent/Insight.Agent/Insight.Agent.csproj
@@ -13,11 +13,6 @@
none
-
-
-
-
-
none
@@ -27,10 +22,9 @@
-
-
+
diff --git a/src/Agent/Insight.Agent/Internal/Config.cs b/src/Agent/Insight.Agent/Internal/Config.cs
new file mode 100644
index 0000000..f79bdff
--- /dev/null
+++ b/src/Agent/Insight.Agent/Internal/Config.cs
@@ -0,0 +1,6 @@
+namespace Insight.Agent;
+
+internal sealed class Config
+{
+ public Guid? Serial { get; set; }
+}
\ No newline at end of file
diff --git a/src/Agent/Insight.Agent/Internals/Extensions.cs b/src/Agent/Insight.Agent/Internal/Extensions.cs
similarity index 100%
rename from src/Agent/Insight.Agent/Internals/Extensions.cs
rename to src/Agent/Insight.Agent/Internal/Extensions.cs
diff --git a/src/Agent/Insight.Agent/Internals/Helpers.cs b/src/Agent/Insight.Agent/Internal/Helpers.cs
similarity index 100%
rename from src/Agent/Insight.Agent/Internals/Helpers.cs
rename to src/Agent/Insight.Agent/Internal/Helpers.cs
diff --git a/src/Agent/Insight.Agent/Models/Config.cs b/src/Agent/Insight.Agent/Models/Config.cs
deleted file mode 100644
index 4fdd2ae..0000000
--- a/src/Agent/Insight.Agent/Models/Config.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Insight.Agent.Models;
-
-public class Config
-{
- public Guid? Serial { get; set; }
-}
\ No newline at end of file
diff --git a/src/Agent/Insight.Agent/Network/AgentSession.cs b/src/Agent/Insight.Agent/Network/AgentSession.cs
index 8b23245..7e6ed26 100644
--- a/src/Agent/Insight.Agent/Network/AgentSession.cs
+++ b/src/Agent/Insight.Agent/Network/AgentSession.cs
@@ -1,5 +1,8 @@
-using Insight.Domain.Interfaces;
+using Insight.Agent.Services;
+using Insight.Domain.Constants;
+using Insight.Domain.Interfaces;
using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
using Microsoft.Extensions.Logging;
using Vaitr.Network;
@@ -31,6 +34,11 @@ public class AgentSession(IEnumerable> handlers, I
{
await base.OnReceivedAsync(context, cancellationToken);
+ // catch authentication request
+ if (context.Packet is AuthenticationRequest)
+ await OnAuthenticationAsync(cancellationToken);
+
+ // pass message to handlers
foreach (var handler in _handlers)
{
try
@@ -49,4 +57,31 @@ public class AgentSession(IEnumerable> handlers, I
_logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
return default;
}
+
+ private async ValueTask OnAuthenticationAsync(CancellationToken cancellationToken)
+ {
+ Config? config = null;
+
+ try
+ {
+ config = await Configurator.ReadAsync(Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError("Config ({config}) read error ({exception})", Configuration.DefaultConfig, ex);
+ }
+
+ if (config is null)
+ {
+ config = new Config { Serial = Guid.NewGuid() };
+ await Configurator.WriteAsync(config, Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false);
+ }
+
+ await SendAsync(new AuthenticationResponse
+ {
+ Serial = config.Serial ?? throw new InvalidDataException(nameof(config.Serial)),
+ Version = Configuration.Version,
+ Hostname = Configuration.Hostname
+ }, cancellationToken).ConfigureAwait(false);
+ }
}
\ No newline at end of file
diff --git a/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs
deleted file mode 100644
index cc71964..0000000
--- a/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Insight.Agent.Models;
-using Insight.Agent.Services;
-using Insight.Domain.Constants;
-using Insight.Domain.Interfaces;
-using Insight.Domain.Network;
-using Insight.Domain.Network.Agent.Messages;
-
-namespace Insight.Agent.Network.Handlers;
-
-public class AuthenticationHandler : IMessageHandler
-{
- public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
- {
- switch (message)
- {
- case AuthenticationRequest:
- {
- Config? config = null;
-
- try
- {
- config = await Configurator.ReadAsync(Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception) { }
-
- if (config is null)
- {
- config = new Config { Serial = Guid.NewGuid() };
- await Configurator.WriteAsync(config, Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false);
- }
-
- await sender.SendAsync(new AuthenticationResponse
- {
- Serial = config.Serial ?? throw new InvalidDataException(nameof(config.Serial)),
- Version = Configuration.Version,
- Hostname = Configuration.Hostname
- }, cancellationToken);
-
- break;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs
index ed62a70..3f3dabe 100644
--- a/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs
+++ b/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs
@@ -51,7 +51,7 @@ public class OperationSystemHandler : IMessageHandler
if (@object.TryGetValue(properties, "osarchitecture", out var architecture))
{
- if (architecture is not null && architecture.Contains("64", StringComparison.CurrentCultureIgnoreCase))
+ if (architecture is not null && architecture.Contains("64", StringComparison.CurrentCultureIgnoreCase))
os.Architecture = Architecture.X64;
}
else
diff --git a/src/Agent/Insight.Agent/Program.cs b/src/Agent/Insight.Agent/Program.cs
index 3b07be9..3d85d84 100644
--- a/src/Agent/Insight.Agent/Program.cs
+++ b/src/Agent/Insight.Agent/Program.cs
@@ -1,8 +1,8 @@
-using Insight.Agent.Extensions;
-using Insight.Agent.Network;
+using Insight.Agent.Network;
using Insight.Agent.Network.Handlers;
using Insight.Agent.Services;
using Insight.Domain.Constants;
+using Insight.Domain.Extensions;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Microsoft.Extensions.Configuration;
@@ -67,7 +67,6 @@ internal class Program
options.UseSerializer>();
});
- services.AddSingleton, AuthenticationHandler>();
services.AddSingleton, ProxyHandler>();
services.AddSingleton, CustomHandler>();
diff --git a/src/Agent/Insight.Agent/Services/_Collector/_Os.cs b/src/Agent/Insight.Agent/Services/_Collector/_Os.cs
index b74254d..d1d2daa 100644
--- a/src/Agent/Insight.Agent/Services/_Collector/_Os.cs
+++ b/src/Agent/Insight.Agent/Services/_Collector/_Os.cs
@@ -1,4 +1,4 @@
-using Insight.Agent.Extensions;
+using Insight.Domain.Extensions;
using Insight.Domain.Network.Agent.Messages;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
diff --git a/src/Agent/Insight.Agent/Services/_Collector/_Session.cs b/src/Agent/Insight.Agent/Services/_Collector/_Session.cs
index a393ee1..5b98531 100644
--- a/src/Agent/Insight.Agent/Services/_Collector/_Session.cs
+++ b/src/Agent/Insight.Agent/Services/_Collector/_Session.cs
@@ -1,4 +1,4 @@
-using Insight.Agent.Extensions;
+using Insight.Domain.Extensions;
using Insight.Domain.Network.Agent.Messages;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
diff --git a/src/Api/Insight.Api/Extensions/ServiceExtensions.cs b/src/Api/Insight.Api/Extensions/ServiceExtensions.cs
index 51342ce..40098f1 100644
--- a/src/Api/Insight.Api/Extensions/ServiceExtensions.cs
+++ b/src/Api/Insight.Api/Extensions/ServiceExtensions.cs
@@ -2,7 +2,7 @@
using Microsoft.OpenApi.Models;
using System.Reflection;
-namespace Insight.Api.Hosting;
+namespace Insight.Api.Extensions;
public static class ServiceExtensions
{
diff --git a/src/Api/Insight.Api/Insight.Api.csproj b/src/Api/Insight.Api/Insight.Api.csproj
index e67d2bf..89f1bc9 100644
--- a/src/Api/Insight.Api/Insight.Api.csproj
+++ b/src/Api/Insight.Api/Insight.Api.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/Api/Insight.Api/Program.cs b/src/Api/Insight.Api/Program.cs
index d9dea66..66b1d11 100644
--- a/src/Api/Insight.Api/Program.cs
+++ b/src/Api/Insight.Api/Program.cs
@@ -1,4 +1,4 @@
-using Insight.Api.Hosting;
+using Insight.Api.Extensions;
using Insight.Domain.Constants;
using Insight.Infrastructure;
using Microsoft.Extensions.FileProviders;
diff --git a/src/Api/Insight.Api/appsettings.Development.json b/src/Api/Insight.Api/appsettings.Development.json
index fff92f2..9bac897 100644
--- a/src/Api/Insight.Api/appsettings.Development.json
+++ b/src/Api/Insight.Api/appsettings.Development.json
@@ -1,7 +1,7 @@
{
"AllowedHosts": "*",
"Urls": "http://127.0.0.1:5000",
- "database": "mongodb://db.insight.local:27017",
+ "mongo.connection": "mongodb://db.insight.local:27017",
"jwt.key": "x5dcaE8fiBmHfgsNrwIEtSWzZkz6gpouzKOIgEiVjxJnW28V1aUnYXF19IcnF5x",
"jwt.exp": 3600,
"jwt.audience": "http://127.0.0.1:5000",
diff --git a/src/Api/Insight.Api/appsettings.json b/src/Api/Insight.Api/appsettings.json
index 21706b1..6b62cc2 100644
--- a/src/Api/Insight.Api/appsettings.json
+++ b/src/Api/Insight.Api/appsettings.json
@@ -1,7 +1,7 @@
{
"AllowedHosts": "*",
"Urls": "http://127.0.0.1:5000",
- "database": "mongodb://127.0.0.1:27017",
+ "mongo.connection": "mongodb://127.0.0.1:27017",
"jwt.key": "x5dcaE8fiBmHfgsNrwIEtSWzZkz6gpouzKOIgEiVjxJnW28V1aUnYXF19IcnF5x",
"jwt.exp": 3600,
"jwt.audience": "https://insight.webmatic.de/api",
diff --git a/src/Agent/Insight.Agent/Extensions/Configuration.cs b/src/Core/Insight.Domain/Extensions/Configuration.cs
similarity index 93%
rename from src/Agent/Insight.Agent/Extensions/Configuration.cs
rename to src/Core/Insight.Domain/Extensions/Configuration.cs
index 8646d47..592de33 100644
--- a/src/Agent/Insight.Agent/Extensions/Configuration.cs
+++ b/src/Core/Insight.Domain/Extensions/Configuration.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration;
-namespace Insight.Agent.Extensions;
+namespace Insight.Domain.Extensions;
public static class ConfigurationExtensions
{
diff --git a/src/Agent/Insight.Agent/Extensions/Linux.cs b/src/Core/Insight.Domain/Extensions/Linux.cs
similarity index 95%
rename from src/Agent/Insight.Agent/Extensions/Linux.cs
rename to src/Core/Insight.Domain/Extensions/Linux.cs
index 0574954..0cd076b 100644
--- a/src/Agent/Insight.Agent/Extensions/Linux.cs
+++ b/src/Core/Insight.Domain/Extensions/Linux.cs
@@ -1,7 +1,7 @@
using System.Diagnostics;
using System.Runtime.Versioning;
-namespace Insight.Agent.Extensions;
+namespace Insight.Domain.Extensions;
public static class Linux
{
diff --git a/src/Core/Insight.Domain/Insight.Domain.csproj b/src/Core/Insight.Domain/Insight.Domain.csproj
index 31bc155..b2e0753 100644
--- a/src/Core/Insight.Domain/Insight.Domain.csproj
+++ b/src/Core/Insight.Domain/Insight.Domain.csproj
@@ -17,9 +17,8 @@
-
-
-
+
+
\ No newline at end of file
diff --git a/src/Core/Insight.Infrastructure.Web/Extensions/ServiceExtensions.cs b/src/Core/Insight.Infrastructure.Web/Extensions/ServiceExtensions.cs
index ad9100b..1dda883 100644
--- a/src/Core/Insight.Infrastructure.Web/Extensions/ServiceExtensions.cs
+++ b/src/Core/Insight.Infrastructure.Web/Extensions/ServiceExtensions.cs
@@ -1,6 +1,4 @@
-using AspNetCore.Identity.MongoDbCore.Extensions;
-using AspNetCore.Identity.MongoDbCore.Infrastructure;
-using Insight.Infrastructure.Entities;
+using Insight.Infrastructure.Entities;
using Insight.Infrastructure.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
@@ -21,10 +19,10 @@ public static partial class ServiceExtensions
public static IServiceCollection AddTokenServices(this IServiceCollection services, IConfiguration configuration)
{
var options = new Models.TokenOptions(
- key: configuration.GetValue(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)"),
- expires: configuration.GetValue(Appsettings.JwtExp) ?? throw new Exception($"{Appsettings.JwtExp} value not set (appsettings)"),
- audience: configuration.GetValue(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)"),
- issuer: configuration.GetValue(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)"));
+ key: configuration.GetValue(Appsettings.Jwt.Key) ?? throw new Exception($"{Appsettings.Jwt.Key} value not set (appsettings)"),
+ expires: configuration.GetValue(Appsettings.Jwt.Exp) ?? throw new Exception($"{Appsettings.Jwt.Exp} value not set (appsettings)"),
+ audience: configuration.GetValue(Appsettings.Jwt.Audience) ?? throw new Exception($"{Appsettings.Jwt.Audience} value not set (appsettings)"),
+ issuer: configuration.GetValue(Appsettings.Jwt.Issuer) ?? throw new Exception($"{Appsettings.Jwt.Issuer} value not set (appsettings)"));
services.AddSingleton(options);
services.AddTransient();
@@ -56,7 +54,7 @@ public static partial class ServiceExtensions
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
- var connectionString = configuration.GetValue(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)");
+ var connectionString = configuration.GetValue(Appsettings.Mongo.ConnectionString) ?? throw new Exception($"{Appsettings.Mongo.ConnectionString} value not set (appsettings)");
services.AddIdentity(options =>
{
@@ -146,14 +144,14 @@ public static partial class ServiceExtensions
options.TokenValidationParameters.ValidateActor = false;
- options.TokenValidationParameters.ValidAudience = configuration.GetValue(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)");
+ options.TokenValidationParameters.ValidAudience = configuration.GetValue(Appsettings.Jwt.Audience) ?? throw new Exception($"{Appsettings.Jwt.Audience} value not set (appsettings)");
options.TokenValidationParameters.ValidateAudience = true;
- options.TokenValidationParameters.ValidIssuer = configuration.GetValue(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)");
+ options.TokenValidationParameters.ValidIssuer = configuration.GetValue(Appsettings.Jwt.Issuer) ?? throw new Exception($"{Appsettings.Jwt.Issuer} value not set (appsettings)");
options.TokenValidationParameters.ValidateIssuer = true;
options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(
- Encoding.UTF8.GetBytes(configuration.GetValue(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)"))
+ Encoding.UTF8.GetBytes(configuration.GetValue(Appsettings.Jwt.Key) ?? throw new Exception($"{Appsettings.Jwt.Key} value not set (appsettings)"))
);
options.TokenValidationParameters.ValidateIssuerSigningKey = true;
diff --git a/src/Core/Insight.Infrastructure.Web/Insight.Infrastructure.Web.csproj b/src/Core/Insight.Infrastructure.Web/Insight.Infrastructure.Web.csproj
index bc64ae5..903db67 100644
--- a/src/Core/Insight.Infrastructure.Web/Insight.Infrastructure.Web.csproj
+++ b/src/Core/Insight.Infrastructure.Web/Insight.Infrastructure.Web.csproj
@@ -12,13 +12,12 @@
-
-
-
-
+
+
+
diff --git a/src/Core/Insight.Infrastructure/Constants/Appsettings.cs b/src/Core/Insight.Infrastructure/Constants/Appsettings.cs
index e1adf44..df66859 100644
--- a/src/Core/Insight.Infrastructure/Constants/Appsettings.cs
+++ b/src/Core/Insight.Infrastructure/Constants/Appsettings.cs
@@ -1,17 +1,58 @@
namespace Insight.Infrastructure;
-public class Appsettings
+public static class Appsettings
{
- public const string Database = "database";
- public const string JwtKey = "jwt.key";
- public const string JwtAudience = "jwt.audience";
- public const string JwtIssuer = "jwt.issuer";
- public const string JwtExp = "jwt.exp";
+ public static class Mongo
+ {
+ public const string ConnectionString = "mongo.connection";
+ }
- public const string ServerHost = "server.host";
- public const string ServerPort = "server.port";
+ public static class Influx
+ {
+ public const string Endpoint = "influx.endpoint";
+ public const string Token = "influx.token";
+ public const string Organization = "influx.org";
+ public const string Bucket = "influx.bucket";
+ public const string Service = "influx.service";
+ }
- public const string RemoteServerPort = "remote.port";
- public const string RemoteServerCertificate = "remote.certificate";
- public const string RemoteServerCertificatePassword = "remote.certificate.password";
+ public static class Jwt
+ {
+ public const string Key = "jwt.key";
+ public const string Audience = "jwt.audience";
+ public const string Issuer = "jwt.issuer";
+ public const string Exp = "jwt.exp";
+ }
+
+ public static class Backend
+ {
+ public const string Host = "server.host";
+ public const string Port = "server.port";
+ }
+
+ public static class Agent
+ {
+ public const string Port = "agent.port";
+ public const string Certificate = "agent.certificate";
+ public const string CertificatePassword = "agent.certificate.password";
+ }
+
+ public static class Web
+ {
+ public const string Port = "web.port";
+ public const string Certificate = "web.certificate";
+ public const string CertificatePassword = "web.certificate.password";
+ }
+
+ public static class Remote
+ {
+ public const string Port = "remote.port";
+ public const string Certificate = "remote.certificate";
+ public const string CertificatePassword = "remote.certificate.password";
+ }
+
+ public static class Dispatch
+ {
+ public const string Webmatic = "dispatch.webmatic";
+ }
}
\ No newline at end of file
diff --git a/src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/ConfigurationExtensions.cs
similarity index 93%
rename from src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs
rename to src/Core/Insight.Infrastructure/Extensions/ConfigurationExtensions.cs
index 3bc0aa5..b347b0d 100644
--- a/src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs
+++ b/src/Core/Insight.Infrastructure/Extensions/ConfigurationExtensions.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration;
-namespace Insight.Server.Extensions;
+namespace Insight.Infrastructure;
public static class ConfigurationExtensions
{
diff --git a/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs
index 5f62c94..34ae81d 100644
--- a/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs
+++ b/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs
@@ -11,7 +11,7 @@ public static partial class ServiceExtensions
{
public static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration, ILoggerFactory? loggerFactory = null)
{
- var connectionString = configuration.GetValue(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)");
+ var connectionString = configuration.GetValue(Appsettings.Mongo.ConnectionString) ?? throw new Exception($"{Appsettings.Mongo.ConnectionString} value not set (appsettings)");
var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
settings.ConnectTimeout = TimeSpan.FromSeconds(3);
diff --git a/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj b/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj
index 79e2a5e..bd92b41 100644
--- a/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj
+++ b/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj
@@ -12,14 +12,10 @@
-
+
-
-
-
-
diff --git a/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs b/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs
index 13a40b6..db04a99 100644
--- a/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs
+++ b/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs
@@ -1,6 +1,5 @@
using Insight.Infrastructure.Entities;
using Microsoft.AspNetCore.Identity;
-using MongoDB.Driver;
using System.Globalization;
using System.Text;
using System.Text.Encodings.Web;
diff --git a/src/Core/Insight.Infrastructure/Services/IdentityService.cs b/src/Core/Insight.Infrastructure/Services/IdentityService.cs
index 90d935a..80d9e98 100644
--- a/src/Core/Insight.Infrastructure/Services/IdentityService.cs
+++ b/src/Core/Insight.Infrastructure/Services/IdentityService.cs
@@ -1,6 +1,5 @@
using Insight.Infrastructure.Entities;
using Microsoft.AspNetCore.Identity;
-using Microsoft.Extensions.Logging;
using System.Security.Claims;
namespace Insight.Infrastructure.Services;
diff --git a/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj b/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj
index 3a81496..7cb0df8 100644
--- a/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj
+++ b/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj
@@ -18,6 +18,7 @@
+
diff --git a/src/Server/Insight.Server/Constants/Appsettings.cs b/src/Server/Insight.Server/Constants/Appsettings.cs
deleted file mode 100644
index 07a59f5..0000000
--- a/src/Server/Insight.Server/Constants/Appsettings.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Insight.Server;
-
-internal static class Appsettings
-{
- internal const string AgentServerPort = "agent.server.port";
- internal const string AgentServerCertificate = "agent.server.certificate";
- internal const string AgentServerCertificatePassword = "agent.server.certificate.password";
- internal const string DispatchWebmatic = "dispatch.webmatic";
-
- internal const string WebServerPort = "web.server.port";
- internal const string WebServerCertificate = "web.server.certificate";
- internal const string WebServerCertificatePassword = "web.server.certificate.password";
-}
\ No newline at end of file
diff --git a/src/Server/Insight.Server/Insight.Server.csproj b/src/Server/Insight.Server/Insight.Server.csproj
index 92d5d5e..41f9fe3 100644
--- a/src/Server/Insight.Server/Insight.Server.csproj
+++ b/src/Server/Insight.Server/Insight.Server.csproj
@@ -43,10 +43,17 @@
+
+
+
+
+
+
+
diff --git a/src/Server/Insight.Server/Network/Agent/AgentSession.cs b/src/Server/Insight.Server/Network/Agent/AgentSession.cs
index 29ac2cf..91744ca 100644
--- a/src/Server/Insight.Server/Network/Agent/AgentSession.cs
+++ b/src/Server/Insight.Server/Network/Agent/AgentSession.cs
@@ -1,63 +1,95 @@
-using Insight.Domain.Interfaces;
+using Insight.Domain.Enums;
+using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Domain.Network.Agent.Messages;
-using Insight.Server.Network.Agent.Handlers;
+using Insight.Infrastructure.Entities;
using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
using Vaitr.Network;
namespace Insight.Server.Network.Agent;
public class AgentSession(
- AgentHandler agentHandler,
+ IMongoDatabase database,
IEnumerable> handlers,
ISerializer serializer,
ILogger logger) : TcpSession(serializer, logger)
{
public string? Id { get; set; }
- private readonly AgentHandler _agentHandler = agentHandler;
+ private readonly IMongoDatabase _database = database;
private readonly IEnumerable> _handlers = handlers;
protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Agent ({ep?}) connected", RemoteEndPoint);
- var request = new AuthenticationRequest();
+ // auth request
+ await SendAsync(new AuthenticationRequest(), cancellationToken);
- foreach (var handler in _handlers)
+ // wait for ack
+ for (int i = 0; i < 200; i++)
{
- await handler.HandleAsync(this, request, cancellationToken);
+ if (Id is not null)
+ break;
+
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
- await _agentHandler.ConnectedAsync(this, default);
- await _agentHandler.StatisticUpdateAsync(this, default);
+ // if ack not received
+ if (Id is null)
+ {
+ _logger.LogError("Agent ({ep?}) authentication timeout", RemoteEndPoint);
+ Disconnect();
+ return;
+ }
- _logger.LogInformation("Agent ({ep?}) ID: {id}", RemoteEndPoint, Id);
+ _logger.LogInformation("Agent ({ep?} / {id}) authenticated", RemoteEndPoint, Id);
+
+ // insert log
+ await WriteLogAsync(CategoryEnum.Network, StatusEnum.Information, $"Connected ({RemoteEndPoint})", default);
+
+ // update entity
+ await UpdateAsync(default);
+
+ // init inventory task
+ _ = InitInventoryAsync(cancellationToken);
}
protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Agent ({ep?}) disconnected", RemoteEndPoint);
- await _agentHandler.StatisticUpdateAsync(this, default);
- await _agentHandler.DisconnectedAsync(this, default);
+ // insert log
+ await WriteLogAsync(CategoryEnum.Network, StatusEnum.Information, $"Disconnected ({RemoteEndPoint})", default);
+
+ // update entity
+ await UpdateAsync(default);
}
protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken)
{
await base.OnSentAsync(context, cancellationToken);
- await _agentHandler.StatisticUpdateAsync(this, cancellationToken);
+ // update entity
+ await UpdateAsync(cancellationToken);
}
protected override async ValueTask OnReceivedAsync(IPacketContext context, CancellationToken cancellationToken)
{
await base.OnReceivedAsync(context, cancellationToken);
- if (Id is null && context.Packet is not AuthenticationResponse) return;
+ // only accept auth response if not authenticated
+ if (Id is null)
+ {
+ if (context.Packet is not AuthenticationResponse authentication) return;
+ await OnAuthenticationAsync(authentication, cancellationToken);
+ }
- await _agentHandler.StatisticUpdateAsync(this, cancellationToken);
+ // update entity
+ await UpdateAsync(cancellationToken);
+ // pass message to handlers
foreach (var handler in _handlers)
{
try
@@ -75,6 +107,100 @@ public class AgentSession(
{
_logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
- await _agentHandler.StatisticUpdateAsync(this, cancellationToken);
+ // update entity
+ await UpdateAsync(cancellationToken);
+ }
+
+ private async ValueTask OnAuthenticationAsync(AuthenticationResponse authentication, CancellationToken cancellationToken)
+ {
+ // check serial
+ if (authentication.Serial == default)
+ throw new InvalidDataException($"authentication failed ({nameof(authentication.Serial)})");
+
+ // check version
+ //if (authentication.Version == default || authentication.Version < Domain.Constants.Configuration.Version)
+ // throw new InvalidDataException($"authentication failed ({nameof(authentication.Version)})");
+
+ // upsert agent
+ await _database.Agent()
+ .UpdateOneAsync(Builders
+ .Filter
+ .Eq(p => p.Serial, authentication.Serial.ToString()), Builders.Update
+ .SetOnInsert(p => p.Insert, DateTime.Now)
+ .SetOnInsert(p => p.Serial, authentication.Serial.ToString())
+ .Set(p => p.Update, DateTime.Now)
+ .Set(p => p.Connected, DateTime.Now)
+ .Set(p => p.Version, authentication.Version)
+ .Set(p => p.Endpoint, RemoteEndPoint?.ToString())
+ .Set(p => p.Hostname, authentication.Hostname), new UpdateOptions
+ {
+ IsUpsert = true
+ }, cancellationToken)
+ .ConfigureAwait(false);
+
+ // get agent
+ var agentEntity = await _database.Agent()
+ .Find(Builders
+ .Filter
+ .Eq(p => p.Serial, authentication.Serial.ToString()))
+ .FirstOrDefaultAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ // set session id
+ Id = agentEntity.Id;
+ }
+
+ private async ValueTask WriteLogAsync(CategoryEnum category, StatusEnum status, string message, CancellationToken cancellationToken)
+ {
+ await _database.AgentLog()
+ .InsertOneAsync(new AgentLogEntity
+ {
+ Insert = DateTime.Now,
+ Agent = Id,
+ Category = category.ToString(),
+ Status = status.ToString(),
+ Message = message,
+ Timestamp = DateTime.Now
+ }, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ private async ValueTask UpdateAsync(CancellationToken cancellationToken)
+ {
+ await _database.Agent().UpdateOneAsync(Builders
+ .Filter
+ .Eq(p => p.Id, Id), Builders
+ .Update
+ .Set(p => p.Update, DateTime.Now)
+ .Set(p => p.Activity, Activity)
+ .Set(p => p.SentBytes, SentBytes)
+ .Set(p => p.SentPackets, SentPackets)
+ .Set(p => p.ReceivedBytes, ReceivedBytes)
+ .Set(p => p.ReceivedPackets, ReceivedPackets), null, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ private async Task InitInventoryAsync(CancellationToken cancellationToken)
+ {
+ while (cancellationToken.IsCancellationRequested is false)
+ {
+ _logger.LogWarning("try get inventory");
+
+ // find assigned host
+ var host = await _database.Host()
+ .CountDocumentsAsync(Builders.Filter.Eq(p => p.Agent, Id), cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ // if not assigned => short delay
+ if (host == 0)
+ {
+ await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
+ continue;
+ }
+
+ // send request
+ await SendAsync(new InventoryRequest(), cancellationToken);
+ await Task.Delay(TimeSpan.FromHours(1), cancellationToken);
+ }
}
}
\ No newline at end of file
diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/AgentHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/AgentHandler.cs
deleted file mode 100644
index 73ab7d9..0000000
--- a/src/Server/Insight.Server/Network/Agent/Handlers/AgentHandler.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-using Insight.Domain.Enums;
-using Insight.Domain.Interfaces;
-using Insight.Domain.Network;
-using Insight.Domain.Network.Agent.Messages;
-using Insight.Infrastructure.Entities;
-using Microsoft.Extensions.Logging;
-using MongoDB.Driver;
-
-namespace Insight.Server.Network.Agent.Handlers;
-
-public class AgentHandler(IMongoDatabase database, ILogger logger) : IMessageHandler
-{
- private readonly IMongoDatabase _database = database;
- private readonly ILogger _logger = logger;
-
- public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
- {
- switch (message)
- {
- case AuthenticationRequest authenticationRequest:
- await OnAuthenticationRequestAsync(sender, authenticationRequest, cancellationToken);
- break;
- case AuthenticationResponse authenticationResponse:
- await OnAuthenticationResponseAsync(sender, authenticationResponse, cancellationToken);
- break;
- }
- }
-
- private async ValueTask OnAuthenticationRequestAsync(AgentSession session, AuthenticationRequest message, CancellationToken cancellationToken)
- {
- await session.SendAsync(message, cancellationToken);
-
- for (int i = 0; i < 200; i++)
- {
- if (session.Id is not null)
- {
- _logger.LogInformation("Agent ({ep?}) authenticated", session.RemoteEndPoint);
- return;
- }
-
- await Task.Delay(50, cancellationToken).ConfigureAwait(false);
- }
-
- _logger.LogError("Authentication Timeout ({ep?})", session.RemoteEndPoint);
- session.Disconnect();
- }
-
- private async ValueTask OnAuthenticationResponseAsync(AgentSession session, AuthenticationResponse authentication, CancellationToken cancellationToken)
- {
- if (authentication is null)
- {
- throw new NullReferenceException($"authentication failed (empty response)");
- }
-
- if (authentication.Serial == default)
- {
- throw new InvalidDataException($"authentication failed ({nameof(authentication.Serial)})");
- }
-
- //if (authentication.Version == default || authentication.Version < Domain.Constants.Configuration.Version)
- //{
- // throw new InvalidDataException($"authentication failed ({nameof(authentication.Version)})");
- //}
-
- // upsert agent
- await _database.Agent().UpdateOneAsync(Builders
- .Filter
- .Eq(p => p.Serial, authentication.Serial.ToString()), Builders.Update
- .SetOnInsert(p => p.Insert, DateTime.Now)
- .SetOnInsert(p => p.Serial, authentication.Serial.ToString())
- .Set(p => p.Update, DateTime.Now)
- .Set(p => p.Connected, DateTime.Now)
- .Set(p => p.Version, authentication.Version)
- .Set(p => p.Endpoint, session.RemoteEndPoint?.ToString())
- .Set(p => p.Hostname, authentication.Hostname), new UpdateOptions
- {
- IsUpsert = true
- }, cancellationToken)
- .ConfigureAwait(false);
-
- // get agent
- var agentEntity = await _database.Agent()
- .Find(Builders
- .Filter
- .Eq(p => p.Serial, authentication.Serial.ToString()))
- .FirstOrDefaultAsync(cancellationToken)
- .ConfigureAwait(false);
-
- // set session id
- session.Id = agentEntity.Id;
- }
-
- public async ValueTask ConnectedAsync(AgentSession session, CancellationToken cancellationToken)
- {
- if (session.Id is null) return;
-
- // insert connect log
- await _database.AgentLog()
- .InsertOneAsync(new AgentLogEntity
- {
- Insert = DateTime.Now,
- Agent = session.Id,
- Category = CategoryEnum.Network.ToString(),
- Status = StatusEnum.Information.ToString(),
- Message = $"Connected ({session.RemoteEndPoint})",
- Timestamp = DateTime.Now
- }, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- }
-
- public async ValueTask DisconnectedAsync(AgentSession session, CancellationToken cancellationToken)
- {
- if (session.Id is null) return;
-
- // insert disconnect log
- await _database.AgentLog()
- .InsertOneAsync(new AgentLogEntity
- {
- Insert = DateTime.Now,
- Agent = session.Id,
- Category = CategoryEnum.Network.ToString(),
- Status = StatusEnum.Information.ToString(),
- Message = $"Disconnected ({session.RemoteEndPoint})",
- Timestamp = DateTime.Now
- }, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- }
-
- public async ValueTask StatisticUpdateAsync(AgentSession session, CancellationToken cancellationToken)
- {
- if (session.Id is null) return;
-
- // update agents stats
- await _database.Agent().UpdateOneAsync(Builders
- .Filter
- .Eq(p => p.Id, session.Id), Builders
- .Update
- .Set(p => p.Update, DateTime.Now)
- .Set(p => p.Activity, session.Activity)
- .Set(p => p.SentBytes, session.SentBytes)
- .Set(p => p.ReceivedBytes, session.ReceivedBytes)
- .Set(p => p.SentPackets, session.SentPackets)
- .Set(p => p.ReceivedPackets, session.ReceivedPackets), null, cancellationToken)
- .ConfigureAwait(false);
- }
-
- public async ValueTask LogAsync(AgentSession session, AgentLogEntity log, CancellationToken cancellationToken)
- {
- if (session.Id is null) return;
-
- await _database.AgentLog()
- .InsertOneAsync(log, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- }
-}
\ No newline at end of file
diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs
index c1c2bba..0ef5449 100644
--- a/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs
+++ b/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs
@@ -183,10 +183,10 @@ public partial class TrapHandler(IMongoDatabase database) : IMessageHandler();
services.AddHostedService();
// GLOBAL DEPENDENCIES
@@ -84,24 +82,22 @@ internal static class ServiceExtensions
services.UseHostedServer(options =>
{
options.Address = IPAddress.Any;
- options.Port = configuration.GetValue(Appsettings.AgentServerPort) ?? throw new Exception($"{Appsettings.AgentServerPort} value not set (appsettings)");
+ options.Port = configuration.GetValue(Appsettings.Agent.Port) ?? throw new Exception($"{Appsettings.Agent.Port} value not set (appsettings)");
options.Keepalive = 10000;
options.Timeout = 30000;
options.Backlog = 128;
options.Compression = true;
options.Encryption = Encryption.Tls12;
- options.Certificate = configuration.GetValue(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)");
- options.CertificatePassword = configuration.GetValue(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)");
+ options.Certificate = configuration.GetValue(Appsettings.Agent.Certificate) ?? throw new Exception($"{Appsettings.Agent.Certificate} value not set (appsettings)");
+ options.CertificatePassword = configuration.GetValue(Appsettings.Agent.CertificatePassword) ?? throw new Exception($"{Appsettings.Agent.CertificatePassword} value not set (appsettings)");
options.UseSerializer>();
});
// HANDLER
- services.AddSingleton();
services.AddSingleton, CustomHandler>();
services.AddSingleton, ProxyHandler>();
- services.AddSingleton, AgentHandler>();
services.AddSingleton, DriveHandler>();
services.AddSingleton, Network.Agent.Handlers.EventHandler>();
services.AddSingleton, InterfaceHandler>();
@@ -130,15 +126,15 @@ internal static class ServiceExtensions
services.UseHostedServer(options =>
{
options.Address = IPAddress.Any;
- options.Port = configuration.GetValue(Appsettings.WebServerPort) ?? throw new Exception($"{Appsettings.WebServerPort} value not set (appsettings)");
+ options.Port = configuration.GetValue(Appsettings.Web.Port) ?? throw new Exception($"{Appsettings.Web.Port} value not set (appsettings)");
options.Keepalive = 10000;
options.Timeout = 30000;
options.Backlog = 128;
options.Encryption = Encryption.Tls12;
options.Compression = true;
- options.Certificate = configuration.GetValue(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)");
- options.CertificatePassword = configuration.GetValue(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)");
+ options.Certificate = configuration.GetValue(Appsettings.Web.Certificate) ?? throw new Exception($"{Appsettings.Web.Certificate} value not set (appsettings)");
+ options.CertificatePassword = configuration.GetValue(Appsettings.Web.CertificatePassword) ?? throw new Exception($"{Appsettings.Web.CertificatePassword} value not set (appsettings)");
options.UseSerializer>();
});
diff --git a/src/Server/Insight.Server/Services/DispatchService.cs b/src/Server/Insight.Server/Services/DispatchService.cs
index 7473821..6f08467 100644
--- a/src/Server/Insight.Server/Services/DispatchService.cs
+++ b/src/Server/Insight.Server/Services/DispatchService.cs
@@ -19,7 +19,7 @@ internal class DispatchService(HttpClient httpClient, IMongoDatabase database, I
{
_logger.LogTrace("ExecuteAsync");
- var enabled = _configuration.GetValue(Appsettings.DispatchWebmatic) ?? throw new Exception($"{Appsettings.DispatchWebmatic} value not set (appsettings)");
+ var enabled = _configuration.GetValue(Appsettings.Dispatch.Webmatic) ?? throw new Exception($"{Appsettings.Dispatch.Webmatic} value not set (appsettings)");
if (enabled is false) return;
try
diff --git a/src/Server/Insight.Server/Services/JobService.cs b/src/Server/Insight.Server/Services/JobService.cs
deleted file mode 100644
index e9a45ce..0000000
--- a/src/Server/Insight.Server/Services/JobService.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using Insight.Domain.Network;
-using Insight.Domain.Network.Agent.Messages;
-using Insight.Infrastructure.Entities;
-using Insight.Server.Extensions;
-using Insight.Server.Network.Agent;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using MongoDB.Driver;
-using Vaitr.Network;
-
-namespace Insight.Server.Services;
-
-internal class JobService(ISessionPool agentPool, IMongoDatabase database, ILogger logger) : BackgroundService
-{
- private readonly ISessionPool _agentPool = agentPool;
- private readonly IMongoDatabase _database = database;
- private readonly ILogger _logger = logger;
-
- protected override async Task ExecuteAsync(CancellationToken cancellationToken)
- {
- _logger.LogTrace("ExecuteAsync");
-
- var jobs = new List
- {
- RunInventoryAsync(cancellationToken),
- //RunCustomsAsync(cancellationToken)
- };
-
- try
- {
- await Task.WhenAll(jobs).ConfigureAwait(false);
- }
- catch (OperationCanceledException) { }
- catch (Exception) { }
- }
-
- private async Task RunInventoryAsync(CancellationToken cancellationToken)
- {
- _logger.LogTrace("RunInventoryAsync");
-
- while (cancellationToken.IsCancellationRequested is false)
- {
- try
- {
- foreach (var agent in await GetAssignedAgentsAsync(cancellationToken))
- {
- await agent.SendAsync(new InventoryRequest(), cancellationToken);
- }
- }
- catch (OperationCanceledException) { }
- catch (Exception) { }
- finally
- {
- await Task.Delay(TimeSpan.FromHours(1), cancellationToken);
- }
- }
- }
-
- private async Task RunCustomsAsync(CancellationToken cancellationToken)
- {
- _logger.LogTrace("RunCustomsAsync");
-
- while (cancellationToken.IsCancellationRequested is false)
- {
- try
- {
- // check if agent online
- if (_agentPool.FirstOrDefault().Value is not AgentSession agent) continue;
-
- // proxy-send request packet to agent
- await agent.SendAsync(new Request
- {
- RequestId = "test",
- RequestData = "Get-Date"
- }, cancellationToken);
- }
- catch (OperationCanceledException) { }
- catch (Exception) { }
- finally
- {
- await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken);
- }
- }
-
-
- }
-
- private async ValueTask> GetAssignedAgentsAsync(CancellationToken cancellationToken)
- {
- var valid = new List();
-
- await Async.ParallelForEach(_agentPool.Where(p => p.Value.Id is not null), async x =>
- {
- var host = await _database.Host()
- .Find(Builders.Filter.Eq(p => p.Agent, x.Value.Id))
- .FirstOrDefaultAsync(cancellationToken)
- .ConfigureAwait(false);
-
- if (host is null) return;
- valid.Add(x.Value);
- });
-
- return valid;
- }
-}
\ No newline at end of file
diff --git a/src/Server/Insight.Server/appsettings.Development.json b/src/Server/Insight.Server/appsettings.Development.json
index 859a335..9138b80 100644
--- a/src/Server/Insight.Server/appsettings.Development.json
+++ b/src/Server/Insight.Server/appsettings.Development.json
@@ -1,17 +1,13 @@
{
- "database": "mongodb://db.insight.local:27017",
+ "mongo.connection": "mongodb://db.insight.local:27017",
- "remote.server.port": 3003,
- "remote.server.certificate": "localhost.pfx",
- "remote.server.certificate.password": "Webmatic12",
+ "agent.port": 3002,
+ "agent.certificate": "localhost.pfx",
+ "agent.certificate.password": "Webmatic12",
- "agent.server.port": 3002,
- "agent.server.certificate": "localhost.pfx",
- "agent.server.certificate.password": "Webmatic12",
-
- "web.server.port": 3001,
- "web.server.certificate": "localhost.pfx",
- "web.server.certificate.password": "Webmatic12",
+ "web.port": 3001,
+ "web.certificate": "localhost.pfx",
+ "web.certificate.password": "Webmatic12",
"dispatch.webmatic": false
}
\ No newline at end of file
diff --git a/src/Server/Insight.Server/appsettings.json b/src/Server/Insight.Server/appsettings.json
index c43acdf..d9ce71c 100644
--- a/src/Server/Insight.Server/appsettings.json
+++ b/src/Server/Insight.Server/appsettings.json
@@ -1,13 +1,13 @@
{
- "database": "mongodb://127.0.0.1:27017",
+ "mongo.connection": "mongodb://127.0.0.1:27017",
- "agent.server.port": 3002,
- "agent.server.certificate": "localhost.pfx",
- "agent.server.certificate.password": "Webmatic12",
+ "agent.port": 3002,
+ "agent.certificate": "localhost.pfx",
+ "agent.certificate.password": "Webmatic12",
- "web.server.port": 3001,
- "web.server.certificate": "localhost.pfx",
- "web.server.certificate.password": "Webmatic12",
+ "web.port": 3001,
+ "web.certificate": "localhost.pfx",
+ "web.certificate.password": "Webmatic12",
"dispatch.webmatic": false
}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Controllers/WeatherForecastController.cs b/src/Server/Insight.Server2/Controllers/WeatherForecastController.cs
new file mode 100644
index 0000000..4a44f33
--- /dev/null
+++ b/src/Server/Insight.Server2/Controllers/WeatherForecastController.cs
@@ -0,0 +1,33 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Insight.Server2.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class WeatherForecastController : ControllerBase
+ {
+ private static readonly string[] Summaries = new[]
+ {
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ };
+
+ private readonly ILogger _logger;
+
+ public WeatherForecastController(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ [HttpGet(Name = "GetWeatherForecast")]
+ public IEnumerable Get()
+ {
+ return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+ {
+ Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ TemperatureC = Random.Shared.Next(-20, 55),
+ Summary = Summaries[Random.Shared.Next(Summaries.Length)]
+ })
+ .ToArray();
+ }
+ }
+}
diff --git a/src/Server/Insight.Server2/Extensions/Async.cs b/src/Server/Insight.Server2/Extensions/Async.cs
new file mode 100644
index 0000000..225b569
--- /dev/null
+++ b/src/Server/Insight.Server2/Extensions/Async.cs
@@ -0,0 +1,52 @@
+using System.Threading.Tasks.Dataflow;
+
+namespace Insight.Server.Extensions;
+
+public static class Async
+{
+ public static async Task ParallelForEach(
+ this IAsyncEnumerable source,
+ Func body,
+ int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
+ TaskScheduler? scheduler = null)
+ {
+ var options = new ExecutionDataflowBlockOptions
+ {
+ MaxDegreeOfParallelism = maxDegreeOfParallelism
+ };
+
+ if (scheduler != null)
+ options.TaskScheduler = scheduler;
+
+ var block = new ActionBlock(body, options);
+
+ await foreach (var item in source)
+ block.Post(item);
+
+ block.Complete();
+ await block.Completion;
+ }
+
+ public static async Task ParallelForEach(
+ this IEnumerable source,
+ Func body,
+ int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
+ TaskScheduler? scheduler = null)
+ {
+ var options = new ExecutionDataflowBlockOptions
+ {
+ MaxDegreeOfParallelism = maxDegreeOfParallelism
+ };
+
+ if (scheduler != null)
+ options.TaskScheduler = scheduler;
+
+ var block = new ActionBlock(body, options);
+
+ foreach (var item in source)
+ block.Post(item);
+
+ block.Complete();
+ await block.Completion;
+ }
+}
diff --git a/src/Server/Insight.Server2/Insight.Server2.csproj b/src/Server/Insight.Server2/Insight.Server2.csproj
new file mode 100644
index 0000000..d35f0cc
--- /dev/null
+++ b/src/Server/Insight.Server2/Insight.Server2.csproj
@@ -0,0 +1,41 @@
+
+
+
+ Exe
+ net8.0
+ latest
+ Insight
+ server
+ 2024.1.10.0
+ Insight.Server
+ enable
+ enable
+ none
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/src/Server/Insight.Server2/Insight.Server2.http b/src/Server/Insight.Server2/Insight.Server2.http
new file mode 100644
index 0000000..4d3b8e8
--- /dev/null
+++ b/src/Server/Insight.Server2/Insight.Server2.http
@@ -0,0 +1,6 @@
+@Insight.Server2_HostAddress = http://localhost:5197
+
+GET {{Insight.Server2_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/src/Server/Insight.Server2/Models/MonitorMessage.cs b/src/Server/Insight.Server2/Models/MonitorMessage.cs
new file mode 100644
index 0000000..3886d21
--- /dev/null
+++ b/src/Server/Insight.Server2/Models/MonitorMessage.cs
@@ -0,0 +1,26 @@
+using Insight.Domain.Enums;
+
+namespace Insight.Server.Models;
+
+internal class MonitorMessage
+{
+ public DateTime? Timestamp { get; set; }
+ public string? Community { get; set; }
+ public ApplicationEnum? Application { get; set; }
+ public CategoryEnum? Category { get; set; }
+ public StatusEnum? Status { get; set; }
+ public string? Endpoint { get; set; }
+ public string? Hostname { get; set; }
+ public string? Subject { get; set; }
+ public string? Message { get; set; }
+
+ public enum ApplicationEnum
+ {
+ Unknown = 0,
+ Insight = 1,
+ Acronis = 2,
+ Veeam = 3,
+ QNAP = 4,
+ FreeNas = 5
+ }
+}
diff --git a/src/Server/Insight.Server2/Network/Agent/AgentSession.cs b/src/Server/Insight.Server2/Network/Agent/AgentSession.cs
new file mode 100644
index 0000000..91744ca
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/AgentSession.cs
@@ -0,0 +1,206 @@
+using Insight.Domain.Enums;
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
+using Vaitr.Network;
+
+namespace Insight.Server.Network.Agent;
+
+public class AgentSession(
+ IMongoDatabase database,
+ IEnumerable> handlers,
+ ISerializer serializer,
+ ILogger logger) : TcpSession(serializer, logger)
+{
+ public string? Id { get; set; }
+
+ private readonly IMongoDatabase _database = database;
+ private readonly IEnumerable> _handlers = handlers;
+
+ protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Agent ({ep?}) connected", RemoteEndPoint);
+
+ // auth request
+ await SendAsync(new AuthenticationRequest(), cancellationToken);
+
+ // wait for ack
+ for (int i = 0; i < 200; i++)
+ {
+ if (Id is not null)
+ break;
+
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
+ }
+
+ // if ack not received
+ if (Id is null)
+ {
+ _logger.LogError("Agent ({ep?}) authentication timeout", RemoteEndPoint);
+ Disconnect();
+ return;
+ }
+
+ _logger.LogInformation("Agent ({ep?} / {id}) authenticated", RemoteEndPoint, Id);
+
+ // insert log
+ await WriteLogAsync(CategoryEnum.Network, StatusEnum.Information, $"Connected ({RemoteEndPoint})", default);
+
+ // update entity
+ await UpdateAsync(default);
+
+ // init inventory task
+ _ = InitInventoryAsync(cancellationToken);
+ }
+
+ protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Agent ({ep?}) disconnected", RemoteEndPoint);
+
+ // insert log
+ await WriteLogAsync(CategoryEnum.Network, StatusEnum.Information, $"Disconnected ({RemoteEndPoint})", default);
+
+ // update entity
+ await UpdateAsync(default);
+ }
+
+ protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken)
+ {
+ await base.OnSentAsync(context, cancellationToken);
+
+ // update entity
+ await UpdateAsync(cancellationToken);
+ }
+
+ protected override async ValueTask OnReceivedAsync(IPacketContext context, CancellationToken cancellationToken)
+ {
+ await base.OnReceivedAsync(context, cancellationToken);
+
+ // only accept auth response if not authenticated
+ if (Id is null)
+ {
+ if (context.Packet is not AuthenticationResponse authentication) return;
+ await OnAuthenticationAsync(authentication, cancellationToken);
+ }
+
+ // update entity
+ await UpdateAsync(cancellationToken);
+
+ // pass message to handlers
+ foreach (var handler in _handlers)
+ {
+ try
+ {
+ await handler.HandleAsync(this, context.Packet, cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning("Agent ({ep?}) {ex}", RemoteEndPoint, ex.ToString());
+ }
+ }
+ }
+
+ protected override async ValueTask OnHeartbeatAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
+
+ // update entity
+ await UpdateAsync(cancellationToken);
+ }
+
+ private async ValueTask OnAuthenticationAsync(AuthenticationResponse authentication, CancellationToken cancellationToken)
+ {
+ // check serial
+ if (authentication.Serial == default)
+ throw new InvalidDataException($"authentication failed ({nameof(authentication.Serial)})");
+
+ // check version
+ //if (authentication.Version == default || authentication.Version < Domain.Constants.Configuration.Version)
+ // throw new InvalidDataException($"authentication failed ({nameof(authentication.Version)})");
+
+ // upsert agent
+ await _database.Agent()
+ .UpdateOneAsync(Builders
+ .Filter
+ .Eq(p => p.Serial, authentication.Serial.ToString()), Builders.Update
+ .SetOnInsert(p => p.Insert, DateTime.Now)
+ .SetOnInsert(p => p.Serial, authentication.Serial.ToString())
+ .Set(p => p.Update, DateTime.Now)
+ .Set(p => p.Connected, DateTime.Now)
+ .Set(p => p.Version, authentication.Version)
+ .Set(p => p.Endpoint, RemoteEndPoint?.ToString())
+ .Set(p => p.Hostname, authentication.Hostname), new UpdateOptions
+ {
+ IsUpsert = true
+ }, cancellationToken)
+ .ConfigureAwait(false);
+
+ // get agent
+ var agentEntity = await _database.Agent()
+ .Find(Builders
+ .Filter
+ .Eq(p => p.Serial, authentication.Serial.ToString()))
+ .FirstOrDefaultAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ // set session id
+ Id = agentEntity.Id;
+ }
+
+ private async ValueTask WriteLogAsync(CategoryEnum category, StatusEnum status, string message, CancellationToken cancellationToken)
+ {
+ await _database.AgentLog()
+ .InsertOneAsync(new AgentLogEntity
+ {
+ Insert = DateTime.Now,
+ Agent = Id,
+ Category = category.ToString(),
+ Status = status.ToString(),
+ Message = message,
+ Timestamp = DateTime.Now
+ }, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ private async ValueTask UpdateAsync(CancellationToken cancellationToken)
+ {
+ await _database.Agent().UpdateOneAsync(Builders
+ .Filter
+ .Eq(p => p.Id, Id), Builders
+ .Update
+ .Set(p => p.Update, DateTime.Now)
+ .Set(p => p.Activity, Activity)
+ .Set(p => p.SentBytes, SentBytes)
+ .Set(p => p.SentPackets, SentPackets)
+ .Set(p => p.ReceivedBytes, ReceivedBytes)
+ .Set(p => p.ReceivedPackets, ReceivedPackets), null, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ private async Task InitInventoryAsync(CancellationToken cancellationToken)
+ {
+ while (cancellationToken.IsCancellationRequested is false)
+ {
+ _logger.LogWarning("try get inventory");
+
+ // find assigned host
+ var host = await _database.Host()
+ .CountDocumentsAsync(Builders.Filter.Eq(p => p.Agent, Id), cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ // if not assigned => short delay
+ if (host == 0)
+ {
+ await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
+ continue;
+ }
+
+ // send request
+ await SendAsync(new InventoryRequest(), cancellationToken);
+ await Task.Delay(TimeSpan.FromHours(1), cancellationToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/CustomHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/CustomHandler.cs
new file mode 100644
index 0000000..14e1e87
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/CustomHandler.cs
@@ -0,0 +1,27 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Microsoft.Extensions.Logging;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class CustomHandler(ILogger logger) : IMessageHandler
+{
+ private readonly ILogger _logger = logger;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Response response:
+ await OnResponseAsync(sender, response, cancellationToken);
+ break;
+ }
+ }
+
+ private ValueTask OnResponseAsync(AgentSession sender, Response response, CancellationToken cancellationToken)
+ {
+ _logger.LogWarning("Response: {response}", response.ResponseData);
+ return default;
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/DriveHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/DriveHandler.cs
new file mode 100644
index 0000000..2fe86a6
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/DriveHandler.cs
@@ -0,0 +1,141 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class DriveHandler(IMongoDatabase database) : IMessageHandler
+{
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Collection drives:
+ await OnDrivesAsync(sender, drives, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnDrivesAsync(AgentSession session, List drives, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ var batch = ObjectId.GenerateNewId().ToString();
+ var date = DateTime.Now;
+
+ var driveBulk = new List>();
+
+ if (drives is not null && drives.Count != 0)
+ {
+ foreach (var drive in drives)
+ {
+ var driveFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Index, drive.Index)
+ });
+
+ var driveUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Index, drive.Index)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch)
+ .Set(p => p.Company, drive.Manufacturer)
+ .Set(p => p.Name, drive.Name)
+ .Set(p => p.Size, drive.Size)
+ .Set(p => p.Type, drive.InterfaceType)
+ .Set(p => p.Serial, drive.SerialNumber)
+ .Set(p => p.Firmware, drive.FirmwareRevision)
+ .Set(p => p.Status, drive.Status)
+ .Set(p => p.Pnp, drive.PNPDeviceID);
+
+ driveBulk.Add(new UpdateOneModel(driveFilter, driveUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+
+ driveBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var driveResult = await _database.HostDrive().BulkWriteAsync(driveBulk, cancellationToken: cancellationToken);
+
+ // volumes
+
+ var volumeBulk = new List>();
+
+ if (drives is not null && drives.Count != 0)
+ {
+ foreach (var drive in drives)
+ {
+ var driveId = await _database.HostDrive()
+ .Find(p => p.Host == hostEntity.Id && p.Index == drive.Index)
+ .Project(p => p.Id)
+ .FirstOrDefaultAsync(cancellationToken: default);
+
+ if (drive.Volumes is not null && drive.Volumes.Count != 0)
+ {
+ foreach (var volume in drive.Volumes)
+ {
+ var volumeFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Drive, driveId),
+ Builders.Filter.Eq(x => x.Index, volume.Index)
+ });
+
+ var volumeUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Drive, driveId)
+ .SetOnInsert(p => p.Index, volume.Index)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch)
+ .Set(p => p.Name, volume.Name)
+ .Set(p => p.Label, volume.Id)
+ .Set(p => p.Serial, volume.SerialNumber)
+ .Set(p => p.Size, volume.Size)
+ .Set(p => p.FreeSpace, volume.FreeSpace)
+ .Set(p => p.Type, volume.Type)
+ .Set(p => p.FileSystem, volume.FileSystem)
+ .Set(p => p.Compressed, volume.Compressed)
+ .Set(p => p.Bootable, volume.Bootable)
+ .Set(p => p.Primary, volume.PrimaryPartition)
+ .Set(p => p.Boot, volume.Bootable)
+ .Set(p => p.BlockSize, volume.BlockSize)
+ .Set(p => p.Blocks, volume.NumberOfBlocks)
+ .Set(p => p.StartingOffset, volume.StartingOffset)
+ .Set(p => p.Provider, volume.ProviderName);
+
+ volumeBulk.Add(new UpdateOneModel(volumeFilter, volumeUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+ }
+ }
+
+ volumeBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var volumeResult = await _database.HostVolume().BulkWriteAsync(volumeBulk, cancellationToken: cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/EventHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/EventHandler.cs
new file mode 100644
index 0000000..ee6080c
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/EventHandler.cs
@@ -0,0 +1,261 @@
+using Insight.Domain.Enums;
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Driver;
+using static Insight.Domain.Network.Agent.Messages.Event;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class EventHandler(IMongoDatabase database) : IMessageHandler
+{
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Event @event:
+ await OnEventAsync(sender, @event, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnEventAsync(AgentSession session, Event @event, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ if (FilterEventId(@event)) return;
+
+ var hostLog = await InsertHostLogAsync(hostEntity, @event, cancellationToken);
+
+ if (hostLog is null || FilterMonitoringHostLog(hostLog)) return;
+
+ await _database.HostLogMonitoring()
+ .InsertOneAsync(new HostLogMonitoringEntity
+ {
+ Host = hostEntity.Id,
+ Insert = hostLog.Insert,
+ Timestamp = hostLog.Timestamp,
+ Category = hostLog.Category,
+ Status = hostLog.Status,
+ Message = hostLog.Message,
+ Dispatch = DispatchEnum.Pending.ToString()
+ }, cancellationToken: cancellationToken);
+ }
+
+ private async ValueTask InsertAgentLogAsync(AgentSession session, Event @event, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent()
+ .Aggregate()
+ .Match(Builders.Filter.Eq(p => p.Id, session.Id))
+ .Lookup(_database.Host(), p => p.Serial, p => p.Agent, p => p.Hosts)
+ .FirstOrDefaultAsync(cancellationToken);
+
+ if (agentEntity is null) return null;
+
+ StatusEnum? status = @event.Status switch
+ {
+ StatusType.Information => StatusEnum.Information,
+ StatusType.Warning => StatusEnum.Warning,
+ StatusType.Error => StatusEnum.Error,
+ _ => null
+ };
+
+ CategoryEnum? category = @event.Category?.ToLower() switch
+ {
+ "network" => CategoryEnum.Network,
+ "application" => CategoryEnum.Application,
+ "security" => CategoryEnum.Security,
+ "system" => CategoryEnum.System,
+ _ => CategoryEnum.None
+ };
+
+ var date = DateTime.Now;
+
+ var log = new AgentLogEntity
+ {
+ Insert = date,
+ Agent = agentEntity.Id,
+ Timestamp = @event.Timestamp,
+ EventId = @event.EventId.ToString(),
+ Source = @event.Source,
+ Status = status.ToString(),
+ Category = category.ToString(),
+ Message = @event.Message
+ };
+
+ await _database.AgentLog().InsertOneAsync(log, cancellationToken: cancellationToken);
+ return log;
+ }
+
+ private async ValueTask InsertHostLogAsync(HostEntity hostEntity, Event @event, CancellationToken cancellationToken)
+ {
+ StatusEnum? status = @event.Status switch
+ {
+ StatusType.Information => StatusEnum.Information,
+ StatusType.Warning => StatusEnum.Warning,
+ StatusType.Error => StatusEnum.Error,
+ StatusType.Critical => StatusEnum.Error,
+ StatusType.Unknown => null,
+ _ => null
+ };
+
+ var category = CategoryEnum.None;
+
+ if (@event.Category is not null)
+ {
+ switch (@event.Category)
+ {
+ case var _ when @event.Category.Contains("network", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Network;
+ break;
+
+ case var _ when @event.Category.Contains("application", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Application;
+ break;
+
+ case var _ when @event.Category.Contains("security", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Security;
+ break;
+
+ case var _ when @event.Category.Contains("system", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.System;
+ break;
+
+ case var _ when @event.Category.Contains("printservice", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Printer;
+ break;
+
+ case var _ when @event.Category.Contains("taskscheduler", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Task;
+ break;
+
+ case var _ when @event.Category.Contains("terminalservices", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.RDP;
+ break;
+
+ case var _ when @event.Category.Contains("smbclient", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Network;
+ break;
+
+ case var _ when @event.Category.Contains("smbserver", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.Network;
+ break;
+
+ case var _ when @event.Category.Contains("storagespaces", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.System;
+ break;
+
+ case var _ when @event.Category.Contains("diagnostics", StringComparison.InvariantCultureIgnoreCase):
+ category = CategoryEnum.System;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ var date = DateTime.Now;
+
+ var log = new HostLogEntity
+ {
+ Insert = date,
+ Host = hostEntity.Id,
+ Timestamp = @event.Timestamp,
+ EventId = @event.EventId.ToString(),
+ Status = status.ToString(),
+ Source = @event.Source,
+ Category = category.ToString(),
+ Message = @event.Message
+ };
+
+ await _database.HostLog().InsertOneAsync(log, cancellationToken: cancellationToken);
+ return log;
+ }
+
+ private static bool FilterEventId(Event @event)
+ {
+ var filter = new List
+ {
+ 0,
+ 3,
+ 4,
+ 16,
+ 37,
+ 900,
+ 902,
+ 903,
+ 1001,
+ 1003,
+ 1008,
+ 1023,
+ 1066,
+ 4624,
+ 4625,
+ 4634,
+ 4648,
+ 4672,
+ 4776,
+ 4798,
+ 4799,
+ 5058,
+ 5059,
+ 5061,
+ 5379,
+ 5612,
+ 5781,
+ //7036,
+ 8014,
+ 8016,
+ 8019,
+ 8194,
+ 8224,
+ 9009,
+ 9027,
+ 10001,
+ 10016,
+ 16384,
+ 16394,
+ 36874,
+ 36888,
+ };
+
+ if (filter.Any(p => p == @event.EventId)) return true;
+ return false;
+ }
+
+ private static bool FilterMonitoringHostLog(HostLogEntity hostLog)
+ {
+ if (Enum.TryParse(hostLog.Status, out StatusType status) is false) return true;
+
+ if (hostLog.Category == CategoryEnum.System.ToString())
+ {
+ if (hostLog.Source == "Service Control Manager" && status < StatusType.Warning) return true;
+ }
+
+ if (hostLog.Category == CategoryEnum.Task.ToString())
+ {
+ if (status < StatusType.Error) return true;
+ }
+
+ // skip rdp infos
+ if (hostLog.Category == CategoryEnum.RDP.ToString())
+ {
+ if (hostLog.EventId == "261") return true;
+ }
+
+ // skip smbclient (veeam errors)
+ if (hostLog.Category == CategoryEnum.Security.ToString())
+ {
+ if (hostLog.Source == "Microsoft-Windows-SMBClient") return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/InterfaceHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/InterfaceHandler.cs
new file mode 100644
index 0000000..c986aad
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/InterfaceHandler.cs
@@ -0,0 +1,295 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class InterfaceHandler(IMongoDatabase database) : IMessageHandler
+{
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Collection interfaces:
+ await OnInterfacesAsync(sender, interfaces, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnInterfacesAsync(AgentSession session, List interfaces, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ var batch = ObjectId.GenerateNewId().ToString();
+ var date = DateTime.Now;
+
+ // interfaces
+
+ if (interfaces is not null && interfaces.Count != 0)
+ {
+ var interfaceBulk = new List>();
+
+ foreach (var @interface in interfaces)
+ {
+ var interfaceFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Index, @interface.Index)
+ });
+
+ var interfaceUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Index, @interface.Index)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch)
+
+ .Set(p => p.Mac, @interface?.Mac)
+ .Set(p => p.Name, @interface?.Name)
+ .Set(p => p.Description, @interface?.Description)
+ .Set(p => p.Physical, @interface?.Physical)
+ .Set(p => p.Status, @interface?.Status?.ToString())
+ .Set(p => p.Suffix, @interface?.Suffix)
+ .Set(p => p.Speed, @interface?.Speed)
+ .Set(p => p.Ipv4Mtu, @interface?.Ipv4Mtu)
+ .Set(p => p.Ipv4Dhcp, @interface?.Ipv4Dhcp)
+
+ .Set(p => p.Ipv4Forwarding, @interface?.Ipv4Forwarding)
+ .Set(p => p.Ipv6Mtu, @interface?.Ipv6Mtu)
+ .Set(p => p.Sent, @interface?.Sent)
+ .Set(p => p.Received, @interface?.Received)
+ .Set(p => p.IncomingPacketsDiscarded, @interface?.IncomingPacketsDiscarded)
+ .Set(p => p.IncomingPacketsWithErrors, @interface?.IncomingPacketsWithErrors)
+ .Set(p => p.IncomingUnknownProtocolPackets, @interface?.IncomingUnknownProtocolPackets)
+ .Set(p => p.OutgoingPacketsDiscarded, @interface?.OutgoingPacketsDiscarded)
+ .Set(p => p.OutgoingPacketsWithErrors, @interface?.OutgoingPacketsWithErrors);
+
+ interfaceBulk.Add(new UpdateOneModel(interfaceFilter, interfaceUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+
+ interfaceBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var interfaceResult = await _database.HostInterface().BulkWriteAsync(interfaceBulk, cancellationToken: cancellationToken);
+ }
+
+ // addresses
+
+ if (interfaces is not null && interfaces.Count != 0)
+ {
+ var addressBulk = new List>();
+
+ foreach (var @interface in interfaces)
+ {
+ var interfaceId = await _database.HostInterface()
+ .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index)
+ .Project(p => p.Id)
+ .FirstOrDefaultAsync(cancellationToken: default);
+
+ if (@interface.Addresses is not null && @interface.Addresses.Count != 0)
+ {
+ foreach (var address in @interface.Addresses)
+ {
+ var addressFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Interface, interfaceId),
+ Builders.Filter.Eq(x => x.Address, address?.IpAddress?.Address),
+ Builders.Filter.Eq(x => x.Mask, address?.Ipv4Mask?.Address)
+ });
+
+ var addressUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Interface, interfaceId)
+ .SetOnInsert(p => p.Address, address?.IpAddress?.Address)
+ .SetOnInsert(p => p.Mask, address?.Ipv4Mask?.Address)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch);
+
+ addressBulk.Add(new UpdateOneModel(addressFilter, addressUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+ }
+
+ addressBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var addressResult = await _database.HostInterfaceAddress().BulkWriteAsync(addressBulk, cancellationToken: cancellationToken);
+ }
+
+ // gateways
+
+ if (interfaces is not null && interfaces.Count != 0)
+ {
+ var gatewayBulk = new List>();
+
+ foreach (var @interface in interfaces)
+ {
+ var interfaceId = await _database.HostInterface()
+ .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index)
+ .Project(p => p.Id)
+ .FirstOrDefaultAsync(cancellationToken: default);
+
+ if (@interface.Gateways is not null && @interface.Gateways.Count != 0)
+ {
+ foreach (var gateway in @interface.Gateways)
+ {
+ var gatewayFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Interface, interfaceId),
+ Builders.Filter.Eq(x => x.Address, gateway?.Address)
+ });
+
+ var gatewayUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Interface, interfaceId)
+ .SetOnInsert(p => p.Address, gateway?.Address)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch);
+
+ gatewayBulk.Add(new UpdateOneModel(gatewayFilter, gatewayUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+ }
+
+ gatewayBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var gatewayResult = await _database.HostInterfaceGateway().BulkWriteAsync(gatewayBulk, cancellationToken: cancellationToken);
+ }
+
+ // nameservers
+
+ if (interfaces is not null && interfaces.Count != 0)
+ {
+ var nameserverBulk = new List>();
+
+ foreach (var @interface in interfaces)
+ {
+ var interfaceId = await _database.HostInterface()
+ .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index)
+ .Project(p => p.Id)
+ .FirstOrDefaultAsync(cancellationToken: default);
+
+ if (@interface.Dns is not null && @interface.Dns.Count != 0)
+ {
+ foreach (var nameserver in @interface.Dns)
+ {
+ var nameserverFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Interface, interfaceId),
+ Builders.Filter.Eq(x => x.Address, nameserver?.Address)
+ });
+
+ var nameserverUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Interface, interfaceId)
+ .SetOnInsert(p => p.Address, nameserver?.Address)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch);
+
+ nameserverBulk.Add(new UpdateOneModel(nameserverFilter, nameserverUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+ }
+
+ nameserverBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var nameserverResult = await _database.HostInterfaceNameserver().BulkWriteAsync(nameserverBulk, cancellationToken: cancellationToken);
+ }
+
+ // routes
+
+ if (interfaces is not null && interfaces.Count != 0)
+ {
+ var routeBulk = new List>();
+
+ foreach (var @interface in interfaces)
+ {
+ var interfaceId = await _database.HostInterface()
+ .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index)
+ .Project(p => p.Id)
+ .FirstOrDefaultAsync(cancellationToken: default);
+
+ if (@interface.Routes is not null && @interface.Routes.Count != 0)
+ {
+ foreach (var route in @interface.Routes)
+ {
+ var routeFilter = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Interface, interfaceId),
+ Builders.Filter.Eq(x => x.Destination, route?.Destination?.Address),
+ Builders.Filter.Eq(x => x.Gateway, route?.Gateway?.Address),
+ Builders.Filter.Eq(x => x.Mask, route?.Mask),
+ });
+
+ var routeUpdate = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Interface, interfaceId)
+ .SetOnInsert(p => p.Destination, route?.Destination?.Address)
+ .SetOnInsert(p => p.Gateway, route?.Gateway?.Address)
+ .SetOnInsert(p => p.Mask, route?.Mask)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch)
+
+ .Set(p => p.Metric, route?.Metric);
+
+ routeBulk.Add(new UpdateOneModel(routeFilter, routeUpdate)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+ }
+
+ routeBulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var routeResult = await _database.HostInterfaceRoute().BulkWriteAsync(routeBulk, cancellationToken: cancellationToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/MainboardHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/MainboardHandler.cs
new file mode 100644
index 0000000..95811a8
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/MainboardHandler.cs
@@ -0,0 +1,48 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Driver;
+
+namespace Insight.Server.Network.Agent.Handlers
+{
+ public class MainboardHandler(IMongoDatabase database) : IMessageHandler
+ {
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Mainboard mainboard:
+ await OnMainboardAsync(sender, mainboard, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnMainboardAsync(AgentSession session, Mainboard mainboard, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ var date = DateTime.Now;
+
+ await _database.HostMainboard().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .Set(p => p.Update, date)
+ .Set(p => p.Name, mainboard?.Manufacturer)
+ .Set(p => p.Name, mainboard?.Model)
+ .Set(p => p.Serial, mainboard?.Serial)
+ .Set(p => p.Bios, mainboard?.BiosManufacturer)
+ .Set(p => p.Version, mainboard?.BiosVersion)
+ .Set(p => p.Date, mainboard?.BiosDate), new UpdateOptions
+ {
+ IsUpsert = true
+ }, cancellationToken);
+ }
+ }
+}
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/MemoryHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/MemoryHandler.cs
new file mode 100644
index 0000000..97e1ae5
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/MemoryHandler.cs
@@ -0,0 +1,79 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class MemoryHandler(IMongoDatabase database) : IMessageHandler
+{
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Collection memory:
+ await OnMemoryAsync(sender, memory, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnMemoryAsync(AgentSession session, List memory, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ var batch = ObjectId.GenerateNewId().ToString();
+ var date = DateTime.Now;
+
+ var bulk = new List>();
+
+ if (memory is not null && memory.Count != 0)
+ {
+ foreach (var mem in memory)
+ {
+ var filterDefinition = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Index, mem.Index)
+ });
+
+ var updateDefinition = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Index, mem.Index)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch)
+ .Set(p => p.Company, mem.Manufacturer)
+ .Set(p => p.Name, mem.Model)
+ .Set(p => p.Tag, mem.Tag)
+ .Set(p => p.Location, mem.Location)
+ .Set(p => p.Serial, mem.Serial)
+ .Set(p => p.Capacity, mem.Capacity)
+ .Set(p => p.Clock, mem.Speed)
+ .Set(p => p.CurrentClock, mem.ConfiguredSpeed)
+ .Set(p => p.Voltage, mem.Voltage)
+ .Set(p => p.CurrentVoltage, mem.ConfiguredVoltage);
+
+ bulk.Add(new UpdateOneModel(filterDefinition, updateDefinition)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+
+ bulk.Add(new DeleteManyModel(Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Ne(x => x.Batch, batch)
+ })));
+
+ var result = await _database.HostMemory().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/OperationSystemHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/OperationSystemHandler.cs
new file mode 100644
index 0000000..3fb0d47
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/OperationSystemHandler.cs
@@ -0,0 +1,47 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Driver;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class OperationSystemHandler(IMongoDatabase database) : IMessageHandler
+{
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case OperationSystem os:
+ await OnOperationSystemAsync(sender, os, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnOperationSystemAsync(AgentSession session, OperationSystem operatingSystem, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ var date = DateTime.Now;
+
+ await _database.HostOs().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .Set(p => p.Update, date)
+ .Set(p => p.Name, operatingSystem.Name)
+ .Set(p => p.Version, operatingSystem.Version)
+ .Set(p => p.Architecture, operatingSystem.Architecture.ToString())
+ .Set(p => p.SerialNumber, operatingSystem.SerialNumber)
+ .Set(p => p.Virtual, operatingSystem.Virtual)
+ .Set(p => p.Installed, operatingSystem.InstallDate), new UpdateOptions
+ {
+ IsUpsert = true
+ }, cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Insight.Server2/Network/Agent/Handlers/PrinterHandler.cs b/src/Server/Insight.Server2/Network/Agent/Handlers/PrinterHandler.cs
new file mode 100644
index 0000000..e57f0c0
--- /dev/null
+++ b/src/Server/Insight.Server2/Network/Agent/Handlers/PrinterHandler.cs
@@ -0,0 +1,72 @@
+using Insight.Domain.Interfaces;
+using Insight.Domain.Network;
+using Insight.Domain.Network.Agent.Messages;
+using Insight.Infrastructure.Entities;
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+namespace Insight.Server.Network.Agent.Handlers;
+
+public class PrinterHandler(IMongoDatabase database) : IMessageHandler
+{
+ private readonly IMongoDatabase _database = database;
+
+ public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
+ {
+ switch (message)
+ {
+ case Collection printers:
+ await OnPrintersAsync(sender, printers, cancellationToken);
+ break;
+ }
+ }
+
+ private async ValueTask OnPrintersAsync(AgentSession session, List printers, CancellationToken cancellationToken)
+ {
+ var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (agentEntity is null) return;
+
+ var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
+ if (hostEntity is null) return;
+
+ var batch = ObjectId.GenerateNewId().ToString();
+ var date = DateTime.Now;
+
+ var bulk = new List>();
+
+ if (printers is not null && printers.Count != 0)
+ {
+ foreach (var printer in printers)
+ {
+ var filterDefinition = Builders.Filter.And(new List>
+ {
+ Builders.Filter.Eq(x => x.Host, hostEntity.Id),
+ Builders.Filter.Eq(x => x.Name, printer.Name)
+ });
+
+ var updateDefinition = Builders.Update
+ .SetOnInsert(p => p.Insert, date)
+ .SetOnInsert(p => p.Host, hostEntity.Id)
+ .SetOnInsert(p => p.Name, printer.Name)
+ .Set(p => p.Update, date)
+ .Set(p => p.Batch, batch)
+ .Set(p => p.Port, printer.Port)
+ .Set(p => p.Location, printer.Location)
+ .Set(p => p.Comment, printer.Comment);
+
+ bulk.Add(new UpdateOneModel(filterDefinition, updateDefinition)
+ {
+ IsUpsert = true
+ });
+ }
+ }
+
+ bulk.Add(new DeleteManyModel(Builders.Filter.And(new List