before server asp convertion
This commit is contained in:
parent
ce99053a10
commit
a4ed1a5956
82 changed files with 3802 additions and 444 deletions
|
|
@ -41,6 +41,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Remote.Windows", "s
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Infrastructure.Web", "src\Core\Insight.Infrastructure.Web\Insight.Infrastructure.Web.csproj", "{39B81A0D-A88C-44D3-9624-1A19C78A4310}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Infrastructure.Web", "src\Core\Insight.Infrastructure.Web\Insight.Infrastructure.Web.csproj", "{39B81A0D-A88C-44D3-9624-1A19C78A4310}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Server2", "src\Server\Insight.Server2\Insight.Server2.csproj", "{9F7E88B2-7415-410C-9C31-7720596B0607}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{39B81A0D-A88C-44D3-9624-1A19C78A4310}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -107,6 +113,7 @@ Global
|
||||||
{5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B} = {D4D7BF4A-B2E3-470A-A14C-FC658FF7461D}
|
{5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B} = {D4D7BF4A-B2E3-470A-A14C-FC658FF7461D}
|
||||||
{AF313B47-3079-407F-91D1-9989C1E1AF2A} = {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}
|
{39B81A0D-A88C-44D3-9624-1A19C78A4310} = {88B03853-2215-4E52-8986-0E76602E5F21}
|
||||||
|
{9F7E88B2-7415-410C-9C31-7720596B0607} = {038C3821-E554-496D-B585-A3BC193B7913}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {F376A326-7590-4E7E-AB9B-76CED8527AB0}
|
SolutionGuid = {F376A326-7590-4E7E-AB9B-76CED8527AB0}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,6 @@
|
||||||
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<!--WMI / System.Management-->
|
|
||||||
<RuntimeHostConfigurationOption Include="System.Runtime.Loader.UseRidGraph" Value="true" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
@ -27,10 +22,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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.Systemd" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" 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="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
||||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
||||||
|
|
|
||||||
6
src/Agent/Insight.Agent/Internal/Config.cs
Normal file
6
src/Agent/Insight.Agent/Internal/Config.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Insight.Agent;
|
||||||
|
|
||||||
|
internal sealed class Config
|
||||||
|
{
|
||||||
|
public Guid? Serial { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
namespace Insight.Agent.Models;
|
|
||||||
|
|
||||||
public class Config
|
|
||||||
{
|
|
||||||
public Guid? Serial { get; set; }
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
||||||
|
using Insight.Domain.Network.Agent.Messages;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Vaitr.Network;
|
using Vaitr.Network;
|
||||||
|
|
||||||
|
|
@ -31,6 +34,11 @@ public class AgentSession(IEnumerable<IMessageHandler<AgentSession>> handlers, I
|
||||||
{
|
{
|
||||||
await base.OnReceivedAsync(context, cancellationToken);
|
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)
|
foreach (var handler in _handlers)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -49,4 +57,31 @@ public class AgentSession(IEnumerable<IMessageHandler<AgentSession>> handlers, I
|
||||||
_logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
|
_logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
|
||||||
return default;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -51,7 +51,7 @@ public class OperationSystemHandler : IMessageHandler<AgentSession>
|
||||||
|
|
||||||
if (@object.TryGetValue<string>(properties, "osarchitecture", out var architecture))
|
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;
|
os.Architecture = Architecture.X64;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
using Insight.Agent.Extensions;
|
using Insight.Agent.Network;
|
||||||
using Insight.Agent.Network;
|
|
||||||
using Insight.Agent.Network.Handlers;
|
using Insight.Agent.Network.Handlers;
|
||||||
using Insight.Agent.Services;
|
using Insight.Agent.Services;
|
||||||
using Insight.Domain.Constants;
|
using Insight.Domain.Constants;
|
||||||
|
using Insight.Domain.Extensions;
|
||||||
using Insight.Domain.Interfaces;
|
using Insight.Domain.Interfaces;
|
||||||
using Insight.Domain.Network;
|
using Insight.Domain.Network;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
@ -67,7 +67,6 @@ internal class Program
|
||||||
options.UseSerializer<AgentSession, IMessage, MemPackSerializer<IMessage>>();
|
options.UseSerializer<AgentSession, IMessage, MemPackSerializer<IMessage>>();
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, AuthenticationHandler>();
|
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Insight.Agent.Extensions;
|
using Insight.Domain.Extensions;
|
||||||
using Insight.Domain.Network.Agent.Messages;
|
using Insight.Domain.Network.Agent.Messages;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Insight.Agent.Extensions;
|
using Insight.Domain.Extensions;
|
||||||
using Insight.Domain.Network.Agent.Messages;
|
using Insight.Domain.Network.Agent.Messages;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Insight.Api.Hosting;
|
namespace Insight.Api.Extensions;
|
||||||
|
|
||||||
public static class ServiceExtensions
|
public static class ServiceExtensions
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" 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.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="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
||||||
<!--Unix Serilog stuff-->
|
<!--Unix Serilog stuff-->
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Insight.Api.Hosting;
|
using Insight.Api.Extensions;
|
||||||
using Insight.Domain.Constants;
|
using Insight.Domain.Constants;
|
||||||
using Insight.Infrastructure;
|
using Insight.Infrastructure;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Urls": "http://127.0.0.1:5000",
|
"Urls": "http://127.0.0.1:5000",
|
||||||
"database": "mongodb://db.insight.local:27017",
|
"mongo.connection": "mongodb://db.insight.local:27017",
|
||||||
"jwt.key": "x5dcaE8fiBmHfgsNrwIEtSWzZkz6gpouzKOIgEiVjxJnW28V1aUnYXF19IcnF5x",
|
"jwt.key": "x5dcaE8fiBmHfgsNrwIEtSWzZkz6gpouzKOIgEiVjxJnW28V1aUnYXF19IcnF5x",
|
||||||
"jwt.exp": 3600,
|
"jwt.exp": 3600,
|
||||||
"jwt.audience": "http://127.0.0.1:5000",
|
"jwt.audience": "http://127.0.0.1:5000",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Urls": "http://127.0.0.1:5000",
|
"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.key": "x5dcaE8fiBmHfgsNrwIEtSWzZkz6gpouzKOIgEiVjxJnW28V1aUnYXF19IcnF5x",
|
||||||
"jwt.exp": 3600,
|
"jwt.exp": 3600,
|
||||||
"jwt.audience": "https://insight.webmatic.de/api",
|
"jwt.audience": "https://insight.webmatic.de/api",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Insight.Agent.Extensions;
|
namespace Insight.Domain.Extensions;
|
||||||
|
|
||||||
public static class ConfigurationExtensions
|
public static class ConfigurationExtensions
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Insight.Agent.Extensions;
|
namespace Insight.Domain.Extensions;
|
||||||
|
|
||||||
public static class Linux
|
public static class Linux
|
||||||
{
|
{
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
|
<PackageReference Include="Vaitr.Network" Version="2024.1.8" />
|
||||||
<PackageReference Include="Vaitr.Network" Version="2023.12.16" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
using AspNetCore.Identity.MongoDbCore.Extensions;
|
using Insight.Infrastructure.Entities;
|
||||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
|
||||||
using Insight.Infrastructure.Entities;
|
|
||||||
using Insight.Infrastructure.Services;
|
using Insight.Infrastructure.Services;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
@ -21,10 +19,10 @@ public static partial class ServiceExtensions
|
||||||
public static IServiceCollection AddTokenServices(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddTokenServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var options = new Models.TokenOptions(
|
var options = new Models.TokenOptions(
|
||||||
key: configuration.GetValue<string?>(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} 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.JwtExp) ?? throw new Exception($"{Appsettings.JwtExp} 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.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} 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.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} 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.AddSingleton(options);
|
||||||
services.AddTransient<TokenService>();
|
services.AddTransient<TokenService>();
|
||||||
|
|
@ -56,7 +54,7 @@ public static partial class ServiceExtensions
|
||||||
|
|
||||||
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
|
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 =>
|
services.AddIdentity<InsightUser, InsightRole>(options =>
|
||||||
{
|
{
|
||||||
|
|
@ -146,14 +144,14 @@ public static partial class ServiceExtensions
|
||||||
|
|
||||||
options.TokenValidationParameters.ValidateActor = false;
|
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.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.ValidateIssuer = true;
|
||||||
|
|
||||||
options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(
|
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;
|
options.TokenValidationParameters.ValidateIssuerSigningKey = true;
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,12 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.0.3" />
|
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Insight.Domain\Insight.Domain.csproj" />
|
||||||
<ProjectReference Include="..\Insight.Infrastructure\Insight.Infrastructure.csproj" />
|
<ProjectReference Include="..\Insight.Infrastructure\Insight.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,58 @@
|
||||||
namespace Insight.Infrastructure;
|
namespace Insight.Infrastructure;
|
||||||
|
|
||||||
public class Appsettings
|
public static class Appsettings
|
||||||
{
|
{
|
||||||
public const string Database = "database";
|
public static class Mongo
|
||||||
public const string JwtKey = "jwt.key";
|
{
|
||||||
public const string JwtAudience = "jwt.audience";
|
public const string ConnectionString = "mongo.connection";
|
||||||
public const string JwtIssuer = "jwt.issuer";
|
}
|
||||||
public const string JwtExp = "jwt.exp";
|
|
||||||
|
|
||||||
public const string ServerHost = "server.host";
|
public static class Influx
|
||||||
public const string ServerPort = "server.port";
|
{
|
||||||
|
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 static class Jwt
|
||||||
public const string RemoteServerCertificate = "remote.certificate";
|
{
|
||||||
public const string RemoteServerCertificatePassword = "remote.certificate.password";
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Insight.Server.Extensions;
|
namespace Insight.Infrastructure;
|
||||||
|
|
||||||
public static class ConfigurationExtensions
|
public static class ConfigurationExtensions
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +11,7 @@ public static partial class ServiceExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration, ILoggerFactory? loggerFactory = null)
|
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));
|
var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
|
||||||
settings.ConnectTimeout = TimeSpan.FromSeconds(3);
|
settings.ConnectTimeout = TimeSpan.FromSeconds(3);
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,10 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
|
||||||
<PackageReference Include="AspNetCore.Identity.MongoDbCore" Version="3.1.2" />
|
<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="MongoDB.Driver" Version="2.23.1" />
|
||||||
<PackageReference Include="Vaitr.Scheduler" Version="2023.12.15.1" />
|
<PackageReference Include="Vaitr.Scheduler" Version="2023.12.15.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Insight.Domain\Insight.Domain.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using Insight.Infrastructure.Entities;
|
using Insight.Infrastructure.Entities;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using MongoDB.Driver;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using Insight.Infrastructure.Entities;
|
using Insight.Infrastructure.Entities;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace Insight.Infrastructure.Services;
|
namespace Insight.Infrastructure.Services;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
||||||
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.6" />
|
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.6" />
|
||||||
|
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
@ -43,10 +43,17 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" 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.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.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-->
|
<!--Unix Serilog stuff-->
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Core\Insight.Domain\Insight.Domain.csproj" />
|
||||||
<ProjectReference Include="..\..\Core\Insight.Infrastructure\Insight.Infrastructure.csproj" />
|
<ProjectReference Include="..\..\Core\Insight.Infrastructure\Insight.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,95 @@
|
||||||
using Insight.Domain.Interfaces;
|
using Insight.Domain.Enums;
|
||||||
|
using Insight.Domain.Interfaces;
|
||||||
using Insight.Domain.Network;
|
using Insight.Domain.Network;
|
||||||
using Insight.Domain.Network.Agent.Messages;
|
using Insight.Domain.Network.Agent.Messages;
|
||||||
using Insight.Server.Network.Agent.Handlers;
|
using Insight.Infrastructure.Entities;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MongoDB.Driver;
|
||||||
using Vaitr.Network;
|
using Vaitr.Network;
|
||||||
|
|
||||||
namespace Insight.Server.Network.Agent;
|
namespace Insight.Server.Network.Agent;
|
||||||
|
|
||||||
public class AgentSession(
|
public class AgentSession(
|
||||||
AgentHandler agentHandler,
|
IMongoDatabase database,
|
||||||
IEnumerable<IMessageHandler<AgentSession>> handlers,
|
IEnumerable<IMessageHandler<AgentSession>> handlers,
|
||||||
ISerializer<IMessage> serializer,
|
ISerializer<IMessage> serializer,
|
||||||
ILogger<AgentSession> logger) : TcpSession<IMessage>(serializer, logger)
|
ILogger<AgentSession> logger) : TcpSession<IMessage>(serializer, logger)
|
||||||
{
|
{
|
||||||
public string? Id { get; set; }
|
public string? Id { get; set; }
|
||||||
|
|
||||||
private readonly AgentHandler _agentHandler = agentHandler;
|
private readonly IMongoDatabase _database = database;
|
||||||
private readonly IEnumerable<IMessageHandler<AgentSession>> _handlers = handlers;
|
private readonly IEnumerable<IMessageHandler<AgentSession>> _handlers = handlers;
|
||||||
|
|
||||||
protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken)
|
protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Agent ({ep?}) connected", RemoteEndPoint);
|
_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);
|
// if ack not received
|
||||||
await _agentHandler.StatisticUpdateAsync(this, default);
|
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)
|
protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Agent ({ep?}) disconnected", RemoteEndPoint);
|
_logger.LogInformation("Agent ({ep?}) disconnected", RemoteEndPoint);
|
||||||
|
|
||||||
await _agentHandler.StatisticUpdateAsync(this, default);
|
// insert log
|
||||||
await _agentHandler.DisconnectedAsync(this, default);
|
await WriteLogAsync(CategoryEnum.Network, StatusEnum.Information, $"Disconnected ({RemoteEndPoint})", default);
|
||||||
|
|
||||||
|
// update entity
|
||||||
|
await UpdateAsync(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask OnSentAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
|
protected override async ValueTask OnSentAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await base.OnSentAsync(context, 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)
|
protected override async ValueTask OnReceivedAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await base.OnReceivedAsync(context, 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)
|
foreach (var handler in _handlers)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -75,6 +107,100 @@ public class AgentSession(
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint);
|
_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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -183,10 +183,10 @@ public partial class TrapHandler(IMongoDatabase database) : IMessageHandler<Agen
|
||||||
{
|
{
|
||||||
var summary = false;
|
var summary = false;
|
||||||
|
|
||||||
if (Guid.TryParse(data[0].Value, out _))
|
if (Guid.TryParse(data[0].Value, out _))
|
||||||
summary = true;
|
summary = true;
|
||||||
|
|
||||||
if (data[1].Value?.ToLower() == "backup configuration job")
|
if (data[1].Value?.ToLower() == "backup configuration job")
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
status = (summary ? data[2].Value?.ToLower() : data[3].Value?.ToLower()) switch
|
status = (summary ? data[2].Value?.ToLower() : data[3].Value?.ToLower()) switch
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ public class VirtualMaschineHandler(IMongoDatabase database) : IMessageHandler<A
|
||||||
// custom "notes" concat
|
// custom "notes" concat
|
||||||
string notes = string.Empty;
|
string notes = string.Empty;
|
||||||
|
|
||||||
if (config?.Notes is not null)
|
if (config?.Notes is not null)
|
||||||
foreach (var n in config.Notes) notes += n;
|
foreach (var n in config.Notes) notes += n;
|
||||||
|
|
||||||
if (config?.Id is null) continue;
|
if (config?.Id is null) continue;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
using Insight.Domain.Interfaces;
|
using Insight.Domain.Interfaces;
|
||||||
using Insight.Domain.Network;
|
using Insight.Domain.Network;
|
||||||
using Insight.Infrastructure;
|
using Insight.Infrastructure;
|
||||||
using Insight.Server.Extensions;
|
|
||||||
using Insight.Server.Network.Agent;
|
using Insight.Server.Network.Agent;
|
||||||
using Insight.Server.Network.Agent.Handlers;
|
using Insight.Server.Network.Agent.Handlers;
|
||||||
using Insight.Server.Network.Globals;
|
using Insight.Server.Network.Globals;
|
||||||
|
|
@ -59,7 +58,6 @@ internal class Program
|
||||||
services.AddDatabase(host.Configuration);
|
services.AddDatabase(host.Configuration);
|
||||||
|
|
||||||
// BACKGROUND SERVICES
|
// BACKGROUND SERVICES
|
||||||
services.AddHostedService<JobService>();
|
|
||||||
services.AddHostedService<DispatchService>();
|
services.AddHostedService<DispatchService>();
|
||||||
|
|
||||||
// GLOBAL DEPENDENCIES
|
// GLOBAL DEPENDENCIES
|
||||||
|
|
@ -84,24 +82,22 @@ internal static class ServiceExtensions
|
||||||
services.UseHostedServer<AgentSession, IMessage>(options =>
|
services.UseHostedServer<AgentSession, IMessage>(options =>
|
||||||
{
|
{
|
||||||
options.Address = IPAddress.Any;
|
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.Keepalive = 10000;
|
||||||
options.Timeout = 30000;
|
options.Timeout = 30000;
|
||||||
options.Backlog = 128;
|
options.Backlog = 128;
|
||||||
|
|
||||||
options.Compression = true;
|
options.Compression = true;
|
||||||
options.Encryption = Encryption.Tls12;
|
options.Encryption = Encryption.Tls12;
|
||||||
options.Certificate = configuration.GetValue<string?>(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} 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.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} 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>>();
|
options.UseSerializer<AgentSession, IMessage, MemPackSerializer<IMessage>>();
|
||||||
});
|
});
|
||||||
|
|
||||||
// HANDLER
|
// HANDLER
|
||||||
services.AddSingleton<AgentHandler>();
|
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, AgentHandler>();
|
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, DriveHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, DriveHandler>();
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, Network.Agent.Handlers.EventHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, Network.Agent.Handlers.EventHandler>();
|
||||||
services.AddSingleton<IMessageHandler<AgentSession>, InterfaceHandler>();
|
services.AddSingleton<IMessageHandler<AgentSession>, InterfaceHandler>();
|
||||||
|
|
@ -130,15 +126,15 @@ internal static class ServiceExtensions
|
||||||
services.UseHostedServer<WebSession, IMessage>(options =>
|
services.UseHostedServer<WebSession, IMessage>(options =>
|
||||||
{
|
{
|
||||||
options.Address = IPAddress.Any;
|
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.Keepalive = 10000;
|
||||||
options.Timeout = 30000;
|
options.Timeout = 30000;
|
||||||
options.Backlog = 128;
|
options.Backlog = 128;
|
||||||
options.Encryption = Encryption.Tls12;
|
options.Encryption = Encryption.Tls12;
|
||||||
options.Compression = true;
|
options.Compression = true;
|
||||||
|
|
||||||
options.Certificate = configuration.GetValue<string?>(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} 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.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} 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>>();
|
options.UseSerializer<WebSession, IMessage, MemPackSerializer<IMessage>>();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ internal class DispatchService(HttpClient httpClient, IMongoDatabase database, I
|
||||||
{
|
{
|
||||||
_logger.LogTrace("ExecuteAsync");
|
_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;
|
if (enabled is false) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
{
|
{
|
||||||
"database": "mongodb://db.insight.local:27017",
|
"mongo.connection": "mongodb://db.insight.local:27017",
|
||||||
|
|
||||||
"remote.server.port": 3003,
|
"agent.port": 3002,
|
||||||
"remote.server.certificate": "localhost.pfx",
|
"agent.certificate": "localhost.pfx",
|
||||||
"remote.server.certificate.password": "Webmatic12",
|
"agent.certificate.password": "Webmatic12",
|
||||||
|
|
||||||
"agent.server.port": 3002,
|
"web.port": 3001,
|
||||||
"agent.server.certificate": "localhost.pfx",
|
"web.certificate": "localhost.pfx",
|
||||||
"agent.server.certificate.password": "Webmatic12",
|
"web.certificate.password": "Webmatic12",
|
||||||
|
|
||||||
"web.server.port": 3001,
|
|
||||||
"web.server.certificate": "localhost.pfx",
|
|
||||||
"web.server.certificate.password": "Webmatic12",
|
|
||||||
|
|
||||||
"dispatch.webmatic": false
|
"dispatch.webmatic": false
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"database": "mongodb://127.0.0.1:27017",
|
"mongo.connection": "mongodb://127.0.0.1:27017",
|
||||||
|
|
||||||
"agent.server.port": 3002,
|
"agent.port": 3002,
|
||||||
"agent.server.certificate": "localhost.pfx",
|
"agent.certificate": "localhost.pfx",
|
||||||
"agent.server.certificate.password": "Webmatic12",
|
"agent.certificate.password": "Webmatic12",
|
||||||
|
|
||||||
"web.server.port": 3001,
|
"web.port": 3001,
|
||||||
"web.server.certificate": "localhost.pfx",
|
"web.certificate": "localhost.pfx",
|
||||||
"web.server.certificate.password": "Webmatic12",
|
"web.certificate.password": "Webmatic12",
|
||||||
|
|
||||||
"dispatch.webmatic": false
|
"dispatch.webmatic": false
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/Server/Insight.Server2/Extensions/Async.cs
Normal file
52
src/Server/Insight.Server2/Extensions/Async.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/Server/Insight.Server2/Insight.Server2.csproj
Normal file
41
src/Server/Insight.Server2/Insight.Server2.csproj
Normal 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>
|
||||||
6
src/Server/Insight.Server2/Insight.Server2.http
Normal file
6
src/Server/Insight.Server2/Insight.Server2.http
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
@Insight.Server2_HostAddress = http://localhost:5197
|
||||||
|
|
||||||
|
GET {{Insight.Server2_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
26
src/Server/Insight.Server2/Models/MonitorMessage.cs
Normal file
26
src/Server/Insight.Server2/Models/MonitorMessage.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
206
src/Server/Insight.Server2/Network/Agent/AgentSession.cs
Normal file
206
src/Server/Insight.Server2/Network/Agent/AgentSession.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
264
src/Server/Insight.Server2/Network/Agent/Handlers/TrapHandler.cs
Normal file
264
src/Server/Insight.Server2/Network/Agent/Handlers/TrapHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
183
src/Server/Insight.Server2/Network/Agent/Handlers/UserHandler.cs
Normal file
183
src/Server/Insight.Server2/Network/Agent/Handlers/UserHandler.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/Server/Insight.Server2/Network/Remote/RemoteSession.cs
Normal file
83
src/Server/Insight.Server2/Network/Remote/RemoteSession.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/Server/Insight.Server2/Network/Shared/ProxyHandler.cs
Normal file
86
src/Server/Insight.Server2/Network/Shared/ProxyHandler.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/Server/Insight.Server2/Network/Web/WebSession.cs
Normal file
53
src/Server/Insight.Server2/Network/Web/WebSession.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
171
src/Server/Insight.Server2/Program.cs
Normal file
171
src/Server/Insight.Server2/Program.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/Server/Insight.Server2/Properties/launchSettings.json
Normal file
19
src/Server/Insight.Server2/Properties/launchSettings.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
src/Server/Insight.Server2/Services/DispatchService.cs
Normal file
148
src/Server/Insight.Server2/Services/DispatchService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Server/Insight.Server2/WeatherForecast.cs
Normal file
13
src/Server/Insight.Server2/WeatherForecast.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Server/Insight.Server2/appsettings.Development.json
Normal file
30
src/Server/Insight.Server2/appsettings.Development.json
Normal 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
|
||||||
|
}
|
||||||
30
src/Server/Insight.Server2/appsettings.json
Normal file
30
src/Server/Insight.Server2/appsettings.json
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ using Insight.Web.Services;
|
||||||
using Microsoft.AspNetCore.Components.Server.Circuits;
|
using Microsoft.AspNetCore.Components.Server.Circuits;
|
||||||
using MudBlazor.Services;
|
using MudBlazor.Services;
|
||||||
|
|
||||||
namespace Insight.Web.Hosting;
|
namespace Insight.Web.Extensions;
|
||||||
|
|
||||||
public static class ServiceExtensions
|
public static class ServiceExtensions
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,9 @@
|
||||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
||||||
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
|
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
|
||||||
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0" />
|
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.1" />
|
||||||
<PackageReference Include="MudBlazor" Version="6.11.1" />
|
<PackageReference Include="MudBlazor" Version="6.12.0" />
|
||||||
|
<PackageReference Include="Vaitr.Bus" Version="2023.12.15.1" />
|
||||||
<!--Unix Serilog stuff-->
|
<!--Unix Serilog stuff-->
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public partial class Index
|
||||||
[Inject] private Bus Bus { get; init; } = default!;
|
[Inject] private Bus Bus { get; init; } = default!;
|
||||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||||
|
|
||||||
|
|
||||||
private readonly string _id = ObjectId.GenerateNewId().ToString();
|
private readonly string _id = ObjectId.GenerateNewId().ToString();
|
||||||
private string _title = Global.Name;
|
private string _title = Global.Name;
|
||||||
private readonly List<BreadcrumbItem> _breadcrumbs = [];
|
private readonly List<BreadcrumbItem> _breadcrumbs = [];
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public partial class Details
|
||||||
|
|
||||||
private string _title = Global.Name;
|
private string _title = Global.Name;
|
||||||
private readonly List<BreadcrumbItem> _breadcrumbs = [];
|
private readonly List<BreadcrumbItem> _breadcrumbs = [];
|
||||||
private HostStoragePoolPhysicalDiskEntity? _hostPhysicalDisk { get; set; }
|
private HostStoragePoolPhysicalDiskEntity? _hostPhysicalDisk;
|
||||||
|
|
||||||
private async Task LoadDataAsync()
|
private async Task LoadDataAsync()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ using Insight.Domain.Constants;
|
||||||
using Insight.Domain.Interfaces;
|
using Insight.Domain.Interfaces;
|
||||||
using Insight.Domain.Network;
|
using Insight.Domain.Network;
|
||||||
using Insight.Infrastructure;
|
using Insight.Infrastructure;
|
||||||
using Insight.Web.Hosting;
|
using Insight.Web.Extensions;
|
||||||
using Insight.Web.Middleware;
|
using Insight.Web.Middleware;
|
||||||
using Insight.Web.Network.Broker;
|
using Insight.Web.Network.Broker;
|
||||||
using Insight.Web.Network.Broker.Handlers;
|
using Insight.Web.Network.Broker.Handlers;
|
||||||
|
|
@ -108,7 +108,6 @@ internal class Program
|
||||||
// BLAZOR HUBS (SIGNALR)
|
// BLAZOR HUBS (SIGNALR)
|
||||||
app.MapBlazorHub();
|
app.MapBlazorHub();
|
||||||
|
|
||||||
|
|
||||||
// FALLBACK
|
// FALLBACK
|
||||||
app.MapFallbackToPage("/_Host");
|
app.MapFallbackToPage("/_Host");
|
||||||
|
|
||||||
|
|
@ -126,8 +125,8 @@ internal static class ServiceExtensions
|
||||||
{
|
{
|
||||||
services.UseHostedClient<WebSession, IMessage>(options =>
|
services.UseHostedClient<WebSession, IMessage>(options =>
|
||||||
{
|
{
|
||||||
options.Host = configuration.GetValue<string?>(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} 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.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} 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.Keepalive = 10000;
|
||||||
options.Timeout = 30000;
|
options.Timeout = 30000;
|
||||||
options.Encryption = Encryption.Tls12;
|
options.Encryption = Encryption.Tls12;
|
||||||
|
|
@ -146,15 +145,15 @@ internal static class ServiceExtensions
|
||||||
services.UseHostedServer<RemoteSession, IMessage>(options =>
|
services.UseHostedServer<RemoteSession, IMessage>(options =>
|
||||||
{
|
{
|
||||||
options.Address = IPAddress.Any;
|
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.Keepalive = 10000;
|
||||||
options.Timeout = 30000;
|
options.Timeout = 30000;
|
||||||
options.Backlog = 128;
|
options.Backlog = 128;
|
||||||
|
|
||||||
options.Compression = true;
|
options.Compression = true;
|
||||||
options.Encryption = Encryption.Tls12;
|
options.Encryption = Encryption.Tls12;
|
||||||
options.Certificate = configuration.GetValue<string?>(Appsettings.RemoteServerCertificate) ?? throw new Exception($"{Appsettings.RemoteServerCertificate} 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.RemoteServerCertificatePassword) ?? throw new Exception($"{Appsettings.RemoteServerCertificatePassword} 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>>();
|
options.UseSerializer<RemoteSession, IMessage, MemPackSerializer<IMessage>>();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
{
|
{
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Urls": "http://127.0.0.1:5001",
|
"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.host": "insight.local",
|
||||||
"server.port": 3001,
|
"server.port": 3001,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
{
|
{
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Urls": "http://127.0.0.1:5001",
|
"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.host": "127.0.0.1",
|
||||||
"server.port": 3001,
|
"server.port": 3001,
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue