before server asp convertion

This commit is contained in:
kkb 2024-01-10 16:49:56 +01:00
parent ce99053a10
commit a4ed1a5956
82 changed files with 3802 additions and 444 deletions

View file

@ -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}

View file

@ -13,11 +13,6 @@
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
</PropertyGroup>
<ItemGroup>
<!--WMI / System.Management-->
<RuntimeHostConfigurationOption Include="System.Runtime.Loader.UseRidGraph" Value="true" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>none</DebugType>
</PropertyGroup>
@ -27,10 +22,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.10" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.0" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />

View file

@ -0,0 +1,6 @@
namespace Insight.Agent;
internal sealed class Config
{
public Guid? Serial { get; set; }
}

View file

@ -1,6 +0,0 @@
namespace Insight.Agent.Models;
public class Config
{
public Guid? Serial { get; set; }
}

View file

@ -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<IMessageHandler<AgentSession>> 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<IMessageHandler<AgentSession>> handlers, I
_logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
return default;
}
private async ValueTask OnAuthenticationAsync(CancellationToken cancellationToken)
{
Config? config = null;
try
{
config = await Configurator.ReadAsync<Config>(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);
}
}

View file

@ -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<AgentSession>
{
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case AuthenticationRequest:
{
Config? config = null;
try
{
config = await Configurator.ReadAsync<Config>(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;
}
}
}
}

View file

@ -51,7 +51,7 @@ public class OperationSystemHandler : IMessageHandler<AgentSession>
if (@object.TryGetValue<string>(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

View file

@ -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<AgentSession, IMessage, MemPackSerializer<IMessage>>();
});
services.AddSingleton<IMessageHandler<AgentSession>, AuthenticationHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();

View file

@ -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;

View file

@ -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;

View file

@ -2,7 +2,7 @@
using Microsoft.OpenApi.Models;
using System.Reflection;
namespace Insight.Api.Hosting;
namespace Insight.Api.Extensions;
public static class ServiceExtensions
{

View file

@ -22,7 +22,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<!--Unix Serilog stuff-->

View file

@ -1,4 +1,4 @@
using Insight.Api.Hosting;
using Insight.Api.Extensions;
using Insight.Domain.Constants;
using Insight.Infrastructure;
using Microsoft.Extensions.FileProviders;

View file

@ -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",

View file

@ -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",

View file

@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration;
namespace Insight.Agent.Extensions;
namespace Insight.Domain.Extensions;
public static class ConfigurationExtensions
{

View file

@ -1,7 +1,7 @@
using System.Diagnostics;
using System.Runtime.Versioning;
namespace Insight.Agent.Extensions;
namespace Insight.Domain.Extensions;
public static class Linux
{

View file

@ -17,9 +17,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.0" />
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
<PackageReference Include="Vaitr.Network" Version="2023.12.16" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Vaitr.Network" Version="2024.1.8" />
</ItemGroup>
</Project>

View file

@ -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<string?>(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)"),
expires: configuration.GetValue<int?>(Appsettings.JwtExp) ?? throw new Exception($"{Appsettings.JwtExp} value not set (appsettings)"),
audience: configuration.GetValue<Uri?>(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)"),
issuer: configuration.GetValue<Uri?>(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)"));
key: configuration.GetValue<string?>(Appsettings.Jwt.Key) ?? throw new Exception($"{Appsettings.Jwt.Key} value not set (appsettings)"),
expires: configuration.GetValue<int?>(Appsettings.Jwt.Exp) ?? throw new Exception($"{Appsettings.Jwt.Exp} value not set (appsettings)"),
audience: configuration.GetValue<Uri?>(Appsettings.Jwt.Audience) ?? throw new Exception($"{Appsettings.Jwt.Audience} value not set (appsettings)"),
issuer: configuration.GetValue<Uri?>(Appsettings.Jwt.Issuer) ?? throw new Exception($"{Appsettings.Jwt.Issuer} value not set (appsettings)"));
services.AddSingleton(options);
services.AddTransient<TokenService>();
@ -56,7 +54,7 @@ public static partial class ServiceExtensions
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetValue<string?>(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)");
var connectionString = configuration.GetValue<string?>(Appsettings.Mongo.ConnectionString) ?? throw new Exception($"{Appsettings.Mongo.ConnectionString} value not set (appsettings)");
services.AddIdentity<InsightUser, InsightRole>(options =>
{
@ -146,14 +144,14 @@ public static partial class ServiceExtensions
options.TokenValidationParameters.ValidateActor = false;
options.TokenValidationParameters.ValidAudience = configuration.GetValue<string?>(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)");
options.TokenValidationParameters.ValidAudience = configuration.GetValue<string?>(Appsettings.Jwt.Audience) ?? throw new Exception($"{Appsettings.Jwt.Audience} value not set (appsettings)");
options.TokenValidationParameters.ValidateAudience = true;
options.TokenValidationParameters.ValidIssuer = configuration.GetValue<string?>(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)");
options.TokenValidationParameters.ValidIssuer = configuration.GetValue<string?>(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<string?>(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)"))
Encoding.UTF8.GetBytes(configuration.GetValue<string?>(Appsettings.Jwt.Key) ?? throw new Exception($"{Appsettings.Jwt.Key} value not set (appsettings)"))
);
options.TokenValidationParameters.ValidateIssuerSigningKey = true;

View file

@ -12,13 +12,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Insight.Domain\Insight.Domain.csproj" />
<ProjectReference Include="..\Insight.Infrastructure\Insight.Infrastructure.csproj" />
</ItemGroup>

View file

@ -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";
}
}

View file

@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration;
namespace Insight.Server.Extensions;
namespace Insight.Infrastructure;
public static class ConfigurationExtensions
{

View file

@ -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<string?>(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)");
var connectionString = configuration.GetValue<string?>(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);

View file

@ -12,14 +12,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="AspNetCore.Identity.MongoDbCore" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.23.1" />
<PackageReference Include="Vaitr.Scheduler" Version="2023.12.15.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Insight.Domain\Insight.Domain.csproj" />
</ItemGroup>
</Project>

View file

@ -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;

View file

@ -1,6 +1,5 @@
using Insight.Infrastructure.Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
namespace Insight.Infrastructure.Services;

View file

@ -18,6 +18,7 @@
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.6" />
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
</ItemGroup>
<ItemGroup>

View file

@ -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";
}

View file

@ -43,10 +43,17 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
<PackageReference Include="OpenTelemetry.Exporter.InfluxDB" Version="1.0.0-alpha.3" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.4" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.7.0" />
<!--Unix Serilog stuff-->
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Insight.Domain\Insight.Domain.csproj" />
<ProjectReference Include="..\..\Core\Insight.Infrastructure\Insight.Infrastructure.csproj" />
</ItemGroup>

View file

@ -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<IMessageHandler<AgentSession>> handlers,
ISerializer<IMessage> serializer,
ILogger<AgentSession> logger) : TcpSession<IMessage>(serializer, logger)
{
public string? Id { get; set; }
private readonly AgentHandler _agentHandler = agentHandler;
private readonly IMongoDatabase _database = database;
private readonly IEnumerable<IMessageHandler<AgentSession>> _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<IMessage> context, CancellationToken cancellationToken)
{
await base.OnSentAsync(context, cancellationToken);
await _agentHandler.StatisticUpdateAsync(this, cancellationToken);
// update entity
await UpdateAsync(cancellationToken);
}
protected override async ValueTask OnReceivedAsync(IPacketContext<IMessage> 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<AgentEntity>
.Filter
.Eq(p => p.Serial, authentication.Serial.ToString()), Builders<AgentEntity>.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<AgentEntity>
.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<AgentEntity>
.Filter
.Eq(p => p.Id, Id), Builders<AgentEntity>
.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<HostEntity>.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);
}
}
}

View file

@ -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<AgentHandler> logger) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
private readonly ILogger<AgentHandler> _logger = logger;
public async ValueTask HandleAsync<TMessage>(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<AgentEntity>
.Filter
.Eq(p => p.Serial, authentication.Serial.ToString()), Builders<AgentEntity>.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<AgentEntity>
.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<AgentEntity>
.Filter
.Eq(p => p.Id, session.Id), Builders<AgentEntity>
.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);
}
}

View file

@ -183,10 +183,10 @@ public partial class TrapHandler(IMongoDatabase database) : IMessageHandler<Agen
{
var summary = false;
if (Guid.TryParse(data[0].Value, out _))
if (Guid.TryParse(data[0].Value, out _))
summary = true;
if (data[1].Value?.ToLower() == "backup configuration job")
if (data[1].Value?.ToLower() == "backup configuration job")
return false;
status = (summary ? data[2].Value?.ToLower() : data[3].Value?.ToLower()) switch

View file

@ -116,7 +116,7 @@ public class VirtualMaschineHandler(IMongoDatabase database) : IMessageHandler<A
// custom "notes" concat
string notes = string.Empty;
if (config?.Notes is not null)
if (config?.Notes is not null)
foreach (var n in config.Notes) notes += n;
if (config?.Id is null) continue;

View file

@ -2,7 +2,6 @@
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Infrastructure;
using Insight.Server.Extensions;
using Insight.Server.Network.Agent;
using Insight.Server.Network.Agent.Handlers;
using Insight.Server.Network.Globals;
@ -59,7 +58,6 @@ internal class Program
services.AddDatabase(host.Configuration);
// BACKGROUND SERVICES
services.AddHostedService<JobService>();
services.AddHostedService<DispatchService>();
// GLOBAL DEPENDENCIES
@ -84,24 +82,22 @@ internal static class ServiceExtensions
services.UseHostedServer<AgentSession, IMessage>(options =>
{
options.Address = IPAddress.Any;
options.Port = configuration.GetValue<int?>(Appsettings.AgentServerPort) ?? throw new Exception($"{Appsettings.AgentServerPort} value not set (appsettings)");
options.Port = configuration.GetValue<int?>(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<string?>(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)");
options.CertificatePassword = configuration.GetValue<string?>(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)");
options.Certificate = configuration.GetValue<string?>(Appsettings.Agent.Certificate) ?? throw new Exception($"{Appsettings.Agent.Certificate} value not set (appsettings)");
options.CertificatePassword = configuration.GetValue<string?>(Appsettings.Agent.CertificatePassword) ?? throw new Exception($"{Appsettings.Agent.CertificatePassword} value not set (appsettings)");
options.UseSerializer<AgentSession, IMessage, MemPackSerializer<IMessage>>();
});
// HANDLER
services.AddSingleton<AgentHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, AgentHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, DriveHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, Network.Agent.Handlers.EventHandler>();
services.AddSingleton<IMessageHandler<AgentSession>, InterfaceHandler>();
@ -130,15 +126,15 @@ internal static class ServiceExtensions
services.UseHostedServer<WebSession, IMessage>(options =>
{
options.Address = IPAddress.Any;
options.Port = configuration.GetValue<int?>(Appsettings.WebServerPort) ?? throw new Exception($"{Appsettings.WebServerPort} value not set (appsettings)");
options.Port = configuration.GetValue<int?>(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<string?>(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)");
options.CertificatePassword = configuration.GetValue<string?>(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)");
options.Certificate = configuration.GetValue<string?>(Appsettings.Web.Certificate) ?? throw new Exception($"{Appsettings.Web.Certificate} value not set (appsettings)");
options.CertificatePassword = configuration.GetValue<string?>(Appsettings.Web.CertificatePassword) ?? throw new Exception($"{Appsettings.Web.CertificatePassword} value not set (appsettings)");
options.UseSerializer<WebSession, IMessage, MemPackSerializer<IMessage>>();
});

View file

@ -19,7 +19,7 @@ internal class DispatchService(HttpClient httpClient, IMongoDatabase database, I
{
_logger.LogTrace("ExecuteAsync");
var enabled = _configuration.GetValue<bool?>(Appsettings.DispatchWebmatic) ?? throw new Exception($"{Appsettings.DispatchWebmatic} value not set (appsettings)");
var enabled = _configuration.GetValue<bool?>(Appsettings.Dispatch.Webmatic) ?? throw new Exception($"{Appsettings.Dispatch.Webmatic} value not set (appsettings)");
if (enabled is false) return;
try

View file

@ -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<AgentSession, IMessage> agentPool, IMongoDatabase database, ILogger<JobService> logger) : BackgroundService
{
private readonly ISessionPool<AgentSession, IMessage> _agentPool = agentPool;
private readonly IMongoDatabase _database = database;
private readonly ILogger<JobService> _logger = logger;
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogTrace("ExecuteAsync");
var jobs = new List<Task>
{
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<IEnumerable<AgentSession>> GetAssignedAgentsAsync(CancellationToken cancellationToken)
{
var valid = new List<AgentSession>();
await Async.ParallelForEach(_agentPool.Where(p => p.Value.Id is not null), async x =>
{
var host = await _database.Host()
.Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, x.Value.Id))
.FirstOrDefaultAsync(cancellationToken)
.ConfigureAwait(false);
if (host is null) return;
valid.Add(x.Value);
});
return valid;
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> 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();
}
}
}

View file

@ -0,0 +1,52 @@
using System.Threading.Tasks.Dataflow;
namespace Insight.Server.Extensions;
public static class Async
{
public static async Task ParallelForEach<T>(
this IAsyncEnumerable<T> source,
Func<T, Task> body,
int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
TaskScheduler? scheduler = null)
{
var options = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism
};
if (scheduler != null)
options.TaskScheduler = scheduler;
var block = new ActionBlock<T>(body, options);
await foreach (var item in source)
block.Post(item);
block.Complete();
await block.Completion;
}
public static async Task ParallelForEach<T>(
this IEnumerable<T> source,
Func<T, Task> body,
int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
TaskScheduler? scheduler = null)
{
var options = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism
};
if (scheduler != null)
options.TaskScheduler = scheduler;
var block = new ActionBlock<T>(body, options);
foreach (var item in source)
block.Post(item);
block.Complete();
await block.Completion;
}
}

View file

@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Product>Insight</Product>
<AssemblyName>server</AssemblyName>
<AssemblyVersion>2024.1.10.0</AssemblyVersion>
<RootNamespace>Insight.Server</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
<PackageReference Include="OpenTelemetry.Exporter.InfluxDB" Version="1.0.0-alpha.3" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.4" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.7.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Insight.Domain\Insight.Domain.csproj" />
<ProjectReference Include="..\..\Core\Insight.Infrastructure\Insight.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="localhost.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,6 @@
@Insight.Server2_HostAddress = http://localhost:5197
GET {{Insight.Server2_HostAddress}}/weatherforecast/
Accept: application/json
###

View file

@ -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
}
}

View file

@ -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<IMessageHandler<AgentSession>> handlers,
ISerializer<IMessage> serializer,
ILogger<AgentSession> logger) : TcpSession<IMessage>(serializer, logger)
{
public string? Id { get; set; }
private readonly IMongoDatabase _database = database;
private readonly IEnumerable<IMessageHandler<AgentSession>> _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<IMessage> context, CancellationToken cancellationToken)
{
await base.OnSentAsync(context, cancellationToken);
// update entity
await UpdateAsync(cancellationToken);
}
protected override async ValueTask OnReceivedAsync(IPacketContext<IMessage> 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<AgentEntity>
.Filter
.Eq(p => p.Serial, authentication.Serial.ToString()), Builders<AgentEntity>.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<AgentEntity>
.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<AgentEntity>
.Filter
.Eq(p => p.Id, Id), Builders<AgentEntity>
.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<HostEntity>.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);
}
}
}

View file

@ -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<CustomHandler> logger) : IMessageHandler<AgentSession>
{
private readonly ILogger<CustomHandler> _logger = logger;
public async ValueTask HandleAsync<TMessage>(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;
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Drive> drives:
await OnDrivesAsync(sender, drives, cancellationToken);
break;
}
}
private async ValueTask OnDrivesAsync(AgentSession session, List<Drive> drives, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostDriveEntity>>();
if (drives is not null && drives.Count != 0)
{
foreach (var drive in drives)
{
var driveFilter = Builders<HostDriveEntity>.Filter.And(new List<FilterDefinition<HostDriveEntity>>
{
Builders<HostDriveEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostDriveEntity>.Filter.Eq(x => x.Index, drive.Index)
});
var driveUpdate = Builders<HostDriveEntity>.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<HostDriveEntity>(driveFilter, driveUpdate)
{
IsUpsert = true
});
}
}
driveBulk.Add(new DeleteManyModel<HostDriveEntity>(Builders<HostDriveEntity>.Filter.And(new List<FilterDefinition<HostDriveEntity>>
{
Builders<HostDriveEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostDriveEntity>.Filter.Ne(x => x.Batch, batch)
})));
var driveResult = await _database.HostDrive().BulkWriteAsync(driveBulk, cancellationToken: cancellationToken);
// volumes
var volumeBulk = new List<WriteModel<HostVolumeEntity>>();
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<HostVolumeEntity>.Filter.And(new List<FilterDefinition<HostVolumeEntity>>
{
Builders<HostVolumeEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostVolumeEntity>.Filter.Eq(x => x.Drive, driveId),
Builders<HostVolumeEntity>.Filter.Eq(x => x.Index, volume.Index)
});
var volumeUpdate = Builders<HostVolumeEntity>.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<HostVolumeEntity>(volumeFilter, volumeUpdate)
{
IsUpsert = true
});
}
}
}
}
volumeBulk.Add(new DeleteManyModel<HostVolumeEntity>(Builders<HostVolumeEntity>.Filter.And(new List<FilterDefinition<HostVolumeEntity>>
{
Builders<HostVolumeEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostVolumeEntity>.Filter.Ne(x => x.Batch, batch)
})));
var volumeResult = await _database.HostVolume().BulkWriteAsync(volumeBulk, cancellationToken: cancellationToken);
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(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<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<AgentLogEntity?> InsertAgentLogAsync(AgentSession session, Event @event, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent()
.Aggregate()
.Match(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id))
.Lookup<AgentEntity, HostEntity, AgentEntity>(_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<HostLogEntity?> 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<int>
{
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;
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Interface> interfaces:
await OnInterfacesAsync(sender, interfaces, cancellationToken);
break;
}
}
private async ValueTask OnInterfacesAsync(AgentSession session, List<Interface> interfaces, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostInterfaceEntity>>();
foreach (var @interface in interfaces)
{
var interfaceFilter = Builders<HostInterfaceEntity>.Filter.And(new List<FilterDefinition<HostInterfaceEntity>>
{
Builders<HostInterfaceEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceEntity>.Filter.Eq(x => x.Index, @interface.Index)
});
var interfaceUpdate = Builders<HostInterfaceEntity>.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<HostInterfaceEntity>(interfaceFilter, interfaceUpdate)
{
IsUpsert = true
});
}
interfaceBulk.Add(new DeleteManyModel<HostInterfaceEntity>(Builders<HostInterfaceEntity>.Filter.And(new List<FilterDefinition<HostInterfaceEntity>>
{
Builders<HostInterfaceEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceEntity>.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<WriteModel<HostInterfaceAddressEntity>>();
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<HostInterfaceAddressEntity>.Filter.And(new List<FilterDefinition<HostInterfaceAddressEntity>>
{
Builders<HostInterfaceAddressEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceAddressEntity>.Filter.Eq(x => x.Interface, interfaceId),
Builders<HostInterfaceAddressEntity>.Filter.Eq(x => x.Address, address?.IpAddress?.Address),
Builders<HostInterfaceAddressEntity>.Filter.Eq(x => x.Mask, address?.Ipv4Mask?.Address)
});
var addressUpdate = Builders<HostInterfaceAddressEntity>.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<HostInterfaceAddressEntity>(addressFilter, addressUpdate)
{
IsUpsert = true
});
}
}
}
addressBulk.Add(new DeleteManyModel<HostInterfaceAddressEntity>(Builders<HostInterfaceAddressEntity>.Filter.And(new List<FilterDefinition<HostInterfaceAddressEntity>>
{
Builders<HostInterfaceAddressEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceAddressEntity>.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<WriteModel<HostInterfaceGatewayEntity>>();
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<HostInterfaceGatewayEntity>.Filter.And(new List<FilterDefinition<HostInterfaceGatewayEntity>>
{
Builders<HostInterfaceGatewayEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceGatewayEntity>.Filter.Eq(x => x.Interface, interfaceId),
Builders<HostInterfaceGatewayEntity>.Filter.Eq(x => x.Address, gateway?.Address)
});
var gatewayUpdate = Builders<HostInterfaceGatewayEntity>.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<HostInterfaceGatewayEntity>(gatewayFilter, gatewayUpdate)
{
IsUpsert = true
});
}
}
}
gatewayBulk.Add(new DeleteManyModel<HostInterfaceGatewayEntity>(Builders<HostInterfaceGatewayEntity>.Filter.And(new List<FilterDefinition<HostInterfaceGatewayEntity>>
{
Builders<HostInterfaceGatewayEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceGatewayEntity>.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<WriteModel<HostInterfaceNameserverEntity>>();
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<HostInterfaceNameserverEntity>.Filter.And(new List<FilterDefinition<HostInterfaceNameserverEntity>>
{
Builders<HostInterfaceNameserverEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceNameserverEntity>.Filter.Eq(x => x.Interface, interfaceId),
Builders<HostInterfaceNameserverEntity>.Filter.Eq(x => x.Address, nameserver?.Address)
});
var nameserverUpdate = Builders<HostInterfaceNameserverEntity>.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<HostInterfaceNameserverEntity>(nameserverFilter, nameserverUpdate)
{
IsUpsert = true
});
}
}
}
nameserverBulk.Add(new DeleteManyModel<HostInterfaceNameserverEntity>(Builders<HostInterfaceNameserverEntity>.Filter.And(new List<FilterDefinition<HostInterfaceNameserverEntity>>
{
Builders<HostInterfaceNameserverEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceNameserverEntity>.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<WriteModel<HostInterfaceRouteEntity>>();
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<HostInterfaceRouteEntity>.Filter.And(new List<FilterDefinition<HostInterfaceRouteEntity>>
{
Builders<HostInterfaceRouteEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceRouteEntity>.Filter.Eq(x => x.Interface, interfaceId),
Builders<HostInterfaceRouteEntity>.Filter.Eq(x => x.Destination, route?.Destination?.Address),
Builders<HostInterfaceRouteEntity>.Filter.Eq(x => x.Gateway, route?.Gateway?.Address),
Builders<HostInterfaceRouteEntity>.Filter.Eq(x => x.Mask, route?.Mask),
});
var routeUpdate = Builders<HostInterfaceRouteEntity>.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<HostInterfaceRouteEntity>(routeFilter, routeUpdate)
{
IsUpsert = true
});
}
}
}
routeBulk.Add(new DeleteManyModel<HostInterfaceRouteEntity>(Builders<HostInterfaceRouteEntity>.Filter.And(new List<FilterDefinition<HostInterfaceRouteEntity>>
{
Builders<HostInterfaceRouteEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostInterfaceRouteEntity>.Filter.Ne(x => x.Batch, batch)
})));
var routeResult = await _database.HostInterfaceRoute().BulkWriteAsync(routeBulk, cancellationToken: cancellationToken);
}
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(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<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<HostMainboardEntity>.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);
}
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Memory> memory:
await OnMemoryAsync(sender, memory, cancellationToken);
break;
}
}
private async ValueTask OnMemoryAsync(AgentSession session, List<Memory> memory, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostMemoryEntity>>();
if (memory is not null && memory.Count != 0)
{
foreach (var mem in memory)
{
var filterDefinition = Builders<HostMemoryEntity>.Filter.And(new List<FilterDefinition<HostMemoryEntity>>
{
Builders<HostMemoryEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostMemoryEntity>.Filter.Eq(x => x.Index, mem.Index)
});
var updateDefinition = Builders<HostMemoryEntity>.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<HostMemoryEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
bulk.Add(new DeleteManyModel<HostMemoryEntity>(Builders<HostMemoryEntity>.Filter.And(new List<FilterDefinition<HostMemoryEntity>>
{
Builders<HostMemoryEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostMemoryEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostMemory().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(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<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<HostOsEntity>.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);
}
}

View file

@ -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<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Printer> printers:
await OnPrintersAsync(sender, printers, cancellationToken);
break;
}
}
private async ValueTask OnPrintersAsync(AgentSession session, List<Printer> printers, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostPrinterEntity>>();
if (printers is not null && printers.Count != 0)
{
foreach (var printer in printers)
{
var filterDefinition = Builders<HostPrinterEntity>.Filter.And(new List<FilterDefinition<HostPrinterEntity>>
{
Builders<HostPrinterEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostPrinterEntity>.Filter.Eq(x => x.Name, printer.Name)
});
var updateDefinition = Builders<HostPrinterEntity>.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<HostPrinterEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
bulk.Add(new DeleteManyModel<HostPrinterEntity>(Builders<HostPrinterEntity>.Filter.And(new List<FilterDefinition<HostPrinterEntity>>
{
Builders<HostPrinterEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostPrinterEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostPrinter().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -0,0 +1,83 @@
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 ProcessorHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Processor> processors:
await OnProcessorsAsync(sender, processors, cancellationToken);
break;
}
}
private async ValueTask OnProcessorsAsync(AgentSession session, List<Processor> processors, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostProcessorEntity>>();
if (processors is not null && processors.Count != 0)
{
foreach (var processor in processors)
{
var filterDefinition = Builders<HostProcessorEntity>.Filter.And(new List<FilterDefinition<HostProcessorEntity>>
{
Builders<HostProcessorEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostProcessorEntity>.Filter.Eq(x => x.Index, processor.Index)
});
var updateDefinition = Builders<HostProcessorEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Index, processor.Index)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Company, processor.Manufacturer)
.Set(p => p.Name, processor.Name)
.Set(p => p.Socket, processor.Socket)
.Set(p => p.Serial, processor.SerialNumber)
.Set(p => p.Version, processor.Version)
.Set(p => p.Cores, processor.Cores)
.Set(p => p.LogicalCores, processor.LogicalCores)
.Set(p => p.Clock, processor.MaxSpeed)
.Set(p => p.CurrentClock, processor.CurrentSpeed)
.Set(p => p.L1Size, processor.L1Size)
.Set(p => p.L2Size, processor.L2Size)
.Set(p => p.L3Size, processor.L3Size)
.Set(p => p.Virtualization, processor.Virtualization)
.Set(p => p.PNP, processor.DeviceId);
bulk.Add(new UpdateOneModel<HostProcessorEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
bulk.Add(new DeleteManyModel<HostProcessorEntity>(Builders<HostProcessorEntity>.Filter.And(new List<FilterDefinition<HostProcessorEntity>>
{
Builders<HostProcessorEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostProcessorEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostProcessor().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -0,0 +1,77 @@
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 ServiceHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Service> services:
await OnServicesAsync(sender, services, cancellationToken);
break;
}
}
private async ValueTask OnServicesAsync(AgentSession session, List<Service> services, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostServiceEntity>>();
if (services is not null && services.Count != 0)
{
foreach (var service in services)
{
var filterDefinition = Builders<HostServiceEntity>.Filter.And(new List<FilterDefinition<HostServiceEntity>>
{
Builders<HostServiceEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostServiceEntity>.Filter.Eq(x => x.Name, service.Name)
});
var updateDefinition = Builders<HostServiceEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Name, service.Name)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.DisplayName, service.Display)
.Set(p => p.Description, service.Description)
.Set(p => p.StartMode, service.StartMode.ToString())
.Set(p => p.State, service.Status.ToString())
.Set(p => p.ProcessId, service.ProcessId)
.Set(p => p.Delay, service.Delay)
.Set(p => p.Path, service.PathName)
.Set(p => p.Account, service.Account);
bulk.Add(new UpdateOneModel<HostServiceEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
bulk.Add(new DeleteManyModel<HostServiceEntity>(Builders<HostServiceEntity>.Filter.And(new List<FilterDefinition<HostServiceEntity>>
{
Builders<HostServiceEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostServiceEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostService().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -0,0 +1,73 @@
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 SessionHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Session> sessions:
await OnSessionsAsync(sender, sessions, cancellationToken);
break;
}
}
private async ValueTask OnSessionsAsync(AgentSession session, List<Session> sessions, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostSessionEntity>>();
if (sessions is not null && sessions.Count != 0)
{
foreach (var sess in sessions)
{
var filterDefinition = Builders<HostSessionEntity>.Filter.And(new List<FilterDefinition<HostSessionEntity>>
{
Builders<HostSessionEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSessionEntity>.Filter.Eq(x => x.Sid, sess.Sid)
});
var updateDefinition = Builders<HostSessionEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Sid, sess.Sid)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.User, sess.User)
.Set(p => p.Remote, sess.Remote)
.Set(p => p.Type, sess.Type)
.Set(p => p.State, sess.Status);
bulk.Add(new UpdateOneModel<HostSessionEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
bulk.Add(new DeleteManyModel<HostSessionEntity>(Builders<HostSessionEntity>.Filter.And(new List<FilterDefinition<HostSessionEntity>>
{
Builders<HostSessionEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSessionEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostSession().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -0,0 +1,74 @@
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 SoftwareHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Application> applications:
await OnApplicationsAsync(sender, applications, cancellationToken);
break;
}
}
private async ValueTask OnApplicationsAsync(AgentSession session, List<Application> applications, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostApplicationEntity>>();
if (applications is not null && applications.Count != 0)
{
foreach (var app in applications)
{
var filterDefinition = Builders<HostApplicationEntity>.Filter.And(new List<FilterDefinition<HostApplicationEntity>>
{
Builders<HostApplicationEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostApplicationEntity>.Filter.Eq(x => x.Name, app.Name),
Builders<HostApplicationEntity>.Filter.Eq(x => x.Architecture, app.Architecture?.ToString())
});
var updateDefinition = Builders<HostApplicationEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Name, app.Name)
.SetOnInsert(p => p.Architecture, app.Architecture?.ToString())
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Company, app.Publisher)
.Set(p => p.Version, app.Version)
.Set(p => p.InstallDate, app.InstallDate);
bulk.Add(new UpdateOneModel<HostApplicationEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
bulk.Add(new DeleteManyModel<HostApplicationEntity>(Builders<HostApplicationEntity>.Filter.And(new List<FilterDefinition<HostApplicationEntity>>
{
Builders<HostApplicationEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostApplicationEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostApplication().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -0,0 +1,243 @@
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 StoragePoolHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<StoragePool> storagePools:
await OnStoragePoolsAsync(sender, storagePools, cancellationToken);
break;
}
}
private async ValueTask OnStoragePoolsAsync(AgentSession session, List<StoragePool>? storagePools, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return;
var batch = ObjectId.GenerateNewId().ToString();
var date = DateTime.Now;
// storagepools
if (storagePools is not null && storagePools.Count != 0)
{
var storagepoolBulk = new List<WriteModel<HostStoragePoolEntity>>();
foreach (var storagePool in storagePools)
{
var storagePoolFilter = Builders<HostStoragePoolEntity>.Filter.And(new List<FilterDefinition<HostStoragePoolEntity>>
{
Builders<HostStoragePoolEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostStoragePoolEntity>.Filter.Eq(x => x.UniqueId, storagePool.UniqueId)
});
List<string>? states = null;
if (storagePool.States is not null)
{
states = [];
foreach (var state in storagePool.States)
states.Add(state.ToString());
}
var storagePoolUpdate = Builders<HostStoragePoolEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.UniqueId, storagePool.UniqueId)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Name, storagePool.FriendlyName)
.Set(p => p.Health, storagePool.Health?.ToString())
.Set(p => p.Resiliency, storagePool.Resiliency)
.Set(p => p.Primordial, storagePool.IsPrimordial)
.Set(p => p.ReadOnly, storagePool.IsReadOnly)
.Set(p => p.Clustered, storagePool.IsClustered)
.Set(p => p.Size, storagePool.Size)
.Set(p => p.AllocatedSize, storagePool.AllocatedSize)
.Set(p => p.SectorSize, storagePool.SectorSize)
.Set(p => p.States, states);
storagepoolBulk.Add(new UpdateOneModel<HostStoragePoolEntity>(storagePoolFilter, storagePoolUpdate)
{
IsUpsert = true
});
}
storagepoolBulk.Add(new DeleteManyModel<HostStoragePoolEntity>(Builders<HostStoragePoolEntity>.Filter.And(new List<FilterDefinition<HostStoragePoolEntity>>
{
Builders<HostStoragePoolEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostStoragePoolEntity>.Filter.Ne(x => x.Batch, batch)
})));
var storagePoolResult = await _database.HostStoragePool().BulkWriteAsync(storagepoolBulk, cancellationToken: cancellationToken);
}
// physicaldisks
if (storagePools is not null && storagePools.Count != 0)
{
var physicalDiskBulk = new List<WriteModel<HostStoragePoolPhysicalDiskEntity>>();
foreach (var storagePool in storagePools)
{
var storagePoolId = await _database.HostStoragePool()
.Find(p => p.Host == hostEntity.Id && p.UniqueId == storagePool.UniqueId)
.Project(p => p.Id)
.FirstOrDefaultAsync(cancellationToken: default);
if (storagePool.PhysicalDisks is not null && storagePool.PhysicalDisks.Count != 0)
{
foreach (var physicalDisk in storagePool.PhysicalDisks)
{
var physicalDiskFilter = Builders<HostStoragePoolPhysicalDiskEntity>.Filter.And(new List<FilterDefinition<HostStoragePoolPhysicalDiskEntity>>
{
Builders<HostStoragePoolPhysicalDiskEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostStoragePoolPhysicalDiskEntity>.Filter.Eq(x => x.StoragePool, storagePoolId),
Builders<HostStoragePoolPhysicalDiskEntity>.Filter.Eq(x => x.UniqueId, physicalDisk.UniqueId)
});
List<string>? states = null;
if (physicalDisk.States is not null)
{
states = [];
foreach (var state in physicalDisk.States)
states.Add(state.ToString());
}
var physicalDiskUpdate = Builders<HostStoragePoolPhysicalDiskEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.StoragePool, storagePoolId)
.SetOnInsert(p => p.UniqueId, physicalDisk.UniqueId)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.DeviceId, physicalDisk.DeviceId)
.Set(p => p.Name, physicalDisk.FriendlyName)
.Set(p => p.Manufacturer, physicalDisk.Manufacturer)
.Set(p => p.Model, physicalDisk.Model)
.Set(p => p.Media, physicalDisk.MediaType.ToString())
.Set(p => p.Bus, physicalDisk.BusType.ToString())
.Set(p => p.Health, physicalDisk.Health.ToString())
.Set(p => p.Usage, physicalDisk.Usage)
.Set(p => p.Location, physicalDisk.PhysicalLocation)
.Set(p => p.Serial, physicalDisk.SerialNumber)
.Set(p => p.Firmware, physicalDisk.FirmwareVersion)
.Set(p => p.Size, physicalDisk.Size)
.Set(p => p.AllocatedSize, physicalDisk.AllocatedSize)
.Set(p => p.Footprint, physicalDisk.VirtualDiskFootprint)
.Set(p => p.LogicalSectorSize, physicalDisk.LogicalSectorSize)
.Set(p => p.PhysicalSectorSize, physicalDisk.PhysicalSectorSize)
.Set(p => p.States, states);
physicalDiskBulk.Add(new UpdateOneModel<HostStoragePoolPhysicalDiskEntity>(physicalDiskFilter, physicalDiskUpdate)
{
IsUpsert = true
});
}
}
}
physicalDiskBulk.Add(new DeleteManyModel<HostStoragePoolPhysicalDiskEntity>(Builders<HostStoragePoolPhysicalDiskEntity>.Filter.And(new List<FilterDefinition<HostStoragePoolPhysicalDiskEntity>>
{
Builders<HostStoragePoolPhysicalDiskEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostStoragePoolPhysicalDiskEntity>.Filter.Ne(x => x.Batch, batch)
})));
var physicalDiskResult = await _database.HostStoragePoolPhysicalDisk().BulkWriteAsync(physicalDiskBulk, cancellationToken: cancellationToken);
}
// virtual disks
if (storagePools is not null && storagePools.Count != 0)
{
var virtualDiskBulk = new List<WriteModel<HostStoragePoolVirtualDiskEntity>>();
foreach (var storagePool in storagePools)
{
if (storagePool.VirtualDisks is not null && storagePool.VirtualDisks.Count != 0)
{
foreach (var virtualDisk in storagePool.VirtualDisks)
{
var storagePoolId = await _database.HostStoragePool()
.Find(p => p.Host == hostEntity.Id && p.UniqueId == storagePool.UniqueId)
.Project(p => p.Id)
.FirstOrDefaultAsync(cancellationToken: default);
var virtualDiskFilter = Builders<HostStoragePoolVirtualDiskEntity>.Filter.And(new List<FilterDefinition<HostStoragePoolVirtualDiskEntity>>
{
Builders<HostStoragePoolVirtualDiskEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostStoragePoolVirtualDiskEntity>.Filter.Eq(x => x.StoragePool, storagePoolId),
Builders<HostStoragePoolVirtualDiskEntity>.Filter.Eq(x => x.UniqueId, virtualDisk.UniqueId)
});
List<string>? states = null;
if (virtualDisk.States is not null)
{
states = [];
foreach (var state in virtualDisk.States)
states.Add(state.ToString());
}
var virtualDiskUpdate = Builders<HostStoragePoolVirtualDiskEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.StoragePool, storagePoolId)
.SetOnInsert(p => p.UniqueId, virtualDisk.UniqueId)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Name, virtualDisk.FriendlyName)
.Set(p => p.Health, virtualDisk.Health.ToString())
.Set(p => p.Access, virtualDisk.AccessType.ToString())
.Set(p => p.Provisioning, virtualDisk.ProvisioningType.ToString())
.Set(p => p.PhysicalRedundancy, virtualDisk.PhysicalDiskRedundancy)
.Set(p => p.Resiliency, virtualDisk.ResiliencySettingName)
.Set(p => p.Deduplication, virtualDisk.Deduplication)
.Set(p => p.Snapshot, virtualDisk.IsSnapshot)
.Set(p => p.Size, virtualDisk.Size)
.Set(p => p.AllocatedSize, virtualDisk.AllocatedSize)
.Set(p => p.Footprint, virtualDisk.FootprintOnPool)
.Set(p => p.ReadCacheSize, virtualDisk.ReadCacheSize)
.Set(p => p.WriteCacheSize, virtualDisk.WriteCacheSize)
.Set(p => p.States, states);
virtualDiskBulk.Add(new UpdateOneModel<HostStoragePoolVirtualDiskEntity>(virtualDiskFilter, virtualDiskUpdate)
{
IsUpsert = true
});
}
}
}
virtualDiskBulk.Add(new DeleteManyModel<HostStoragePoolVirtualDiskEntity>(Builders<HostStoragePoolVirtualDiskEntity>.Filter.And(new List<FilterDefinition<HostStoragePoolVirtualDiskEntity>>
{
Builders<HostStoragePoolVirtualDiskEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostStoragePoolVirtualDiskEntity>.Filter.Ne(x => x.Batch, batch)
})));
var virtualDiskResult = await _database.HostStoragePoolVirtualDisk().BulkWriteAsync(virtualDiskBulk, cancellationToken: cancellationToken);
}
}
}

View file

@ -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 SystemInfoHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case SystemInfo systemInfo:
await OnSystemInfoAsync(sender, systemInfo, cancellationToken);
break;
}
}
private async ValueTask OnSystemInfoAsync(AgentSession session, SystemInfo? system, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session?.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, agentEntity?.Id)).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return;
var date = DateTime.Now;
if (hostEntity.Id is null) return;
await _database.HostSystem().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders<HostSystemEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.Set(p => p.Update, date)
.Set(p => p.BootUpTime, system?.LastBootUpTime)
.Set(p => p.LocalTime, system?.LocalDateTime)
.Set(p => p.Processes, system?.Processes)
.Set(p => p.License, system?.License), new UpdateOptions
{
IsUpsert = true
}, cancellationToken);
}
}

View file

@ -0,0 +1,264 @@
using Insight.Domain.Enums;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Domain.Network.Agent.Messages;
using Insight.Infrastructure.Entities;
using Insight.Server.Models;
using MongoDB.Driver;
using System.Text;
using System.Text.RegularExpressions;
using static Insight.Server.Models.MonitorMessage;
namespace Insight.Server.Network.Agent.Handlers;
public partial class TrapHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Trap trap:
await OnTrapAsync(sender, trap, cancellationToken);
break;
}
}
private async ValueTask OnTrapAsync(AgentSession session, Trap? trap, CancellationToken cancellationToken)
{
if (trap is null) return;
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return;
if (TryParse(trap, out var monitoring) is false)
{
//_logger.LogWarning($"Failed to parse");
}
// insert monitoring log
var monitoringLog = new HostLogMonitoringEntity
{
Host = hostEntity.Id,
Insert = DateTime.Now,
Timestamp = monitoring?.Timestamp,
Category = monitoring?.Category?.ToString(),
Status = monitoring?.Status?.ToString(),
Hostname = monitoring?.Hostname,
Task = monitoring?.Subject,
Message = monitoring?.Message,
Dispatch = DispatchEnum.Pending.ToString()
};
await _database.HostLogMonitoring()
.InsertOneAsync(monitoringLog, cancellationToken: cancellationToken);
// insert host log
var log = new HostLogEntity
{
Insert = monitoringLog.Insert,
Host = monitoringLog.Host,
Category = monitoringLog.Category,
Status = monitoringLog.Status,
Source = "Trap",
Message = monitoringLog?.Task,
Timestamp = monitoringLog?.Insert
};
if (monitoringLog?.Message is not null) log.Message += $"\n{monitoringLog.Message}";
await _database.HostLog()
.InsertOneAsync(log, cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
private static bool TryParse(Trap packet, out MonitorMessage? monitoring)
{
monitoring = null;
if (packet is null || packet.Data is null || packet.Data.Count == 0)
return false;
monitoring = new MonitorMessage
{
Community = packet.Community,
Category = CategoryEnum.Monitoring,
Endpoint = packet.Endpoint,
Timestamp = packet.Timestamp
};
if (Enum.TryParse<ApplicationEnum>(packet.Community, true, out var application))
monitoring.Application = application;
StatusEnum? status;
string? task;
string? message;
switch (application)
{
case ApplicationEnum.Acronis:
monitoring.Application = ApplicationEnum.Acronis;
if (ParseAcronis(packet.Data, out status, out task, out message) is false) return false;
break;
case ApplicationEnum.Veeam:
monitoring.Application = ApplicationEnum.Veeam;
if (ParseVeeam(packet.Data, out status, out task, out message) is false) return false;
break;
case ApplicationEnum.QNAP:
monitoring.Application = ApplicationEnum.QNAP;
monitoring.Category = CategoryEnum.System;
monitoring.Hostname = packet.Hostname;
if (ParseQnap(packet.Data, out status, out task, out message) is false) return false;
break;
default:
return false;
}
monitoring.Status = status;
monitoring.Subject = task;
monitoring.Message = message;
return true;
}
[GeneratedRegex(@"\s+")]
private static partial Regex AcronisRegex();
private static bool ParseAcronis(List<KeyValuePair<string, string?>> data, out StatusEnum? status, out string? task, out string? message)
{
status = data[0].Value?.ToLower() switch
{
"erfolgreich" => StatusEnum.Information,
"success" => StatusEnum.Information,
"information" => StatusEnum.Information,
"warnung" => StatusEnum.Warning,
"warning" => StatusEnum.Warning,
"fehler" => StatusEnum.Error,
"failed" => StatusEnum.Error,
"error" => StatusEnum.Error,
_ => null,
};
task = null;
message = null;
var trim = data[1].Value?.Split(':', StringSplitOptions.None);
if (trim is null || trim.Length == 0) return false;
task = trim[1].Split("'", StringSplitOptions.None)[1].Split("'")[0].Trim();
message = trim[1].Split("' ", StringSplitOptions.None)[1].Trim();
if (message is not null) return true;
if (data[1].Value is not string val) return false;
var content = AcronisRegex().Replace(val, "");
var bytes = Enumerable.Range(0, content.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(content.Substring(x, 2), 16))
.ToArray();
content = Encoding.UTF8.GetString(bytes);
trim = content.Split(':', StringSplitOptions.None);
if (trim is null || trim.Length == 0) return false;
task = trim[1].Split("'", StringSplitOptions.None)[1].Split("'")[0].Trim();
message = trim[1].Split("' ", StringSplitOptions.None)[1].Trim();
return message is not null;
}
private static bool ParseVeeam(List<KeyValuePair<string, string?>> data, out StatusEnum? status, out string? task, out string? message)
{
status = null;
task = null;
message = null;
var parsed = false;
try
{
var summary = false;
if (Guid.TryParse(data[0].Value, out _))
summary = true;
if (data[1].Value?.ToLower() == "backup configuration job")
return false;
status = (summary ? data[2].Value?.ToLower() : data[3].Value?.ToLower()) switch
{
"success" => StatusEnum.Information,
"warning" => StatusEnum.Warning,
"failed" => StatusEnum.Error,
"error" => StatusEnum.Error,
_ => null,
};
task = data[1].Value;
parsed = true;
}
catch (Exception)
{
}
if (parsed) return true;
return false;
}
private static bool ParseQnap(List<KeyValuePair<string, string?>> data, out StatusEnum? status, out string? task, out string? message)
{
status = StatusEnum.Information;
task = null;
message = string.Empty;
var parsed = false;
try
{
var keywords = new Dictionary<string, StatusEnum>
{
{ "power", StatusEnum.Information },
{ "rights", StatusEnum.Information },
{ "added", StatusEnum.Information },
{ "changed", StatusEnum.Information },
{ "password", StatusEnum.Information },
{ "firmware", StatusEnum.Information },
{ "restarting", StatusEnum.Information },
{ "detected", StatusEnum.Warning },
{ "external", StatusEnum.Warning },
{ "threshold", StatusEnum.Warning },
{ "file system", StatusEnum.Warning },
{ "raid", StatusEnum.Warning },
{ "full", StatusEnum.Error },
{ "failure", StatusEnum.Error },
{ "failed", StatusEnum.Error },
{ "resyncing", StatusEnum.Error },
{ "degraded", StatusEnum.Error },
{ "error", StatusEnum.Error },
{ "without error", StatusEnum.Information },
{ "ncsi", StatusEnum.Information }
};
foreach (var key in keywords)
if (Regex.IsMatch(string.Concat(data).ToLowerInvariant(), $@"\b{key.Key}\b"))
status = key.Value;
foreach (var kv in data)
message += kv.Value;
parsed = true;
}
catch (Exception)
{
//_logger.LogError("{ex}", ex);
}
if (parsed) return true;
return false;
}
}

View file

@ -0,0 +1,117 @@
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 UpdateHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case UpdateCollection updates:
await OnUpdatesAsync(sender, updates, cancellationToken);
break;
}
}
private async ValueTask OnUpdatesAsync(AgentSession session, UpdateCollection updates, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostUpdateEntity>>();
if (updates is not null)
{
if (updates.Installed is not null && updates.Installed.Count != 0)
{
foreach (var update in updates.Installed)
{
var filterDefinition = Builders<HostUpdateEntity>.Filter.And(new List<FilterDefinition<HostUpdateEntity>>
{
Builders<HostUpdateEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostUpdateEntity>.Filter.Eq(x => x.Serial, update.Id)
});
var updateDefinition = Builders<HostUpdateEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Serial, update.Id)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Pending, false)
.Set(p => p.Name, update.Name)
.Set(p => p.Description, update.Description)
.Set(p => p.SupportUrl, update.SupportUrl)
.Set(p => p.Result, update.Result.ToString())
.Set(p => p.Date, update.Date);
bulk.Add(new UpdateOneModel<HostUpdateEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
if (updates.Pending is not null && updates.Pending.Count != 0)
{
foreach (var update in updates.Pending)
{
var filterDefinition = Builders<HostUpdateEntity>.Filter.And(new List<FilterDefinition<HostUpdateEntity>>
{
Builders<HostUpdateEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostUpdateEntity>.Filter.Eq(x => x.Serial, update.Id)
});
var updateDefinition = Builders<HostUpdateEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Serial, update.Id)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Pending, true)
.Set(p => p.Name, update.Name)
.Set(p => p.Description, update.Description)
.Set(p => p.SupportUrl, update.SupportUrl)
.Set(p => p.Result, update.Result?.ToString())
.Set(p => p.Type, update.Type.ToString())
.Set(p => p.Size, update.Size)
.Set(p => p.IsDownloaded, update.IsDownloaded)
.Set(p => p.CanRequestUserInput, update.CanRequestUserInput)
.Set(p => p.RebootBehavior, update.RebootBehavior?.ToString())
.Set(p => p.Date, update.Date);
bulk.Add(new UpdateOneModel<HostUpdateEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
}
}
bulk.Add(new DeleteManyModel<HostUpdateEntity>(Builders<HostUpdateEntity>.Filter.And(new List<FilterDefinition<HostUpdateEntity>>
{
Builders<HostUpdateEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostUpdateEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostUpdate().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}

View file

@ -0,0 +1,183 @@
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 UserHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<User> users:
await OnUsersAsync(sender, users, cancellationToken);
break;
}
}
private async ValueTask OnUsersAsync(AgentSession session, List<User>? users, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return;
var batch = ObjectId.GenerateNewId().ToString();
var date = DateTime.Now;
// users
if (users is not null && users.Count != 0)
{
var userBulk = new List<WriteModel<HostSysUserEntity>>();
foreach (var user in users)
{
var userFilter = Builders<HostSysUserEntity>.Filter.And(new List<FilterDefinition<HostSysUserEntity>>
{
Builders<HostSysUserEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSysUserEntity>.Filter.Eq(x => x.Domain, user?.Domain),
Builders<HostSysUserEntity>.Filter.Eq(x => x.Name, user?.Name)
});
var userUpdate = Builders<HostSysUserEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Domain, user?.Domain)
.SetOnInsert(p => p.Name, user?.Name)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Sid, user?.Sid)
.Set(p => p.FullName, user?.FullName)
.Set(p => p.Description, user?.Description)
.Set(p => p.Status, user?.Status)
.Set(p => p.LocalAccount, user?.LocalAccount)
.Set(p => p.Disabled, user?.Disabled)
.Set(p => p.Lockout, user?.Lockout)
.Set(p => p.PasswordChangeable, user?.PasswordChangeable)
.Set(p => p.PasswordExpires, user?.PasswordExpires)
.Set(p => p.PasswordRequired, user?.PasswordRequired);
userBulk.Add(new UpdateOneModel<HostSysUserEntity>(userFilter, userUpdate)
{
IsUpsert = true
});
}
userBulk.Add(new DeleteManyModel<HostSysUserEntity>(Builders<HostSysUserEntity>.Filter.And(new List<FilterDefinition<HostSysUserEntity>>
{
Builders<HostSysUserEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSysUserEntity>.Filter.Ne(x => x.Batch, batch)
})));
var userResult = await _database.HostSystemUser().BulkWriteAsync(userBulk, cancellationToken: cancellationToken);
}
// groups
if (users is not null && users.Count != 0)
{
var groupBulk = new List<WriteModel<HostSysGroupEntity>>();
var distinctGroups = users.SelectMany(p => p.Groups ?? [])
.GroupBy(p => new { p?.Domain, p?.Name })
.Select(p => p.First());
foreach (var group in distinctGroups)
{
var groupFilter = Builders<HostSysGroupEntity>.Filter.And(new List<FilterDefinition<HostSysGroupEntity>>
{
Builders<HostSysGroupEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSysGroupEntity>.Filter.Eq(x => x.Domain, group?.Domain),
Builders<HostSysGroupEntity>.Filter.Eq(x => x.Name, group?.Name)
});
var groupUpdate = Builders<HostSysGroupEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Domain, group?.Domain)
.SetOnInsert(p => p.Name, group?.Name)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Sid, group?.Sid)
.Set(p => p.Description, group?.Description)
.Set(p => p.LocalAccount, group?.LocalAccount);
groupBulk.Add(new UpdateOneModel<HostSysGroupEntity>(groupFilter, groupUpdate)
{
IsUpsert = true
});
}
groupBulk.Add(new DeleteManyModel<HostSysGroupEntity>(Builders<HostSysGroupEntity>.Filter.And(new List<FilterDefinition<HostSysGroupEntity>>
{
Builders<HostSysGroupEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSysGroupEntity>.Filter.Ne(x => x.Batch, batch)
})));
var groupResult = await _database.HostSystemGroup().BulkWriteAsync(groupBulk, cancellationToken: cancellationToken);
}
// relations
if (users is not null && users.Count != 0)
{
var relationBulk = new List<WriteModel<HostSysUserSysGroupEntity>>();
foreach (var user in users)
{
var userId = await _database.HostSystemUser()
.Find(p => p.Host == hostEntity.Id && p.Domain == user.Domain && p.Name == user.Name)
.Project(p => p.Id)
.FirstOrDefaultAsync(cancellationToken: default);
if (user.Groups is not null && user.Groups.Count != 0)
{
foreach (var group in user.Groups)
{
var groupId = await _database.HostSystemGroup()
.Find(p => p.Host == hostEntity.Id && p.Domain == group.Domain && p.Name == group.Name)
.Project(p => p.Id)
.FirstOrDefaultAsync(cancellationToken: default);
var relationFilter = Builders<HostSysUserSysGroupEntity>.Filter.And(new List<FilterDefinition<HostSysUserSysGroupEntity>>
{
Builders<HostSysUserSysGroupEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSysUserSysGroupEntity>.Filter.Eq(x => x.User, userId),
Builders<HostSysUserSysGroupEntity>.Filter.Eq(x => x.Group, groupId)
});
var relationUpdate = Builders<HostSysUserSysGroupEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.User, userId)
.SetOnInsert(p => p.Group, groupId)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch);
relationBulk.Add(new UpdateOneModel<HostSysUserSysGroupEntity>(relationFilter, relationUpdate)
{
IsUpsert = true
});
}
}
}
relationBulk.Add(new DeleteManyModel<HostSysUserSysGroupEntity>(Builders<HostSysUserSysGroupEntity>.Filter.And(new List<FilterDefinition<HostSysUserSysGroupEntity>>
{
Builders<HostSysUserSysGroupEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostSysUserSysGroupEntity>.Filter.Ne(x => x.Batch, batch)
})));
var relationResult = await _database.HostSystemUserSystemGroup().BulkWriteAsync(relationBulk, cancellationToken: cancellationToken);
}
}
}

View file

@ -0,0 +1,73 @@
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 VideocardHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<Videocard> videocards:
await OnVideocardsAsync(sender, videocards, cancellationToken);
break;
}
}
private async ValueTask OnVideocardsAsync(AgentSession session, List<Videocard>? videocards, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.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<WriteModel<HostVideocardEntity>>();
if (videocards is not null && videocards.Count != 0)
{
foreach (var videocard in videocards)
{
var filterDefinition = Builders<HostVideocardEntity>.Filter.And(new List<FilterDefinition<HostVideocardEntity>>
{
Builders<HostVideocardEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostVideocardEntity>.Filter.Eq(x => x.Name, videocard.Model)
});
var updateDefinition = Builders<HostVideocardEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.Name, videocard.Model)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Company, null)
.Set(p => p.Memory, videocard.Memory)
.Set(p => p.Driver, videocard.DriverVersion)
.Set(p => p.Date, videocard.DriverDate);
bulk.Add(new UpdateOneModel<HostVideocardEntity>(filterDefinition, updateDefinition)
{
IsUpsert = true
});
}
bulk.Add(new DeleteManyModel<HostVideocardEntity>(Builders<HostVideocardEntity>.Filter.And(new List<FilterDefinition<HostVideocardEntity>>
{
Builders<HostVideocardEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostVideocardEntity>.Filter.Ne(x => x.Batch, batch)
})));
var result = await _database.HostVideocard().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
}
}
}

View file

@ -0,0 +1,173 @@
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 VirtualMaschineHandler(IMongoDatabase database) : IMessageHandler<AgentSession>
{
private readonly IMongoDatabase _database = database;
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Collection<VirtualMaschine> virtualMaschines:
await OnVirtualMaschinesAsync(sender, virtualMaschines, cancellationToken);
break;
}
}
private async ValueTask OnVirtualMaschinesAsync(AgentSession session, List<VirtualMaschine>? virtualMaschines, CancellationToken cancellationToken)
{
var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null) return;
var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return;
var batch = ObjectId.GenerateNewId().ToString();
var date = DateTime.Now;
// virtual maschines
if (virtualMaschines is not null && virtualMaschines.Count != 0)
{
var virtualMaschineBulk = new List<WriteModel<HostHypervisorVirtualMaschineEntity>>();
foreach (var virtualMaschine in virtualMaschines)
{
var virtualMaschineFilter = Builders<HostHypervisorVirtualMaschineEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineEntity>>
{
Builders<HostHypervisorVirtualMaschineEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostHypervisorVirtualMaschineEntity>.Filter.Eq(x => x.UniqueId, virtualMaschine.Id.ToString())
});
var virtualMaschineUpdate = Builders<HostHypervisorVirtualMaschineEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.UniqueId, virtualMaschine.Id.ToString())
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.Name, virtualMaschine?.Name)
.Set(p => p.Notes, virtualMaschine?.Notes)
.Set(p => p.Enabled, virtualMaschine?.Enabled?.ToString())
.Set(p => p.EnabledDefault, virtualMaschine?.EnabledDefault?.ToString())
.Set(p => p.Health, virtualMaschine?.HealthState?.ToString())
.Set(p => p.Status, virtualMaschine?.Status)
.Set(p => p.OnTime, virtualMaschine?.OnTime)
.Set(p => p.ReplicationState, virtualMaschine?.ReplicationState?.ToString())
.Set(p => p.ReplicationHealth, virtualMaschine?.ReplicationHealth?.ToString())
.Set(p => p.ConfigurationVersion, virtualMaschine?.ConfigurationVersion)
.Set(p => p.IntegrationServicesVersionState, virtualMaschine?.IntegrationServicesVersionState?.ToString())
.Set(p => p.ProcessId, virtualMaschine?.ProcessId)
.Set(p => p.NumberOfProcessors, virtualMaschine?.NumberOfProcessors)
.Set(p => p.ProcessorLoad, virtualMaschine?.ProcessorLoad)
.Set(p => p.MemoryAvailable, virtualMaschine?.MemoryAvailable)
.Set(p => p.MemoryUsage, virtualMaschine?.MemoryUsage)
.Set(p => p.InstallDate, virtualMaschine?.InstallDate)
.Set(p => p.ConfigurationVersion, virtualMaschine?.ConfigurationVersion)
.Set(p => p.TimeOfLastStateChange, virtualMaschine?.TimeOfLastStateChange)
.Set(p => p.LastReplicationTime, virtualMaschine?.LastReplicationTime)
.Set(p => p.Os, virtualMaschine?.GuestOperatingSystem);
virtualMaschineBulk.Add(new UpdateOneModel<HostHypervisorVirtualMaschineEntity>(virtualMaschineFilter, virtualMaschineUpdate)
{
IsUpsert = true
});
}
virtualMaschineBulk.Add(new DeleteManyModel<HostHypervisorVirtualMaschineEntity>(Builders<HostHypervisorVirtualMaschineEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineEntity>>
{
Builders<HostHypervisorVirtualMaschineEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostHypervisorVirtualMaschineEntity>.Filter.Ne(x => x.Batch, batch)
})));
var virtualMaschineResult = await _database.HostHypervisorVirtualMaschine().BulkWriteAsync(virtualMaschineBulk, cancellationToken: cancellationToken);
}
// virtual maschine configurations
if (virtualMaschines is not null && virtualMaschines.Count != 0)
{
var configurationBulk = new List<WriteModel<HostHypervisorVirtualMaschineConfigEntity>>();
foreach (var virtualmaschine in virtualMaschines)
{
var virtualMaschineId = await _database.HostHypervisorVirtualMaschine()
.Find(p => p.Host == hostEntity.Id && p.UniqueId == virtualmaschine.Id.ToString())
.Project(p => p.Id)
.FirstOrDefaultAsync(cancellationToken: default);
if (virtualmaschine.Configurations is not null && virtualmaschine.Configurations.Count != 0)
{
foreach (var config in virtualmaschine.Configurations)
{
var configFilter = Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineConfigEntity>>
{
Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.VirtualMaschine, virtualMaschineId),
Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.UniqueId, config.Id)
});
// custom "notes" concat
string notes = string.Empty;
if (config?.Notes is not null)
foreach (var n in config.Notes) notes += n;
if (config?.Id is null) continue;
var configUpdate = Builders<HostHypervisorVirtualMaschineConfigEntity>.Update
.SetOnInsert(p => p.Insert, date)
.SetOnInsert(p => p.Host, hostEntity.Id)
.SetOnInsert(p => p.VirtualMaschine, virtualMaschineId)
.SetOnInsert(p => p.UniqueId, config.Id)
.Set(p => p.Update, date)
.Set(p => p.Batch, batch)
.Set(p => p.ParentId, config.ParentId)
.Set(p => p.Type, config.Type)
.Set(p => p.Name, config.Name)
.Set(p => p.Notes, notes)
.Set(p => p.CreationTime, config.CreationTime)
.Set(p => p.Generation, config.Generation)
.Set(p => p.Architecture, config.Architecture)
.Set(p => p.SecureBootEnabled, config.SecureBootEnabled)
.Set(p => p.IsAutomaticSnapshot, config.IsAutomaticSnapshot)
.Set(p => p.AutomaticStartupAction, config.AutomaticStartupAction?.ToString())
.Set(p => p.AutomaticShutdownAction, config.AutomaticShutdownAction?.ToString())
.Set(p => p.AutomaticRecoveryAction, config.AutomaticRecoveryAction?.ToString())
.Set(p => p.AutomaticSnapshotsEnabled, config.AutomaticSnapshotsEnabled)
.Set(p => p.BaseBoardSerialNumber, config.BaseBoardSerialNumber)
.Set(p => p.BIOSSerialNumber, config.BIOSSerialNumber)
.Set(p => p.BIOSGUID, config.BIOSGUID)
.Set(p => p.ConfigurationDataRoot, config.ConfigurationDataRoot)
.Set(p => p.ConfigurationFile, config.ConfigurationFile)
.Set(p => p.GuestStateDataRoot, config.GuestStateDataRoot)
.Set(p => p.GuestStateFile, config.GuestStateFile)
.Set(p => p.SnapshotDataRoot, config.SnapshotDataRoot)
.Set(p => p.SuspendDataRoot, config.SuspendDataRoot)
.Set(p => p.SwapFileDataRoot, config.SwapFileDataRoot);
configurationBulk.Add(new UpdateOneModel<HostHypervisorVirtualMaschineConfigEntity>(configFilter, configUpdate)
{
IsUpsert = true
});
}
}
}
configurationBulk.Add(new DeleteManyModel<HostHypervisorVirtualMaschineConfigEntity>(Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineConfigEntity>>
{
Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Ne(x => x.Batch, batch)
})));
var configurationResult = await _database.HostVirtualMaschineConfig().BulkWriteAsync(configurationBulk, cancellationToken: cancellationToken);
}
}
}

View file

@ -0,0 +1,104 @@
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Domain.Network.Remote.Messages;
using Microsoft.Extensions.Logging;
using Vaitr.Bus;
using Vaitr.Network;
namespace Insight.Server.Network.Remote.Handlers;
public class RemoteHandler(Bus bus, ISessionPool<RemoteSession, IMessage> remotePool, ILogger<RemoteHandler> logger) : IMessageHandler<RemoteSession>
{
private readonly Bus _bus = bus;
private readonly ISessionPool<RemoteSession, IMessage> _remotePool = remotePool;
private readonly ILogger<RemoteHandler> _logger = logger;
public async ValueTask HandleAsync<TMessage>(RemoteSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
if (message is RemoteSessionRequest sessionRequest)
{
await OnSessionRequest(sender, sessionRequest, cancellationToken);
}
else if (message is CastRequestResponse castRequestResponse)
{
await OnCastRequestResponse(sender, castRequestResponse, cancellationToken);
}
else if (message is CastMetric metricData)
{
await OnMetricData(sender, metricData, cancellationToken);
}
else if (message is CastScreen screenData)
{
await OnScreenData(sender, screenData, cancellationToken);
}
else if (message is CastCursor cursorData)
{
await OnCursorData(sender, cursorData, cancellationToken);
}
else if (message is CastClipboardReceived clipboardData)
{
await OnClipboardData(sender, clipboardData, cancellationToken);
}
else if (message is CastAudio audioData)
{
await OnAudioData(sender, audioData, cancellationToken);
}
}
private async Task OnSessionRequest(RemoteSession session, RemoteSessionRequest sessionRequest, CancellationToken cancellationToken)
{
_logger.LogInformation("Remote {session} => SessionRequest", session.Id);
session.Mode = sessionRequest.Mode;
await session.SendAsync(new RemoteSessionResponse
{
SessionId = session.Id
}, cancellationToken);
}
private async Task OnCastRequestResponse(RemoteSession session, CastRequestResponse castRequestResponse, CancellationToken cancellationToken)
{
await _bus.PublishAsync(castRequestResponse, cancellationToken);
}
private async Task OnMetricData(RemoteSession session, CastMetric streamMetrics, CancellationToken cancellationToken)
{
//_logger.LogInformation($"Remote {session.Id} => MetricData");
await _bus.PublishAsync(streamMetrics, cancellationToken);
}
private async Task OnScreenData(RemoteSession session, CastScreen screenData, CancellationToken cancellationToken)
{
//_logger.LogInformation($"Remote {session.Id} => ScreenData");
await _bus.PublishAsync(screenData, cancellationToken);
await session.SendAsync(new CastScreenReceived
{
Timestamp = screenData.Timestamp
}, cancellationToken);
}
private async Task OnCursorData(RemoteSession session, CastCursor cursorChanged, CancellationToken cancellationToken)
{
//_logger.LogInformation($"Remote {session.Id} => CursorData");
await _bus.PublishAsync(cursorChanged, cancellationToken);
}
private async Task OnClipboardData(RemoteSession session, CastClipboardReceived clipboardChanged, CancellationToken cancellationToken)
{
_logger.LogInformation("Remote {session} => ClipboardData", session.Id);
await _bus.PublishAsync(clipboardChanged, cancellationToken);
}
private async Task OnAudioData(RemoteSession session, CastAudio audioSample, CancellationToken cancellationToken)
{
_logger.LogInformation("Remote {session} => AudioData", session.Id);
await _bus.PublishAsync(audioSample, cancellationToken);
}
}

View file

@ -0,0 +1,83 @@
using Insight.Domain.Enums;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Server.Network.Web;
using Microsoft.Extensions.Logging;
using Vaitr.Network;
namespace Insight.Server.Network.Remote;
public class RemoteSession(
ISessionPool<WebSession, IMessage> webPool,
IEnumerable<IMessageHandler<RemoteSession>> handlers,
ISerializer<IMessage> serializer,
ILogger<RemoteSession> logger) : TcpSession<IMessage>(serializer, logger)
{
public string Id { get; } = GenerateRandomId();
public RemoteControlMode Mode { get; set; }
private readonly ISessionPool<WebSession, IMessage> _webPool = webPool;
private readonly IEnumerable<IMessageHandler<RemoteSession>> _handlers = handlers;
public async ValueTask ProxyAsync<TMessage>(TMessage message, CancellationToken cancellationToken)
where TMessage : IMessage
{
// check if web online
if (_webPool.FirstOrDefault().Value is not WebSession web) return;
// proxy-send request packet to web
await web.SendAsync(new Proxy<TMessage>
{
Message = message
}, cancellationToken);
}
protected override ValueTask OnConnectedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Remote ({ep?}) connected", RemoteEndPoint);
return default;
}
protected override ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Remote ({ep?}) disconnected", RemoteEndPoint);
return default;
}
protected override async ValueTask OnSentAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
{
await base.OnSentAsync(context, cancellationToken);
}
protected override async ValueTask OnReceivedAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
{
await base.OnReceivedAsync(context, cancellationToken);
foreach (var handler in _handlers)
{
try
{
await handler.HandleAsync(this, context.Packet, cancellationToken);
}
catch (Exception ex)
{
_logger.LogWarning("Remote ({ep?}) {ex}", RemoteEndPoint, ex.ToString());
}
}
}
protected override ValueTask OnHeartbeatAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Remote ({ep?}) Heartbeat", RemoteEndPoint);
return default;
}
private static string GenerateRandomId()
{
var random = new Random();
string? sessionId = string.Empty;
for (var i = 0; i < 3; i++) sessionId += random.Next(0, 999).ToString().PadLeft(3, '0');
return sessionId;
}
}

View file

@ -0,0 +1,86 @@
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Domain.Network.Agent.Messages;
using Insight.Infrastructure.Entities;
using Insight.Server.Network.Agent;
using Insight.Server.Network.Web;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using Vaitr.Network;
namespace Insight.Server.Network.Globals;
public class ProxyHandler(
ISessionPool<AgentSession, IMessage> agentPool,
ISessionPool<WebSession, IMessage> webPool,
IMongoDatabase database,
ILogger<ProxyHandler> logger) : IMessageHandler<AgentSession>, IMessageHandler<WebSession>
{
private readonly ISessionPool<AgentSession, IMessage> _agentPool = agentPool;
private readonly ISessionPool<WebSession, IMessage> _webPool = webPool;
private readonly IMongoDatabase _database = database;
private readonly ILogger<ProxyHandler> _logger = logger;
public async ValueTask HandleAsync<TMessage>(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Proxy<Request> proxyRequest:
await OnProxyRequestAsync(sender, proxyRequest, cancellationToken);
break;
}
}
public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
{
switch (message)
{
case Proxy<Response> proxyResponse:
await OnProxyResponseAsync(sender, proxyResponse, cancellationToken);
break;
}
}
private async ValueTask OnProxyRequestAsync(WebSession session, Proxy<Request> request, CancellationToken cancellationToken)
{
// get host
var hostEntity = await _database.Host()
.Find(Builders<HostEntity>
.Filter
.Eq(p => p.Id, request.ProxyId))
.FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null)
{
_logger.LogWarning("hostEntity is null");
return;
}
// get agent
var agentEntity = await _database.Agent()
.Find(Builders<AgentEntity>
.Filter
.Eq(p => p.Id, hostEntity.Agent))
.FirstOrDefaultAsync(cancellationToken);
if (agentEntity is null)
{
_logger.LogWarning("agentEntity is null");
return;
}
// check if agent online
if (_agentPool.FirstOrDefault(p => p.Value.Id == agentEntity.Id).Value is not AgentSession agent) return;
// proxy-send request packet to agent
await agent.SendAsync(request, cancellationToken);
}
private async ValueTask OnProxyResponseAsync(AgentSession session, Proxy<Response> response, CancellationToken cancellationToken)
{
// check if web online
if (_webPool.FirstOrDefault().Value is not WebSession web) return;
await web.SendAsync(response, cancellationToken);
}
}

View file

@ -0,0 +1,53 @@
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Microsoft.Extensions.Logging;
using Vaitr.Network;
namespace Insight.Server.Network.Web;
public class WebSession(IEnumerable<IMessageHandler<WebSession>> handlers, ISerializer<IMessage> serializer, ILogger<WebSession> logger) : TcpSession<IMessage>(serializer, logger)
{
public string? Id { get; set; }
private readonly IEnumerable<IMessageHandler<WebSession>> _handlers = handlers;
protected override ValueTask OnConnectedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Web ({ep?}) connected", RemoteEndPoint);
return default;
}
protected override ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Web ({ep?}) disconnected", RemoteEndPoint);
return default;
}
protected override async ValueTask OnSentAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
{
await base.OnSentAsync(context, cancellationToken);
}
protected override async ValueTask OnReceivedAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
{
await base.OnReceivedAsync(context, cancellationToken);
foreach (var handler in _handlers)
{
try
{
await handler.HandleAsync(this, context.Packet, cancellationToken);
}
catch (Exception ex)
{
_logger.LogWarning("Web ({ep?}) {ex}", RemoteEndPoint, ex.ToString());
}
}
}
protected override ValueTask OnHeartbeatAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Web ({ep?}) Heartbeat", RemoteEndPoint);
return default;
}
}

View file

@ -0,0 +1,171 @@
using Insight.Domain.Constants;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Infrastructure;
using Insight.Server.Network.Agent;
using Insight.Server.Network.Agent.Handlers;
using Insight.Server.Network.Globals;
using Insight.Server.Network.Web;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using System.Net;
using Vaitr.Network;
using Vaitr.Network.Hosting;
namespace Insight.Server;
internal class Program
{
public static async Task Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseWindowsService();
builder.Host.UseSystemd();
// Configuration
builder.Configuration.Defaults();
// Logging
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Trace);
builder.Logging.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = true;
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff ";
});
builder.Logging.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/server_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}");
// Modules
builder.AddDefaults();
builder.AddMetrics();
builder.AddAgentServices();
builder.AddWebServices();
// HTTP Pipeline
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
await app.RunAsync();
}
}
internal static class ServiceExtensions
{
internal static WebApplicationBuilder AddDefaults(this WebApplicationBuilder builder)
{
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
return builder;
}
internal static WebApplicationBuilder AddMetrics(this WebApplicationBuilder builder)
{
builder.Services.AddOpenTelemetry()
.WithMetrics(provider =>
{
provider.ConfigureResource(configure =>
{
configure.Clear();
configure.AddService(builder.Configuration.GetValue<string?>(Appsettings.Influx.Service) ?? throw new Exception($"{Appsettings.Influx.Service} value not set (appsettings)"));
})
.AddRuntimeInstrumentation()
.AddProcessInstrumentation()
.AddAspNetCoreInstrumentation()
//.AddMeter("test")
.AddInfluxDBMetricsExporter(configure =>
{
configure.Endpoint = builder.Configuration.GetValue<Uri?>(Appsettings.Influx.Endpoint) ?? throw new Exception($"{Appsettings.Influx.Endpoint} value not set (appsettings)");
configure.Token = builder.Configuration.GetValue<string?>(Appsettings.Influx.Token) ?? throw new Exception($"{Appsettings.Influx.Token} value not set (appsettings)");
configure.Org = builder.Configuration.GetValue<string?>(Appsettings.Influx.Organization) ?? throw new Exception($"{Appsettings.Influx.Organization} value not set (appsettings)");
configure.Bucket = builder.Configuration.GetValue<string?>(Appsettings.Influx.Bucket) ?? throw new Exception($"{Appsettings.Influx.Bucket} value not set (appsettings)");
configure.MetricExportIntervalMilliseconds = 1000;
});
}
);
//builder.Services.AddSingleton<BasicMetrics>();
return builder;
}
internal static WebApplicationBuilder AddAgentServices(this WebApplicationBuilder builder)
{
// SERVER
builder.Services.UseHostedServer<AgentSession, IMessage>(options =>
{
options.Address = IPAddress.Any;
options.Port = builder.Configuration.GetValue<int?>(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 = builder.Configuration.GetValue<string?>(Appsettings.Agent.Certificate) ?? throw new Exception($"{Appsettings.Agent.Certificate} value not set (appsettings)");
options.CertificatePassword = builder.Configuration.GetValue<string?>(Appsettings.Agent.CertificatePassword) ?? throw new Exception($"{Appsettings.Agent.CertificatePassword} value not set (appsettings)");
options.UseSerializer<AgentSession, IMessage, MemPackSerializer<IMessage>>();
});
// HANDLER
builder.Services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, DriveHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, Network.Agent.Handlers.EventHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, InterfaceHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, MainboardHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, MemoryHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, OperationSystemHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, PrinterHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, ProcessorHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, ServiceHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, SessionHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, SoftwareHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, StoragePoolHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, SystemInfoHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, TrapHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, UpdateHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, UserHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, VideocardHandler>();
builder.Services.AddSingleton<IMessageHandler<AgentSession>, VirtualMaschineHandler>();
return builder;
}
internal static WebApplicationBuilder AddWebServices(this WebApplicationBuilder builder)
{
// SERVER
builder.Services.UseHostedServer<WebSession, IMessage>(options =>
{
options.Address = IPAddress.Any;
options.Port = builder.Configuration.GetValue<int?>(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 = builder.Configuration.GetValue<string?>(Appsettings.Web.Certificate) ?? throw new Exception($"{Appsettings.Web.Certificate} value not set (appsettings)");
options.CertificatePassword = builder.Configuration.GetValue<string?>(Appsettings.Web.CertificatePassword) ?? throw new Exception($"{Appsettings.Web.CertificatePassword} value not set (appsettings)");
options.UseSerializer<WebSession, IMessage, MemPackSerializer<IMessage>>();
});
// HANDLER
builder.Services.AddSingleton<IMessageHandler<WebSession>, ProxyHandler>();
return builder;
}
}

View file

@ -0,0 +1,19 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Development": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Production": {
"commandName": "Project",
"applicationUrl": "http://localhost:5001"
}
}
}

View file

@ -0,0 +1,148 @@
using Insight.Domain.Enums;
using Insight.Infrastructure;
using Insight.Infrastructure.Entities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
namespace Insight.Server.Services;
internal class DispatchService(HttpClient httpClient, IMongoDatabase database, IConfiguration configuration, ILogger<DispatchService> logger) : BackgroundService
{
private readonly HttpClient _httpClient = httpClient;
private readonly IMongoDatabase _database = database;
private readonly IConfiguration _configuration = configuration;
private readonly ILogger<DispatchService> _logger = logger;
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogTrace("ExecuteAsync");
var enabled = _configuration.GetValue<bool?>(Appsettings.Dispatch.Webmatic) ?? throw new Exception($"{Appsettings.Dispatch.Webmatic} value not set (appsettings)");
if (enabled is false) return;
try
{
while (cancellationToken.IsCancellationRequested is false)
{
await DispatchAsync(cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
catch (OperationCanceledException) { }
catch (Exception) { }
}
private async ValueTask DispatchAsync(CancellationToken cancellationToken)
{
_logger.LogTrace($"DispatchAsync");
var pendings = await _database.HostLogMonitoring()
.Find(Builders<HostLogMonitoringEntity>
.Filter.Eq(p => p.Dispatch, DispatchEnum.Pending.ToString()))
.Limit(10)
.ToListAsync(cancellationToken);
if (pendings is null || pendings.Count == 0) return;
foreach (var entity in pendings)
{
try
{
var result = await SendAsync(entity, default);
await _database.HostLogMonitoring()
.UpdateOneAsync(Builders<HostLogMonitoringEntity>.Filter
.Eq(p => p.Id, entity.Id), Builders<HostLogMonitoringEntity>
.Update
.Set(p => p.Dispatch, result.ToString()), cancellationToken: default);
}
catch (Exception ex)
{
_logger.LogError("{exception}", ex.Message);
}
finally
{
// webmatic safety offset
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken: default);
}
}
}
private async ValueTask<DispatchEnum> SendAsync(HostLogMonitoringEntity monitoring, CancellationToken cancellationToken)
{
_logger.LogTrace("SendAsync ({monitoring})", monitoring);
var monitoringApi = Monitoring.LogUri;
var monitoringContent = new List<KeyValuePair<string, string>>();
var monitoringResult = new List<KeyValuePair<string, string>>();
// adjust by category
if (Enum.TryParse<CategoryEnum>(monitoring.Category, true, out var monitoringCategory) is false) return DispatchEnum.Failure;
if (monitoringCategory == CategoryEnum.Monitoring) monitoringApi = Monitoring.StatusUri;
// set category (if log)
if (monitoringApi == Monitoring.LogUri) monitoringContent.Add(new KeyValuePair<string, string>("category", monitoringCategory.ToString()));
// host resolve
var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Id, monitoring.Host?.ToString())).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return DispatchEnum.Failure;
// customer resolve
var customerEntity = await _database.Customer().Find(Builders<CustomerEntity>.Filter.Eq(p => p.Id, hostEntity.Customer)).FirstOrDefaultAsync(cancellationToken);
if (hostEntity is null) return DispatchEnum.Failure;
// set host name if no remote host set
if (string.IsNullOrEmpty(monitoring.Hostname)) monitoring.Hostname = hostEntity.Name;
// remove any domain from hostname
if (monitoring.Hostname is not null && monitoring.Hostname.Contains('.')) monitoring.Hostname = monitoring.Hostname.Split(".")[0];
// add customer tag to hostname
monitoring.Hostname += $".{customerEntity.Tag}";
// if task null, set hostname
if (string.IsNullOrEmpty(monitoring.Task)) monitoring.Task = monitoring.Hostname;
// insert hostname as computer-name (lowercase)
monitoringContent.Add(new KeyValuePair<string, string>("computer_name", monitoring.Hostname.ToLower()));
// insert converted status (api styled)
if (Enum.TryParse<StatusEnum>(monitoring.Status, true, out var monitoringStatus) is false) return DispatchEnum.Failure;
monitoringContent.Add(monitoringStatus switch
{
StatusEnum.Information => new KeyValuePair<string, string>("status", monitoringApi == Monitoring.StatusUri ? "erfolgreich" : "info"),
StatusEnum.Warning => new KeyValuePair<string, string>("status", monitoringApi == Monitoring.StatusUri ? "Interaktion" : "warning"),
StatusEnum.Error => new KeyValuePair<string, string>("status", monitoringApi == Monitoring.StatusUri ? "fehlgeschlagen" : "error"),
_ => throw new NotImplementedException(nameof(monitoringStatus))
});
// insert task, timestamp, message,
monitoringContent.Add(new KeyValuePair<string, string>("task", monitoring.Task));
if (monitoring.Timestamp is not null)
{
monitoringContent.Add(new KeyValuePair<string, string>("timestamp", monitoring.Timestamp.Value.ToLocalTime().ToString()));
}
if (string.IsNullOrWhiteSpace(monitoring.Message) is false)
{
monitoringContent.Add(new KeyValuePair<string, string>("message", monitoring.Message));
}
// send message
var result = await _httpClient.PostAsync(monitoringApi, new FormUrlEncodedContent(monitoringContent), default);
monitoringResult.Add(new KeyValuePair<string, string>("HttpStatusCode", result.StatusCode.ToString()));
monitoringResult.Add(new KeyValuePair<string, string>("HttpResponseMessage", await result.Content.ReadAsStringAsync(default)));
// if content != "OK"
if (result is null || result.IsSuccessStatusCode == false) return DispatchEnum.Failure;
// success
return DispatchEnum.Success;
}
}

View file

@ -0,0 +1,13 @@
namespace Insight.Server2
{
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}

View file

@ -0,0 +1,30 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Urls": "http://127.0.0.1:5001",
"influx.endpoint": "http://10.22.70.40:8086",
"influx.endpoint0": "http://192.168.1.10:8086",
"influx.token": "253pd3prqWfwhyLH3eirJkgCE5n6tzPVZcRwRrDcla9z1fIPVLB6tGqJ641e4yI5LY8lxb1VfJX_HrrIXMEv5A==",
"influx.token0": "x7ldV7EYb8ocnNAVbtpfpX0nS2HMyPxq3WyBPrVWBRT3C3tCjU8L8h5WPeESFnUqVvYubOM48GBydz5b3n69YA==",
"influx.org": "insight",
"influx.bucket": "insight",
"influx.service": "server",
"mongo.connection": "mongodb://db.insight.local:27017",
"agent.port": 3002,
"agent.certificate": "localhost.pfx",
"agent.certificate.password": "Webmatic12",
"web.port": 3001,
"web.certificate": "localhost.pfx",
"web.certificate.password": "Webmatic12",
"dispatch.webmatic": false
}

View file

@ -0,0 +1,30 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Urls": "http://127.0.0.1:5001",
"influx.endpoint": "http://10.22.70.40:8086",
"influx.endpoint0": "http://192.168.1.10:8086",
"influx.token": "253pd3prqWfwhyLH3eirJkgCE5n6tzPVZcRwRrDcla9z1fIPVLB6tGqJ641e4yI5LY8lxb1VfJX_HrrIXMEv5A==",
"influx.token0": "x7ldV7EYb8ocnNAVbtpfpX0nS2HMyPxq3WyBPrVWBRT3C3tCjU8L8h5WPeESFnUqVvYubOM48GBydz5b3n69YA==",
"influx.org": "insight",
"influx.bucket": "insight",
"influx.service": "server",
"mongo.connection": "mongodb://127.0.0.1:27017",
"agent.port": 3002,
"agent.certificate": "localhost.pfx",
"agent.certificate.password": "Webmatic12",
"web.port": 3001,
"web.certificate": "localhost.pfx",
"web.certificate.password": "Webmatic12",
"dispatch.webmatic": true
}

View file

@ -4,7 +4,7 @@ using Insight.Web.Services;
using Microsoft.AspNetCore.Components.Server.Circuits;
using MudBlazor.Services;
namespace Insight.Web.Hosting;
namespace Insight.Web.Extensions;
public static class ServiceExtensions
{

View file

@ -32,8 +32,9 @@
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.0" />
<PackageReference Include="MudBlazor" Version="6.11.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.1" />
<PackageReference Include="MudBlazor" Version="6.12.0" />
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
<!--Unix Serilog stuff-->
</ItemGroup>

View file

@ -23,7 +23,7 @@ public partial class Index
[Inject] private Bus Bus { get; init; } = default!;
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
private readonly string _id = ObjectId.GenerateNewId().ToString();
private string _title = Global.Name;
private readonly List<BreadcrumbItem> _breadcrumbs = [];

View file

@ -20,7 +20,7 @@ public partial class Details
private string _title = Global.Name;
private readonly List<BreadcrumbItem> _breadcrumbs = [];
private HostStoragePoolPhysicalDiskEntity? _hostPhysicalDisk { get; set; }
private HostStoragePoolPhysicalDiskEntity? _hostPhysicalDisk;
private async Task LoadDataAsync()
{

View file

@ -2,7 +2,7 @@ using Insight.Domain.Constants;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Infrastructure;
using Insight.Web.Hosting;
using Insight.Web.Extensions;
using Insight.Web.Middleware;
using Insight.Web.Network.Broker;
using Insight.Web.Network.Broker.Handlers;
@ -108,7 +108,6 @@ internal class Program
// BLAZOR HUBS (SIGNALR)
app.MapBlazorHub();
// FALLBACK
app.MapFallbackToPage("/_Host");
@ -126,8 +125,8 @@ internal static class ServiceExtensions
{
services.UseHostedClient<WebSession, IMessage>(options =>
{
options.Host = configuration.GetValue<string?>(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} value not set (appsettings)");
options.Port = configuration.GetValue<int?>(Appsettings.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} value not set (appsettings)");
options.Host = configuration.GetValue<string?>(Appsettings.Backend.Host) ?? throw new Exception($"{Appsettings.Backend.Host} value not set (appsettings)");
options.Port = configuration.GetValue<int?>(Appsettings.Backend.Port) ?? throw new Exception($"{Appsettings.Backend.Port} value not set (appsettings)");
options.Keepalive = 10000;
options.Timeout = 30000;
options.Encryption = Encryption.Tls12;
@ -146,15 +145,15 @@ internal static class ServiceExtensions
services.UseHostedServer<RemoteSession, IMessage>(options =>
{
options.Address = IPAddress.Any;
options.Port = configuration.GetValue<int?>(Appsettings.RemoteServerPort) ?? throw new Exception($"{Appsettings.RemoteServerPort} value not set (appsettings)");
options.Port = configuration.GetValue<int?>(Appsettings.Remote.Port) ?? throw new Exception($"{Appsettings.Remote.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<string?>(Appsettings.RemoteServerCertificate) ?? throw new Exception($"{Appsettings.RemoteServerCertificate} value not set (appsettings)");
options.CertificatePassword = configuration.GetValue<string?>(Appsettings.RemoteServerCertificatePassword) ?? throw new Exception($"{Appsettings.RemoteServerCertificatePassword} value not set (appsettings)");
options.Certificate = configuration.GetValue<string?>(Appsettings.Remote.Certificate) ?? throw new Exception($"{Appsettings.Remote.Certificate} value not set (appsettings)");
options.CertificatePassword = configuration.GetValue<string?>(Appsettings.Remote.CertificatePassword) ?? throw new Exception($"{Appsettings.Remote.CertificatePassword} value not set (appsettings)");
options.UseSerializer<RemoteSession, IMessage, MemPackSerializer<IMessage>>();
});

View file

@ -1,8 +1,10 @@
{
"AllowedHosts": "*",
"Urls": "http://127.0.0.1:5001",
"database": "mongodb://db.insight.local:27017",
"database0": "mongodb://insight.webmatic.de:27017",
"mongo.connection": "mongodb://db.insight.local:27017",
"mongo.connection0": "mongodb://insight.webmatic.de:27017",
"server.host": "insight.local",
"server.port": 3001,

View file

@ -1,7 +1,9 @@
{
"AllowedHosts": "*",
"Urls": "http://127.0.0.1:5001",
"database": "mongodb://127.0.0.1:27017",
"mongo.connection": "mongodb://127.0.0.1:27017",
"server.host": "127.0.0.1",
"server.port": 3001,