diff --git a/dotnetcore.csproj b/dotnetcore.csproj deleted file mode 100644 index 120e38c..0000000 --- a/dotnetcore.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net7.0 - - - diff --git a/insight.sln b/insight.sln index c6e0266..57b9b5c 100644 --- a/insight.sln +++ b/insight.sln @@ -31,11 +31,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Server", "src\Serve EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Updater", "src\Updater\Insight.Updater\Insight.Updater.csproj", "{4875D70F-A96B-4EBA-99BE-218886D29BEB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Web.Assets", "src\Web\Insight.Web.Assets\Insight.Web.Assets.csproj", "{EBB8A2A8-453B-4867-A8E2-072530391DD0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Agent", "src\Agent\Insight.Agent\Insight.Agent.csproj", "{2A391CA2-F96B-4DB7-80AA-0668A5141640}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Agent.Assets", "src\Agent\Insight.Agent.Assets\Insight.Agent.Assets.csproj", "{4C2B66EA-4EE1-47BF-BAEE-DDBAF6FCB324}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Remote", "Remote", "{D4D7BF4A-B2E3-470A-A14C-FC658FF7461D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Remote.Shared", "src\Remote\Insight.Remote.Shared\Insight.Remote.Shared.csproj", "{5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Remote.Windows", "src\Remote\Insight.Remote.Windows\Insight.Remote.Windows.csproj", "{AF313B47-3079-407F-91D1-9989C1E1AF2A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -71,18 +73,18 @@ Global {4875D70F-A96B-4EBA-99BE-218886D29BEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {4875D70F-A96B-4EBA-99BE-218886D29BEB}.Release|Any CPU.ActiveCfg = Release|Any CPU {4875D70F-A96B-4EBA-99BE-218886D29BEB}.Release|Any CPU.Build.0 = Release|Any CPU - {EBB8A2A8-453B-4867-A8E2-072530391DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBB8A2A8-453B-4867-A8E2-072530391DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBB8A2A8-453B-4867-A8E2-072530391DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBB8A2A8-453B-4867-A8E2-072530391DD0}.Release|Any CPU.Build.0 = Release|Any CPU {2A391CA2-F96B-4DB7-80AA-0668A5141640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A391CA2-F96B-4DB7-80AA-0668A5141640}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A391CA2-F96B-4DB7-80AA-0668A5141640}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A391CA2-F96B-4DB7-80AA-0668A5141640}.Release|Any CPU.Build.0 = Release|Any CPU - {4C2B66EA-4EE1-47BF-BAEE-DDBAF6FCB324}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C2B66EA-4EE1-47BF-BAEE-DDBAF6FCB324}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C2B66EA-4EE1-47BF-BAEE-DDBAF6FCB324}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C2B66EA-4EE1-47BF-BAEE-DDBAF6FCB324}.Release|Any CPU.Build.0 = Release|Any CPU + {5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B}.Release|Any CPU.Build.0 = Release|Any CPU + {AF313B47-3079-407F-91D1-9989C1E1AF2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF313B47-3079-407F-91D1-9989C1E1AF2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF313B47-3079-407F-91D1-9989C1E1AF2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF313B47-3079-407F-91D1-9989C1E1AF2A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -95,9 +97,9 @@ Global {EF3188D7-338D-43DA-BF6B-D26E5BDAC3A6} = {35BA5DCB-BECC-4F51-8DD0-694C555D205A} {1E75F7E9-E6AA-44E7-A2F3-DB4CA85E0118} = {038C3821-E554-496D-B585-A3BC193B7913} {4875D70F-A96B-4EBA-99BE-218886D29BEB} = {F2D241DB-7692-46DB-8A6A-958B365DAAF8} - {EBB8A2A8-453B-4867-A8E2-072530391DD0} = {3F000016-069D-477E-ACA3-F643880B57E8} {2A391CA2-F96B-4DB7-80AA-0668A5141640} = {140F73DD-29D3-4C44-809B-5B470880AA0D} - {4C2B66EA-4EE1-47BF-BAEE-DDBAF6FCB324} = {140F73DD-29D3-4C44-809B-5B470880AA0D} + {5C4697BD-BC97-484F-9DB1-CA87E2BEAA4B} = {D4D7BF4A-B2E3-470A-A14C-FC658FF7461D} + {AF313B47-3079-407F-91D1-9989C1E1AF2A} = {D4D7BF4A-B2E3-470A-A14C-FC658FF7461D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F376A326-7590-4E7E-AB9B-76CED8527AB0} diff --git a/src/Agent/Insight.Agent.Assets/Enums/CategoryEnum.cs b/src/Agent/Insight.Agent.Assets/Enums/CategoryEnum.cs deleted file mode 100644 index 5ba8cb9..0000000 --- a/src/Agent/Insight.Agent.Assets/Enums/CategoryEnum.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Insight.Agent.Enums -{ - public enum CategoryEnum - { - Network = 1, - System = 2, - Application = 3, - Security = 4, - Monitoring = 5, - Task = 6, - Printer = 7, - RDP = 8 - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Enums/DispatchEnum.cs b/src/Agent/Insight.Agent.Assets/Enums/DispatchEnum.cs deleted file mode 100644 index 2d824d4..0000000 --- a/src/Agent/Insight.Agent.Assets/Enums/DispatchEnum.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Insight.Agent.Enums -{ - public enum DispatchEnum - { - Pending = 1, - Failure = 2, - Success = 3, - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Enums/StatusEnum.cs b/src/Agent/Insight.Agent.Assets/Enums/StatusEnum.cs deleted file mode 100644 index 49b8e7d..0000000 --- a/src/Agent/Insight.Agent.Assets/Enums/StatusEnum.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Insight.Agent.Enums -{ - public enum StatusEnum - { - Information = 1, - Warning = 2, - Error = 3 - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Insight.Agent.Assets.csproj b/src/Agent/Insight.Agent.Assets/Insight.Agent.Assets.csproj deleted file mode 100644 index 26c306e..0000000 --- a/src/Agent/Insight.Agent.Assets/Insight.Agent.Assets.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - net7.0 - true - enable - Insight.Agent.Assets - Insight.Agent - Insight - 2025.2.24.0 - 2025.2.24.0 - none - true - - - - none - - - - none - - - - - - - - - - - - diff --git a/src/Agent/Insight.Agent.Assets/Interfaces/IAgentMessageHandler.cs b/src/Agent/Insight.Agent.Assets/Interfaces/IAgentMessageHandler.cs deleted file mode 100644 index a9e69a6..0000000 --- a/src/Agent/Insight.Agent.Assets/Interfaces/IAgentMessageHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Insight.Agent.Messages; - -namespace Insight.Agent.Interfaces -{ - public partial interface IAgentMessageHandler - { - ValueTask HandleAsync(TSender sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage; - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Application/Application.cs b/src/Agent/Insight.Agent.Assets/Messages/Application/Application.cs deleted file mode 100644 index ba3ad5c..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Application/Application.cs +++ /dev/null @@ -1,41 +0,0 @@ -using MemoryPack; -using System.Runtime.InteropServices; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(120, typeof(Application))] - [MemoryPackUnion(121, typeof(ApplicationList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Application : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Name { get; set; } - - [MemoryPackOrder(1)] - public string? Publisher { get; set; } - - [MemoryPackOrder(2)] - public string? Version { get; set; } - - [MemoryPackOrder(3)] - public string? Location { get; set; } - - [MemoryPackOrder(4)] - public string? Source { get; set; } - - [MemoryPackOrder(5)] - public string? Uninstall { get; set; } - - [MemoryPackOrder(6)] - public DateTime? InstallDate { get; set; } - - [MemoryPackOrder(7)] - public Architecture? Architecture { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class ApplicationList : List, IAgentMessage { } - -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Authentication/Authentication.cs b/src/Agent/Insight.Agent.Assets/Messages/Authentication/Authentication.cs deleted file mode 100644 index 84406f4..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Authentication/Authentication.cs +++ /dev/null @@ -1,34 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(1, typeof(Authentication))] - [MemoryPackUnion(2, typeof(AuthenticationRequest))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Authentication : IAgentMessage - { - [MemoryPackOrder(0)] - public PlatformType? Platform { get; set; } - - [MemoryPackOrder(1)] - public Guid Serial { get; set; } - - [MemoryPackOrder(2)] - public Version? Version { get; set; } - - [MemoryPackOrder(3)] - public string? Hostname { get; set; } - - public enum PlatformType - { - Unknown = 0, - Windows = 1, - Unix = 2 - } - } - - [MemoryPackable] - public partial class AuthenticationRequest : IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Commands/Commands.cs b/src/Agent/Insight.Agent.Assets/Messages/Commands/Commands.cs deleted file mode 100644 index b231232..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Commands/Commands.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(2000, typeof(GetInventory))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class GetInventory : IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Console/ConsoleQuery.cs b/src/Agent/Insight.Agent.Assets/Messages/Console/ConsoleQuery.cs deleted file mode 100644 index 369304c..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Console/ConsoleQuery.cs +++ /dev/null @@ -1,49 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(1000, typeof(ConsoleQuery))] - [MemoryPackUnion(1001, typeof(ConsoleQueryRequest))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class ConsoleQuery : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Id { get; set; } - - [MemoryPackOrder(1)] - public string? HostId { get; set; } - - [MemoryPackOrder(2)] - public string? Query { get; set; } - - [MemoryPackOrder(3)] - public string? Data { get; set; } - - [MemoryPackOrder(4)] - public string? Errors { get; set; } - - [MemoryPackOrder(5)] - public bool IsString { get; set; } - - [MemoryPackOrder(6)] - public bool IsArray { get; set; } - - [MemoryPackOrder(7)] - public bool HadErrors { get; set; } - } - - [MemoryPackable] - public partial class ConsoleQueryRequest : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Id { get; set; } - - [MemoryPackOrder(1)] - public string? HostId { get; set; } - - [MemoryPackOrder(2)] - public string? Query { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Drive/Drive.cs b/src/Agent/Insight.Agent.Assets/Messages/Drive/Drive.cs deleted file mode 100644 index 5a2f966..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Drive/Drive.cs +++ /dev/null @@ -1,103 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(50, typeof(Drive))] - [MemoryPackUnion(51, typeof(DriveList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Drive : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? Index { get; set; } - - [MemoryPackOrder(1)] - public string? Id { get; set; } - - [MemoryPackOrder(2)] - public string? Name { get; set; } - - [MemoryPackOrder(3)] - public string? Manufacturer { get; set; } - - [MemoryPackOrder(4)] - public string? SerialNumber { get; set; } - - [MemoryPackOrder(5)] - public ulong? Size { get; set; } - - [MemoryPackOrder(6)] - public string? Status { get; set; } - - [MemoryPackOrder(7)] - public string? InterfaceType { get; set; } - - [MemoryPackOrder(8)] - public string? FirmwareRevision { get; set; } - - [MemoryPackOrder(9)] - public string? PNPDeviceID { get; set; } - - [MemoryPackOrder(10)] - public List? Volumes { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class DriveList : List, IAgentMessage { } - - [MemoryPackable] - public partial class Volume : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? Index { get; set; } - - [MemoryPackOrder(1)] - public string? Id { get; set; } - - [MemoryPackOrder(2)] - public string? Name { get; set; } - - [MemoryPackOrder(3)] - public string? SerialNumber { get; set; } - - [MemoryPackOrder(4)] - public ulong? Size { get; set; } - - [MemoryPackOrder(5)] - public ulong? FreeSpace { get; set; } - - [MemoryPackOrder(6)] - public string? Type { get; set; } - - [MemoryPackOrder(7)] - public string? FileSystem { get; set; } - - [MemoryPackOrder(8)] - public bool? Compressed { get; set; } - - [MemoryPackOrder(9)] - public bool? Bootable { get; set; } - - [MemoryPackOrder(10)] - public bool? PrimaryPartition { get; set; } - - [MemoryPackOrder(11)] - public bool? BootPartition { get; set; } - - [MemoryPackOrder(12)] - public ulong? BlockSize { get; set; } - - [MemoryPackOrder(13)] - public ulong? NumberOfBlocks { get; set; } - - [MemoryPackOrder(14)] - public ulong? StartingOffset { get; set; } - - [MemoryPackOrder(15)] - public DriveType? DriveType { get; set; } - - [MemoryPackOrder(16)] - public string? ProviderName { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Event/Event.cs b/src/Agent/Insight.Agent.Assets/Messages/Event/Event.cs deleted file mode 100644 index edd3b61..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Event/Event.cs +++ /dev/null @@ -1,41 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(10, typeof(Event))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Event : IAgentMessage - { - [MemoryPackOrder(0)] - public DateTime? Timestamp { get; set; } - - [MemoryPackOrder(1)] - public StatusType? Status { get; set; } - - [MemoryPackOrder(2)] - public string? Source { get; set; } - - [MemoryPackOrder(3)] - public string? Category { get; set; } - - [MemoryPackOrder(4)] - public int? EventId { get; set; } - - [MemoryPackOrder(5)] - public string? Task { get; set; } - - [MemoryPackOrder(6)] - public string? Message { get; set; } - - public enum StatusType - { - Unknown = 0, - Information = 1, - Warning = 2, - Error = 3, - Critical = 4 - } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/IAgentMessage.cs b/src/Agent/Insight.Agent.Assets/Messages/IAgentMessage.cs deleted file mode 100644 index 3c3edf0..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/IAgentMessage.cs +++ /dev/null @@ -1,7 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackable] - public partial interface IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Interface/Interface.cs b/src/Agent/Insight.Agent.Assets/Messages/Interface/Interface.cs deleted file mode 100644 index 1c008c5..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Interface/Interface.cs +++ /dev/null @@ -1,195 +0,0 @@ -using MemoryPack; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(70, typeof(Interface))] - [MemoryPackUnion(71, typeof(InterfaceList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Interface : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? Index { get; set; } - - [MemoryPackOrder(1)] - public Guid? Guid { get; set; } - - [MemoryPackOrder(2)] - public string? Mac { get; set; } - - [MemoryPackOrder(3)] - public string? Name { get; set; } - - [MemoryPackOrder(4)] - public string? Description { get; set; } - - [MemoryPackOrder(5)] - public string? Manufacturer { get; set; } - - [MemoryPackOrder(6)] - public string? Suffix { get; set; } - - [MemoryPackOrder(7)] - public bool? Physical { get; set; } - - [MemoryPackOrder(8)] - public NetworkInterfaceType? Type { get; set; } - - [MemoryPackOrder(9)] - public OperationalStatus? Status { get; set; } - - [MemoryPackOrder(10)] - public long? Speed { get; set; } - - [MemoryPackOrder(11)] - public long? Ipv4Mtu { get; set; } - - [MemoryPackOrder(12)] - public bool? Ipv4Dhcp { get; set; } - - [MemoryPackOrder(13)] - public bool? Ipv4Forwarding { get; set; } - - [MemoryPackOrder(14)] - public long? Ipv6Mtu { get; set; } - - [MemoryPackOrder(15)] - public long? Sent { get; set; } - - [MemoryPackOrder(16)] - public long? Received { get; set; } - - [MemoryPackOrder(17)] - public long? IncomingPacketsDiscarded { get; set; } - - [MemoryPackOrder(18)] - public long? IncomingPacketsWithErrors { get; set; } - - [MemoryPackOrder(19)] - public long? IncomingUnknownProtocolPackets { get; set; } - - [MemoryPackOrder(20)] - public long? OutgoingPacketsDiscarded { get; set; } - - [MemoryPackOrder(21)] - public long? OutgoingPacketsWithErrors { get; set; } - - [MemoryPackOrder(22)] - public List? Addresses { get; set; } - - [MemoryPackOrder(23)] - public List? Gateways { get; set; } - - [MemoryPackOrder(24)] - public List? Dns { get; set; } - - [MemoryPackOrder(25)] - public List? Dhcp { get; set; } - - [MemoryPackOrder(26)] - public List? Routes { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class InterfaceList : List, IAgentMessage { } - - [MemoryPackable] - public partial class Unicast : IAgentMessage - { - [MemoryPackOrder(0)] - public IPAddress2? IpAddress { get; set; } - - [MemoryPackOrder(1)] - public IPAddress2? Ipv4Mask { get; set; } - - [MemoryPackOrder(2)] - public long? AddressPreferredLifetime { get; set; } - - [MemoryPackOrder(3)] - public long? AddressValidLifetime { get; set; } - - [MemoryPackOrder(4)] - public long? DhcpLeaseLifetime { get; set; } - - [MemoryPackOrder(5)] - public DuplicateAddressDetectionState? DuplicateAddressDetectionState { get; set; } - - [MemoryPackOrder(6)] - public int? PrefixLength { get; set; } - - [MemoryPackOrder(7)] - public PrefixOrigin? PrefixOrigin { get; set; } - - [MemoryPackOrder(8)] - public SuffixOrigin? SuffixOrigin { get; set; } - } - - [MemoryPackable] - public partial class Route : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? InterfaceIndex { get; set; } - - [MemoryPackOrder(1)] - public IPAddress2? Destination { get; set; } - - [MemoryPackOrder(2)] - public IPAddress2? Gateway { get; set; } - - [MemoryPackOrder(3)] - public string? Mask { get; set; } - - [MemoryPackOrder(4)] - public int? Metric { get; set; } - } - - [MemoryPackable] - public partial class IPAddress2 : IAgentMessage - { - [MemoryPackOrder(0)] - public AddressFamily? AddressFamily { get; set; } - - [MemoryPackOrder(1)] - public string? Address { get; set; } - - [MemoryPackOrder(2)] - public bool? IsIPv6Teredo { get; set; } - - [MemoryPackOrder(3)] - public bool? IsIPv6SiteLocal { get; set; } - - [MemoryPackOrder(4)] - public bool? IsIPv6Multicast { get; set; } - - [MemoryPackOrder(5)] - public bool? IsIPv6LinkLocal { get; set; } - - [MemoryPackOrder(6)] - public bool? IsIPv4MappedToIPv6 { get; set; } - - [MemoryPackOrder(7)] - public bool? IsIPv6UniqueLocal { get; set; } - - [MemoryPackConstructor] - public IPAddress2() - { - - } - - public IPAddress2(IPAddress address) - { - AddressFamily = address.AddressFamily; - Address = address.ToString(); - IsIPv4MappedToIPv6 = address.IsIPv4MappedToIPv6; - IsIPv6LinkLocal = address.IsIPv6LinkLocal; - IsIPv6Multicast = address.IsIPv6Multicast; - IsIPv6SiteLocal = address.IsIPv6SiteLocal; - IsIPv6Teredo = address.IsIPv6Teredo; - IsIPv6UniqueLocal = address.IsIPv6UniqueLocal; - } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Keepalive/Keepalive.cs b/src/Agent/Insight.Agent.Assets/Messages/Keepalive/Keepalive.cs deleted file mode 100644 index 63588dc..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Keepalive/Keepalive.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(0, typeof(Keepalive))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Keepalive : IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Mainboard/Mainboard.cs b/src/Agent/Insight.Agent.Assets/Messages/Mainboard/Mainboard.cs deleted file mode 100644 index a86bc5b..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Mainboard/Mainboard.cs +++ /dev/null @@ -1,29 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(20, typeof(Mainboard))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Mainboard : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Manufacturer { get; set; } - - [MemoryPackOrder(1)] - public string? Model { get; set; } - - [MemoryPackOrder(2)] - public string? Serial { get; set; } - - [MemoryPackOrder(3)] - public string? BiosManufacturer { get; set; } - - [MemoryPackOrder(4)] - public string? BiosVersion { get; set; } - - [MemoryPackOrder(5)] - public DateTime? BiosDate { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Memory/Memory.cs b/src/Agent/Insight.Agent.Assets/Messages/Memory/Memory.cs deleted file mode 100644 index bb8e444..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Memory/Memory.cs +++ /dev/null @@ -1,67 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(40, typeof(Memory))] - [MemoryPackUnion(41, typeof(MemoryList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Memory : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? Index { get; set; } - - [MemoryPackOrder(1)] - public string? Tag { get; set; } - - [MemoryPackOrder(2)] - public string? Location { get; set; } - - [MemoryPackOrder(3)] - public string? Manufacturer { get; set; } - - [MemoryPackOrder(4)] - public string? Model { get; set; } - - [MemoryPackOrder(5)] - public string? Serial { get; set; } - - [MemoryPackOrder(6)] - public ulong? Capacity { get; set; } - - [MemoryPackOrder(7)] - public uint? Speed { get; set; } - - [MemoryPackOrder(8)] - public uint? Voltage { get; set; } - - [MemoryPackOrder(9)] - public uint? ConfiguredSpeed { get; set; } - - [MemoryPackOrder(10)] - public uint? ConfiguredVoltage { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class MemoryList : List, IAgentMessage { } - - [MemoryPackable] - public partial class MemoryMetric : IAgentMessage - { - [MemoryPackOrder(0)] - public DateTime? Timestamp { get; set; } - - [MemoryPackOrder(1)] - public float? MemoryAvailable { get; set; } - - [MemoryPackOrder(2)] - public float? MemoryAvailablePercentage { get; set; } - - [MemoryPackOrder(3)] - public float? MemoryUsed { get; set; } - - [MemoryPackOrder(4)] - public float? MemoryUsagePercentage { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/OperationSystem/OperationSystem.cs b/src/Agent/Insight.Agent.Assets/Messages/OperationSystem/OperationSystem.cs deleted file mode 100644 index 0fe4ee5..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/OperationSystem/OperationSystem.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MemoryPack; -using System.Runtime.InteropServices; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(90, typeof(OperationSystem))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class OperationSystem : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Name { get; set; } - - [MemoryPackOrder(1)] - public string? Version { get; set; } - - [MemoryPackOrder(2)] - public string? SerialNumber { get; set; } - - [MemoryPackOrder(3)] - public Architecture? Architecture { get; set; } - - [MemoryPackOrder(4)] - public bool? Virtual { get; set; } - - [MemoryPackOrder(5)] - public DateTime? InstallDate { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Printer/Printer.cs b/src/Agent/Insight.Agent.Assets/Messages/Printer/Printer.cs deleted file mode 100644 index 0478fc0..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Printer/Printer.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(140, typeof(Printer))] - [MemoryPackUnion(141, typeof(PrinterList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Printer : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Name { get; set; } - - [MemoryPackOrder(1)] - public string? Driver { get; set; } - - [MemoryPackOrder(2)] - public string? Port { get; set; } - - [MemoryPackOrder(3)] - public string? Location { get; set; } - - [MemoryPackOrder(4)] - public string? Comment { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class PrinterList : List, IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Processor/Processor.cs b/src/Agent/Insight.Agent.Assets/Messages/Processor/Processor.cs deleted file mode 100644 index edd63af..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Processor/Processor.cs +++ /dev/null @@ -1,70 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(30, typeof(Processor))] - [MemoryPackUnion(31, typeof(ProcessorList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Processor : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? Index { get; set; } - - [MemoryPackOrder(1)] - public string? Name { get; set; } - - [MemoryPackOrder(2)] - public string? Manufacturer { get; set; } - - [MemoryPackOrder(3)] - public string? SerialNumber { get; set; } - - [MemoryPackOrder(4)] - public string? Socket { get; set; } - - [MemoryPackOrder(5)] - public string? Version { get; set; } - - [MemoryPackOrder(6)] - public string? DeviceId { get; set; } - - [MemoryPackOrder(7)] - public uint? Cores { get; set; } - - [MemoryPackOrder(8)] - public uint? LogicalCores { get; set; } - - [MemoryPackOrder(9)] - public uint? CurrentSpeed { get; set; } - - [MemoryPackOrder(10)] - public uint? MaxSpeed { get; set; } - - [MemoryPackOrder(11)] - public uint? L1Size { get; set; } - - [MemoryPackOrder(12)] - public uint? L2Size { get; set; } - - [MemoryPackOrder(13)] - public uint? L3Size { get; set; } - - [MemoryPackOrder(14)] - public bool? Virtualization { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class ProcessorList : List, IAgentMessage { } - - [MemoryPackable] - public partial class ProcessorMetric : IAgentMessage - { - [MemoryPackOrder(0)] - public DateTime? Timestamp { get; set; } - - [MemoryPackOrder(1)] - public float? ProcessorUsagePercentage { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Service/Service.cs b/src/Agent/Insight.Agent.Assets/Messages/Service/Service.cs deleted file mode 100644 index 61aa86d..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Service/Service.cs +++ /dev/null @@ -1,64 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(130, typeof(Service))] - [MemoryPackUnion(131, typeof(ServiceList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Service : IAgentMessage - { - [MemoryPackOrder(0)] - public uint? ProcessId { get; set; } - - [MemoryPackOrder(1)] - public string? Name { get; set; } - - [MemoryPackOrder(2)] - public string? Display { get; set; } - - [MemoryPackOrder(3)] - public string? Description { get; set; } - - [MemoryPackOrder(4)] - public string? PathName { get; set; } - - [MemoryPackOrder(5)] - public string? Account { get; set; } - - [MemoryPackOrder(6)] - public bool? Delay { get; set; } - - [MemoryPackOrder(7)] - public ServiceStatus? Status { get; set; } - - [MemoryPackOrder(8)] - public ServiceMode? StartMode { get; set; } - - public enum ServiceStatus - { - Unknown = -1, - Stopped = 1, - StartPending = 2, - StopPending = 3, - Running = 4, - ContinuePending = 5, - PausePending = 6, - Paused = 7 - } - - public enum ServiceMode - { - Unknown = -1, - Boot = 0, - System = 1, - Automatic = 2, - Manual = 3, - Disabled = 4 - } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class ServiceList : List, IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Session/Session.cs b/src/Agent/Insight.Agent.Assets/Messages/Session/Session.cs deleted file mode 100644 index 625c4ef..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Session/Session.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(110, typeof(Session))] - [MemoryPackUnion(111, typeof(SessionList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Session : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Sid { get; set; } - - [MemoryPackOrder(1)] - public string? User { get; set; } - - [MemoryPackOrder(2)] - public string? Type { get; set; } - - [MemoryPackOrder(3)] - public string? Status { get; set; } - - [MemoryPackOrder(4)] - public string? Remote { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class SessionList : List, IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Status/Status.cs b/src/Agent/Insight.Agent.Assets/Messages/Status/Status.cs deleted file mode 100644 index de31591..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Status/Status.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(3, typeof(Status))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Status : IAgentMessage - { - [MemoryPackOrder(0)] - public DateTime Timestamp { get; } = DateTime.Now; - - [MemoryPackOrder(1)] - public TimeSpan Uptime { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/StoragePool/StoragePool.cs b/src/Agent/Insight.Agent.Assets/Messages/StoragePool/StoragePool.cs deleted file mode 100644 index 6148979..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/StoragePool/StoragePool.cs +++ /dev/null @@ -1,302 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(160, typeof(StoragePool))] - [MemoryPackUnion(161, typeof(StoragePoolList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class StoragePool : IAgentMessage - { - [MemoryPackOrder(0)] - public string? UniqueId { get; set; } - - [MemoryPackOrder(1)] - public string? Name { get; set; } - - [MemoryPackOrder(2)] - public string? FriendlyName { get; set; } - - [MemoryPackOrder(3)] - public List? States { get; set; } - - [MemoryPackOrder(4)] - public HealthState? Health { get; set; } - - [MemoryPackOrder(5)] - public RetireMissingPhysicalDisksEnum? RetireMissingPhysicalDisks { get; set; } - - [MemoryPackOrder(6)] - public string? Resiliency { get; set; } - - [MemoryPackOrder(7)] - public bool? IsPrimordial { get; set; } - - [MemoryPackOrder(8)] - public bool? IsReadOnly { get; set; } - - [MemoryPackOrder(9)] - public bool? IsClustered { get; set; } - - [MemoryPackOrder(10)] - public ulong? Size { get; set; } - - [MemoryPackOrder(11)] - public ulong? AllocatedSize { get; set; } - - [MemoryPackOrder(12)] - public ulong? SectorSize { get; set; } - - [MemoryPackOrder(13)] - public List? PhysicalDisks { get; set; } - - [MemoryPackOrder(14)] - public List? VirtualDisks { get; set; } - - public enum OperationalState - { - Unknown = 0, - Other = 1, - OK = 2, - Degraded = 3, - Stressed = 4, - Predictive_Failure = 5, - Error = 6, - Non_Recoverable_Error = 7, - Starting = 8, - Stopping = 9, - Stopped = 10, - In_Service = 11, - No_Contact = 12, - Lost_Communication = 13, - Aborted = 14, - Dormant = 15, - Supporting_Entity_In_Error = 16, - Completed = 17, - Power_Mode = 18, - Relocating = 19 - } - - public enum HealthState - { - Healthy = 0, - Warning = 1, - Unhealthy = 2, - Unknown = 3 - } - - public enum RetireMissingPhysicalDisksEnum - { - Auto = 1, - Always = 2, - Never = 3 - } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class StoragePoolList : List, IAgentMessage { } - - [MemoryPackable] - public partial class PhysicalDisk : IAgentMessage - { - [MemoryPackOrder(0)] - public string? UniqueId { get; set; } - - [MemoryPackOrder(1)] - public string? DeviceId { get; set; } - - [MemoryPackOrder(2)] - public string? FriendlyName { get; set; } - - [MemoryPackOrder(3)] - public string? Manufacturer { get; set; } - - [MemoryPackOrder(4)] - public string? Model { get; set; } - - [MemoryPackOrder(5)] - public ushort? MediaType { get; set; } - - [MemoryPackOrder(6)] - public ushort? BusType { get; set; } - - [MemoryPackOrder(7)] - public List? States { get; set; } - - [MemoryPackOrder(8)] - public HealthState? Health { get; set; } - - [MemoryPackOrder(9)] - public List? SupportedUsages { get; set; } - - [MemoryPackOrder(10)] - public ushort? Usage { get; set; } - - [MemoryPackOrder(11)] - public string? PhysicalLocation { get; set; } - - [MemoryPackOrder(12)] - public string? SerialNumber { get; set; } - - [MemoryPackOrder(13)] - public string? FirmwareVersion { get; set; } - - [MemoryPackOrder(14)] - public ulong? Size { get; set; } - - [MemoryPackOrder(15)] - public ulong? AllocatedSize { get; set; } - - [MemoryPackOrder(16)] - public ulong? LogicalSectorSize { get; set; } - - [MemoryPackOrder(17)] - public ulong? PhysicalSectorSize { get; set; } - - [MemoryPackOrder(18)] - public ulong? VirtualDiskFootprint { get; set; } - - public enum OperationalState - { - Unknown = 0, - Other = 1, - OK = 2, - Degraded = 3, - Stressed = 4, - Predictive_Failure = 5, - Error = 6, - Non_Recoverable_Error = 7, - Starting = 8, - Stopping = 9, - Stopped = 10, - In_Service = 11, - No_Contact = 12, - Lost_Communication = 13, - Aborted = 14, - Dormant = 15, - Supporting_Entity_In_Error = 16, - Completed = 17, - Power_Mode = 18, - Relocating = 19 - } - - public enum HealthState - { - Healthy = 0, - Warning = 1, - Unhealthy = 2, - Unknown = 3 - } - - public enum SupportedUsagesEnum - { - Unknown = 0, - Auto_Select = 1, - Manual_Select = 2, - Hot_Spare = 3, - Retired = 4, - Journal = 5 - } - } - - [MemoryPackable] - public partial class VirtualDisk : IAgentMessage - { - [MemoryPackOrder(0)] - public string? UniqueId { get; set; } - - [MemoryPackOrder(1)] - public string? Name { get; set; } - - [MemoryPackOrder(2)] - public string? FriendlyName { get; set; } - - [MemoryPackOrder(3)] - public List? States { get; set; } - - [MemoryPackOrder(4)] - public HealthState? Health { get; set; } - - [MemoryPackOrder(5)] - public AccessTypeEnum? AccessType { get; set; } - - [MemoryPackOrder(6)] - public ProvisioningTypeEnum? ProvisioningType { get; set; } - - [MemoryPackOrder(7)] - public ushort? PhysicalDiskRedundancy { get; set; } - - [MemoryPackOrder(8)] - public string? ResiliencySettingName { get; set; } - - [MemoryPackOrder(9)] - public bool? Deduplication { get; set; } - - [MemoryPackOrder(10)] - public bool? IsSnapshot { get; set; } - - [MemoryPackOrder(11)] - public ulong? Size { get; set; } - - [MemoryPackOrder(12)] - public ulong? AllocatedSize { get; set; } - - [MemoryPackOrder(13)] - public ulong? FootprintOnPool { get; set; } - - [MemoryPackOrder(14)] - public ulong? ReadCacheSize { get; set; } - - [MemoryPackOrder(15)] - public ulong? WriteCacheSize { get; set; } - - public enum OperationalState - { - Unknown = 0, - Other = 1, - OK = 2, - Degraded = 3, - Stressed = 4, - Predictive_Failure = 5, - Error = 6, - Non_Recoverable_Error = 7, - Starting = 8, - Stopping = 9, - Stopped = 10, - In_Service = 11, - No_Contact = 12, - Lost_Communication = 13, - Aborted = 14, - Dormant = 15, - Supporting_Entity_In_Error = 16, - Completed = 17, - Power_Mode = 18, - Relocating = 19 - } - - public enum HealthState - { - Healthy = 0, - Warning = 1, - Unhealthy = 2, - Unknown = 3 - } - - public enum AccessTypeEnum - { - Unknown = 0, - Readable = 1, - Writeable = 2, - Read_Write = 3, - Write_Once = 4 - } - - public enum ProvisioningTypeEnum - { - Unknown = 0, - Thin = 1, - Fixed = 2 - } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/SystemInfo/SystemInfo.cs b/src/Agent/Insight.Agent.Assets/Messages/SystemInfo/SystemInfo.cs deleted file mode 100644 index 8ec0441..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/SystemInfo/SystemInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(80, typeof(SystemInfo))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class SystemInfo : IAgentMessage - { - [MemoryPackOrder(0)] - public DateTime? LastBootUpTime { get; set; } - - [MemoryPackOrder(1)] - public DateTime? LocalDateTime { get; set; } - - [MemoryPackOrder(2)] - public uint? Processes { get; set; } - - [MemoryPackOrder(3)] - public string? License { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Trap/Trap.cs b/src/Agent/Insight.Agent.Assets/Messages/Trap/Trap.cs deleted file mode 100644 index d0a66db..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Trap/Trap.cs +++ /dev/null @@ -1,29 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(11, typeof(Trap))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Trap : IAgentMessage - { - [MemoryPackOrder(0)] - public DateTime? Timestamp { get; set; } - - [MemoryPackOrder(1)] - public string? Endpoint { get; set; } - - [MemoryPackOrder(2)] - public string? Hostname { get; set; } - - [MemoryPackOrder(3)] - public string? Version { get; set; } - - [MemoryPackOrder(4)] - public string? Community { get; set; } - - [MemoryPackOrder(5)] - public List>? Data { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Update/Update.cs b/src/Agent/Insight.Agent.Assets/Messages/Update/Update.cs deleted file mode 100644 index ecc84c1..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Update/Update.cs +++ /dev/null @@ -1,83 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(100, typeof(Update))] - [MemoryPackUnion(101, typeof(UpdateList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Update : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Id { get; set; } - - [MemoryPackOrder(1)] - public DateTime? Date { get; set; } - - [MemoryPackOrder(2)] - public string? Name { get; set; } - - [MemoryPackOrder(3)] - public string? Description { get; set; } - - [MemoryPackOrder(4)] - public string? SupportUrl { get; set; } - - [MemoryPackOrder(5)] - public string? Hotfix { get; set; } - - // if installed - [MemoryPackOrder(6)] - public OsUpdateResultCodeEnum? Result { get; set; } - - // if pending - [MemoryPackOrder(7)] - public OsUpdateTypeEnum? Type { get; set; } - - [MemoryPackOrder(8)] - public decimal? Size { get; set; } - - [MemoryPackOrder(9)] - public bool? IsDownloaded { get; set; } - - [MemoryPackOrder(10)] - public bool? CanRequestUserInput { get; set; } - - [MemoryPackOrder(11)] - public OsUpdateRebootBehaviorEnum? RebootBehavior { get; set; } - - public enum OsUpdateRebootBehaviorEnum - { - NeverReboots = 1, - AlwaysRequiresReboot = 2, - CanRequestReboot = 3 - } - - public enum OsUpdateResultCodeEnum - { - NotStarted = 1, - InProgress = 2, - Succeeded = 3, - SucceededWithErrors = 4, - Failed = 5, - Aborted = 6 - } - - public enum OsUpdateTypeEnum - { - Software = 1, - Driver = 2 - } - } - - [MemoryPackable] - public partial class UpdateList : IAgentMessage - { - [MemoryPackOrder(0)] - public List? Installed { get; set; } - - [MemoryPackOrder(1)] - public List? Pending { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/User/User.cs b/src/Agent/Insight.Agent.Assets/Messages/User/User.cs deleted file mode 100644 index 968c4bb..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/User/User.cs +++ /dev/null @@ -1,73 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(150, typeof(User))] - [MemoryPackUnion(151, typeof(UserList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class User : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Sid { get; set; } - - [MemoryPackOrder(1)] - public string? Domain { get; set; } - - [MemoryPackOrder(2)] - public string? Name { get; set; } - - [MemoryPackOrder(3)] - public string? FullName { get; set; } - - [MemoryPackOrder(4)] - public string? Description { get; set; } - - [MemoryPackOrder(5)] - public string? Status { get; set; } - - [MemoryPackOrder(6)] - public bool? LocalAccount { get; set; } - - [MemoryPackOrder(7)] - public bool? Disabled { get; set; } - - [MemoryPackOrder(8)] - public bool? Lockout { get; set; } - - [MemoryPackOrder(9)] - public bool? PasswordChangeable { get; set; } - - [MemoryPackOrder(10)] - public bool? PasswordExpires { get; set; } - - [MemoryPackOrder(11)] - public bool? PasswordRequired { get; set; } - - [MemoryPackOrder(12)] - public List? Groups { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class UserList : List, IAgentMessage { } - - [MemoryPackable] - public partial class Group : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Sid { get; set; } - - [MemoryPackOrder(1)] - public string? Domain { get; set; } - - [MemoryPackOrder(2)] - public string? Name { get; set; } - - [MemoryPackOrder(3)] - public string? Description { get; set; } - - [MemoryPackOrder(4)] - public bool? LocalAccount { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/Videocard/Videocard.cs b/src/Agent/Insight.Agent.Assets/Messages/Videocard/Videocard.cs deleted file mode 100644 index 151d441..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/Videocard/Videocard.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(60, typeof(Videocard))] - [MemoryPackUnion(61, typeof(VideocardList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class Videocard : IAgentMessage - { - [MemoryPackOrder(0)] - public string? DeviceId { get; set; } - - [MemoryPackOrder(1)] - public string? Model { get; set; } - - [MemoryPackOrder(2)] - public ulong Memory { get; set; } - - [MemoryPackOrder(3)] - public DateTime DriverDate { get; set; } - - [MemoryPackOrder(4)] - public string? DriverVersion { get; set; } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class VideocardList : List, IAgentMessage { } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Messages/VirtualMaschine/VirtualMaschine.cs b/src/Agent/Insight.Agent.Assets/Messages/VirtualMaschine/VirtualMaschine.cs deleted file mode 100644 index a91dc44..0000000 --- a/src/Agent/Insight.Agent.Assets/Messages/VirtualMaschine/VirtualMaschine.cs +++ /dev/null @@ -1,265 +0,0 @@ -using MemoryPack; - -namespace Insight.Agent.Messages -{ - [MemoryPackUnion(170, typeof(VirtualMaschine))] - [MemoryPackUnion(171, typeof(VirtualMaschineList))] - public partial interface IAgentMessage { } - - [MemoryPackable] - public partial class VirtualMaschine : IAgentMessage - { - [MemoryPackOrder(0)] - public Guid? Id { get; set; } - - [MemoryPackOrder(1)] - public uint? ProcessId { get; set; } - - [MemoryPackOrder(2)] - public string? Caption { get; set; } - - [MemoryPackOrder(3)] - public string? Name { get; set; } - - [MemoryPackOrder(4)] - public string? Notes { get; set; } - - [MemoryPackOrder(5)] - public EnabledEnum? Enabled { get; set; } - - [MemoryPackOrder(6)] - public EnabledDefaultEnum? EnabledDefault { get; set; } - - [MemoryPackOrder(7)] - public HealthStatusEnum? HealthState { get; set; } - - [MemoryPackOrder(8)] - public string? Status { get; set; } - - [MemoryPackOrder(9)] - public ulong? OnTime { get; set; } - - [MemoryPackOrder(10)] - public uint? ReplicationMode { get; set; } - - [MemoryPackOrder(11)] - public ReplicationStateEnum? ReplicationState { get; set; } - - [MemoryPackOrder(12)] - public ReplicationHealthEnum? ReplicationHealth { get; set; } - - [MemoryPackOrder(13)] - public string? ConfigurationVersion { get; set; } - - [MemoryPackOrder(14)] - public IntegrationServicesVersionStateEnum? IntegrationServicesVersionState { get; set; } - - [MemoryPackOrder(15)] - public uint? NumberOfProcessors { get; set; } - - [MemoryPackOrder(16)] - public uint? ProcessorLoad { get; set; } - - [MemoryPackOrder(17)] - public int? MemoryAvailable { get; set; } - - [MemoryPackOrder(18)] - public ulong? MemoryUsage { get; set; } - - [MemoryPackOrder(19)] - public DateTime? InstallDate { get; set; } - - [MemoryPackOrder(20)] - public DateTime? TimeOfLastConfigurationChange { get; set; } - - [MemoryPackOrder(21)] - public DateTime? TimeOfLastStateChange { get; set; } - - [MemoryPackOrder(22)] - public DateTime? LastReplicationTime { get; set; } - - [MemoryPackOrder(23)] - public string? GuestOperatingSystem { get; set; } - - [MemoryPackOrder(24)] - public List? Configurations { get; set; } - - public enum EnabledEnum - { - Unbekannt = 0, - Andere = 1, - Aktiviert = 2, - Deaktiviert = 3, - Herunterfahren = 4, - Nicht_Verfügbar = 5, - Aktiviert_Offline = 6, - In_Test = 7, - Latent = 8, - Eingeschränkt = 9, - Wird_gestartet = 10 - } - - public enum EnabledDefaultEnum - { - Aktiviert = 2, - Deaktiviert = 3, - Aktiviert_Offline = 6 - } - - public enum HealthStatusEnum - { - OK = 5, - Hauptfehler = 20, - Kritischer_Fehler = 25 - } - - public enum ReplicationStateEnum - { - Deaktiviert = 0, - Bereit = 1, - Warten_auf_Erstreplikation = 2, - Replikat = 3, - Synchronisierte_Replication_abgeschlossen = 4, - Wiederhergestellt = 5, - Commit = 6, - Angehalten = 7, - Kritisch = 8, - Warten_auf_die_Neusynchronisierung = 9, - Resynchronisierung = 10, - Resynchronisierung_angehalten = 11, - Failover_in_Bearbeitung = 12, - Failback_in_Fortschritt = 13, - Failback_abgeschlossen = 14, - Datenträgerupdate_in_Bearbeitung = 15, - Datenträgeraktualisierung_kritisch = 16, - Unbekannt = 17, - Repurpose_Replikation_in_Bearbeitung = 18, - Vorbereitet_für_die_Synchronisierungsreplikation = 19, - Vorbereitet_für_die_Umgekehrte_Replikation_der_Gruppe = 20, - Failover_in_Fortschritt = 21 - } - - public enum ReplicationHealthEnum - { - OK = 1, - Warnung = 2, - Kritisch = 3 - } - - public enum IntegrationServicesVersionStateEnum - { - Unknown = 0, - UpToDate = 1, - Mismatch = 2 - } - } - - [MemoryPackable(GenerateType.Collection)] - public partial class VirtualMaschineList : List, IAgentMessage { } - - [MemoryPackable] - public partial class VirtualMaschineConfiguration : IAgentMessage - { - [MemoryPackOrder(0)] - public string? Id { get; set; } - - [MemoryPackOrder(1)] - public string? ParentId { get; set; } - - [MemoryPackOrder(2)] - public string? Type { get; set; } - - [MemoryPackOrder(3)] - public string? Name { get; set; } - - [MemoryPackOrder(4)] - public DateTime? CreationTime { get; set; } - - [MemoryPackOrder(5)] - public string? Generation { get; set; } - - [MemoryPackOrder(6)] - public string? Architecture { get; set; } - - [MemoryPackOrder(7)] - public AutomaticStartupActionEnum? AutomaticStartupAction { get; set; } - //public DateTime? AutomaticStartupActionDelay { get; set; } - - [MemoryPackOrder(8)] - public AutomaticShutdownActionEnum? AutomaticShutdownAction { get; set; } - - [MemoryPackOrder(9)] - public AutomaticRecoveryActionEnum? AutomaticRecoveryAction { get; set; } - - [MemoryPackOrder(10)] - public bool? AutomaticSnapshotsEnabled { get; set; } - - [MemoryPackOrder(11)] - public string? BaseBoardSerialNumber { get; set; } - - [MemoryPackOrder(12)] - public string? BIOSGUID { get; set; } - - [MemoryPackOrder(13)] - public string? BIOSSerialNumber { get; set; } - - [MemoryPackOrder(14)] - public ushort[]? BootOrder { get; set; } - - [MemoryPackOrder(15)] - public string? ConfigurationDataRoot { get; set; } - - [MemoryPackOrder(16)] - public string? ConfigurationFile { get; set; } - - [MemoryPackOrder(17)] - public string? GuestStateDataRoot { get; set; } - - [MemoryPackOrder(18)] - public string? GuestStateFile { get; set; } - - [MemoryPackOrder(19)] - public string? SnapshotDataRoot { get; set; } - - [MemoryPackOrder(20)] - public string? SuspendDataRoot { get; set; } - - [MemoryPackOrder(21)] - public string? SwapFileDataRoot { get; set; } - - [MemoryPackOrder(22)] - public bool? SecureBootEnabled { get; set; } - - [MemoryPackOrder(23)] - public bool? IsAutomaticSnapshot { get; set; } - - [MemoryPackOrder(24)] - public string[]? Notes { get; set; } - - [MemoryPackOrder(25)] - public List? Childs { get; set; } - - //public string[]? HostResource { get; set; } - - public enum AutomaticStartupActionEnum - { - Nothing = 2, - RestartIfLastStateActive = 3, - Alway = 4 - } - - public enum AutomaticShutdownActionEnum - { - Ausschalten = 2, - Speichern = 3, - Herunterfahren = 4 - } - - public enum AutomaticRecoveryActionEnum - { - Keine = 2, - Neustart = 3, - Rollback = 4 - } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent.Assets/Models/Config.cs b/src/Agent/Insight.Agent.Assets/Models/Config.cs deleted file mode 100644 index 4be9391..0000000 --- a/src/Agent/Insight.Agent.Assets/Models/Config.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Insight.Agent.Models -{ - public class Config - { - public Guid? Serial { get; set; } - } -} \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Constants/Appsettings.cs b/src/Agent/Insight.Agent/Constants/Appsettings.cs index 9a45fe7..074d6b8 100644 --- a/src/Agent/Insight.Agent/Constants/Appsettings.cs +++ b/src/Agent/Insight.Agent/Constants/Appsettings.cs @@ -1,10 +1,9 @@ -namespace Insight.Agent +namespace Insight.Agent; + +public static class Appsettings { - public static class Appsettings - { - public const string Api = "api"; - public const string ServerHost = "server.host"; - public const string ServerPort = "server.port"; - public const string TrapPort = "trap.port"; - } + public const string Api = "api"; + public const string ServerHost = "server.host"; + public const string ServerPort = "server.port"; + public const string TrapPort = "trap.port"; } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Constants/Deploy.cs b/src/Agent/Insight.Agent/Constants/Deploy.cs index 35aa19b..ded093c 100644 --- a/src/Agent/Insight.Agent/Constants/Deploy.cs +++ b/src/Agent/Insight.Agent/Constants/Deploy.cs @@ -1,21 +1,20 @@ -namespace Insight.Agent.Constants +namespace Insight.Agent.Constants; + +public static class Deploy { - public static class Deploy + public static class Updater { - public static class Updater - { - public const string Name = "Updater"; - public const string ServiceName = "insight_updater"; - public const string Description = "Insight Updater"; - } - - public static DirectoryInfo GetAppDirectory(string appName) - => new($"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}/Webmatic/Insight/{appName}"); - - public static FileInfo GetAppExecutable(string appName) - => new($"{GetAppDirectory(appName).FullName}/{appName.ToLower()}.exe"); - - public static Uri GetUpdateHref(Uri api, string appName) - => new($"{api.AbsoluteUri}/update/{appName.ToLower()}/windows"); + public const string Name = "Updater"; + public const string ServiceName = "insight_updater"; + public const string Description = "Insight Updater"; } + + public static DirectoryInfo GetAppDirectory(string appName) + => new($"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}/Webmatic/Insight/{appName}"); + + public static FileInfo GetAppExecutable(string appName) + => new($"{GetAppDirectory(appName).FullName}/{appName.ToLower()}.exe"); + + public static Uri GetUpdateHref(Uri api, string appName) + => new($"{api.AbsoluteUri}/update/{appName.ToLower()}/windows"); } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Extensions/Configuration.cs b/src/Agent/Insight.Agent/Extensions/Configuration.cs index c5df5da..8646d47 100644 --- a/src/Agent/Insight.Agent/Extensions/Configuration.cs +++ b/src/Agent/Insight.Agent/Extensions/Configuration.cs @@ -1,14 +1,13 @@ using Microsoft.Extensions.Configuration; -namespace Insight.Agent.Extensions +namespace Insight.Agent.Extensions; + +public static class ConfigurationExtensions { - public static class ConfigurationExtensions + public static IConfigurationBuilder Defaults(this IConfigurationBuilder configuration) { - public static IConfigurationBuilder Defaults(this IConfigurationBuilder configuration) - { - configuration.Sources.Clear(); - configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); - return configuration.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); - } + configuration.Sources.Clear(); + configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + return configuration.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Extensions/Linux.cs b/src/Agent/Insight.Agent/Extensions/Linux.cs index da4f85a..0574954 100644 --- a/src/Agent/Insight.Agent/Extensions/Linux.cs +++ b/src/Agent/Insight.Agent/Extensions/Linux.cs @@ -1,32 +1,31 @@ using System.Diagnostics; using System.Runtime.Versioning; -namespace Insight.Agent.Extensions +namespace Insight.Agent.Extensions; + +public static class Linux { - public static class Linux + [SupportedOSPlatform("linux")] + public static string Bash(this string cmd) { - [SupportedOSPlatform("linux")] - public static string Bash(this string cmd) + var escaped = cmd.Replace("\"", "\\\""); + + using var proc = new Process() { - var escaped = cmd.Replace("\"", "\\\""); - - using var proc = new Process() + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - FileName = "/bin/bash", - Arguments = $"-c \"{escaped}\"", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - } - }; + FileName = "/bin/bash", + Arguments = $"-c \"{escaped}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; - proc.Start(); - var result = proc.StandardOutput.ReadToEnd(); - proc.WaitForExit(); + proc.Start(); + var result = proc.StandardOutput.ReadToEnd(); + proc.WaitForExit(); - return result; - } + return result; } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Insight.Agent.csproj b/src/Agent/Insight.Agent/Insight.Agent.csproj index 59caaa1..52dabcf 100644 --- a/src/Agent/Insight.Agent/Insight.Agent.csproj +++ b/src/Agent/Insight.Agent/Insight.Agent.csproj @@ -3,16 +3,12 @@ Exe net7.0 - latest Insight.Agent Insight agent - 2025.2.24.0 - 2025.2.24.0 + 2023.12.14.0 enable enable - none - true @@ -27,14 +23,15 @@ + - + - + diff --git a/src/Agent/Insight.Agent/Internals/Extensions.cs b/src/Agent/Insight.Agent/Internals/Extensions.cs index 9699249..a9380f8 100644 --- a/src/Agent/Insight.Agent/Internals/Extensions.cs +++ b/src/Agent/Insight.Agent/Internals/Extensions.cs @@ -1,69 +1,68 @@ using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent +namespace Insight.Agent; + +public static class ManagmentExtensions { - public static class ManagmentExtensions + [SupportedOSPlatform("windows")] + public static HashSet GetPropertyHashes(this ManagementBaseObject @object) { - [SupportedOSPlatform("windows")] - public static HashSet GetPropertyHashes(this ManagementBaseObject @object) + var properties = new HashSet(); + + foreach (var property in @object.Properties) { - var properties = new HashSet(); - - foreach (var property in @object.Properties) - { - properties.Add(property.Name); - } - - return properties; + properties.Add(property.Name); } - [SupportedOSPlatform("windows")] - internal static bool TryGet(this ManagementObjectSearcher searcher, out ManagementObjectCollection collection) + return properties; + } + + [SupportedOSPlatform("windows")] + internal static bool TryGet(this ManagementObjectSearcher searcher, out ManagementObjectCollection collection) + { + collection = searcher.Get(); + + try { - collection = searcher.Get(); - - try - { - _ = collection.Count; - return true; - } - catch (ManagementException) - { - collection.Dispose(); - return false; - } - } - - [SupportedOSPlatform("windows")] - internal static T? GetValue(this ManagementObject @object, HashSet properties, string key) - { - if (@object is null || properties is null || key is null) return default; - if (properties.Contains(key, StringComparer.OrdinalIgnoreCase) is false) return default; - - if (@object[key] is not T obj) - { - return default; - } - - return obj; - } - - [SupportedOSPlatform("windows")] - internal static bool TryGetValue(this ManagementBaseObject @object, HashSet properties, string key, out T? value) - { - value = default; - - if (@object is null || properties is null || key is null) return default; - if (properties.Contains(key, StringComparer.OrdinalIgnoreCase) is false) return false; - - if (@object[key] is not T obj) - { - return false; - } - - value = obj; + _ = collection.Count; return true; } + catch (ManagementException) + { + collection.Dispose(); + return false; + } + } + + [SupportedOSPlatform("windows")] + internal static T? GetValue(this ManagementObject @object, HashSet properties, string key) + { + if (@object is null || properties is null || key is null) return default; + if (properties.Contains(key, StringComparer.OrdinalIgnoreCase) is false) return default; + + if (@object[key] is not T obj) + { + return default; + } + + return obj; + } + + [SupportedOSPlatform("windows")] + internal static bool TryGetValue(this ManagementBaseObject @object, HashSet properties, string key, out T? value) + { + value = default; + + if (@object is null || properties is null || key is null) return default; + if (properties.Contains(key, StringComparer.OrdinalIgnoreCase) is false) return false; + + if (@object[key] is not T obj) + { + return false; + } + + value = obj; + return true; } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Internals/Helpers.cs b/src/Agent/Insight.Agent/Internals/Helpers.cs index 93d7e62..e0a0a23 100644 --- a/src/Agent/Insight.Agent/Internals/Helpers.cs +++ b/src/Agent/Insight.Agent/Internals/Helpers.cs @@ -2,43 +2,42 @@ using System.ServiceProcess; using System.Text; -namespace Insight.Agent +namespace Insight.Agent; + +internal class Helpers { - internal class Helpers + internal static string? EscapeWql(string text) { - internal static string? EscapeWql(string text) - { - if (text == null) return null; + if (text == null) return null; - var sb = new StringBuilder(text.Length); - foreach (var c in text) + var sb = new StringBuilder(text.Length); + foreach (var c in text) + { + if (c == '\\' || c == '\'' || c == '"') { - if (c == '\\' || c == '\'' || c == '"') - { - sb.Append('\\'); - } - sb.Append(c); + sb.Append('\\'); } - return sb.ToString(); + sb.Append(c); + } + return sb.ToString(); + } + + [SupportedOSPlatform("windows")] + internal static bool ForceWinRmStart() + { + var winRm = ServiceController + .GetServices() + .First(x => x.ServiceName + .ToLower() + .Equals("winrm", StringComparison.Ordinal)); + + if (winRm.Status is not ServiceControllerStatus.Running) + { + winRm.Start(); + winRm.WaitForStatus(ServiceControllerStatus.Running); } - [SupportedOSPlatform("windows")] - internal static bool ForceWinRmStart() - { - var winRm = ServiceController - .GetServices() - .First(x => x.ServiceName - .ToLower() - .Equals("winrm", StringComparison.Ordinal)); - - if (winRm.Status is not ServiceControllerStatus.Running) - { - winRm.Start(); - winRm.WaitForStatus(ServiceControllerStatus.Running); - } - - if (winRm.Status != ServiceControllerStatus.Running) return false; - return true; - } + if (winRm.Status != ServiceControllerStatus.Running) return false; + return true; } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Models/Config.cs b/src/Agent/Insight.Agent/Models/Config.cs new file mode 100644 index 0000000..4fdd2ae --- /dev/null +++ b/src/Agent/Insight.Agent/Models/Config.cs @@ -0,0 +1,6 @@ +namespace Insight.Agent.Models; + +public class Config +{ + public Guid? Serial { get; set; } +} \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/AgentSession.cs b/src/Agent/Insight.Agent/Network/AgentSession.cs index 4b17ea0..b13df10 100644 --- a/src/Agent/Insight.Agent/Network/AgentSession.cs +++ b/src/Agent/Insight.Agent/Network/AgentSession.cs @@ -1,17 +1,37 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; using Microsoft.Extensions.Logging; -using System.Net.Sockets; using Vaitr.Network; namespace Insight.Agent.Network; -public class AgentSession(IEnumerable> handlers, Socket socket, Stream stream, TcpConnectionOptions options, MemPackSerializer serializer, ILogger logger) - : TcpSession(socket, stream, options, serializer, logger) +public class AgentSession : TcpSession { - private readonly IEnumerable> _handlers = handlers; + private readonly IEnumerable> _handlers; - protected override async ValueTask OnReceivedAsync(PacketContext context, CancellationToken cancellationToken) + public AgentSession(IEnumerable> handlers, ISerializer serializer, ILogger logger) : base(serializer, logger) + { + _handlers = handlers; + } + + protected override ValueTask OnConnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Agent ({ep?}) connected", RemoteEndPoint); + return default; + } + + protected override ValueTask OnDisconnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Agent ({ep?}) disconnected", RemoteEndPoint); + return default; + } + + protected override ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) + { + return base.OnSentAsync(context, cancellationToken); + } + + protected override async ValueTask OnReceivedAsync(IPacketContext context, CancellationToken cancellationToken) { await base.OnReceivedAsync(context, cancellationToken); @@ -19,7 +39,7 @@ public class AgentSession(IEnumerable> handle { try { - await handler.HandleAsync(this, context.Data, cancellationToken); + await handler.HandleAsync(this, context.Packet, cancellationToken); } catch (Exception ex) { @@ -27,4 +47,10 @@ public class AgentSession(IEnumerable> handle } } } + + protected override ValueTask OnHeartbeatAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Agent ({ep?}) Heartbeat", RemoteEndPoint); + return default; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs index 4247693..cc71964 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs @@ -1,38 +1,43 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Agent.Models; +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 +namespace Insight.Agent.Network.Handlers; + +public class AuthenticationHandler : IMessageHandler { - public class AuthenticationHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is AuthenticationRequest) - { - Config? config = null; - - try + case AuthenticationRequest: { - config = await Configurator.ReadAsync(Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false); + Config? config = null; + + try + { + config = await Configurator.ReadAsync(Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false); + } + catch (Exception) { } + + if (config is null) + { + config = new Config { Serial = Guid.NewGuid() }; + await Configurator.WriteAsync(config, Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false); + } + + await sender.SendAsync(new AuthenticationResponse + { + Serial = config.Serial ?? throw new InvalidDataException(nameof(config.Serial)), + Version = Configuration.Version, + Hostname = Configuration.Hostname + }, cancellationToken); + + break; } - 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 Authentication - { - Serial = config.Serial ?? throw new InvalidDataException(nameof(config.Serial)), - Version = Configuration.Version, - Hostname = Configuration.Hostname - }, cancellationToken); - } } } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/CustomHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/CustomHandler.cs new file mode 100644 index 0000000..8330a6c --- /dev/null +++ b/src/Agent/Insight.Agent/Network/Handlers/CustomHandler.cs @@ -0,0 +1,40 @@ +using Insight.Agent.Services; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; +using Microsoft.Extensions.Logging; + +namespace Insight.Agent.Network.Handlers; + +public class CustomHandler : IMessageHandler +{ + private readonly ScriptService _scriptService; + private readonly ILogger _logger; + + public CustomHandler(ScriptService scriptService, ILogger logger) + { + _scriptService = scriptService; + _logger = logger; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Request request: + await OnRequestAsync(sender, request, cancellationToken); + break; + } + } + + private async ValueTask OnRequestAsync(AgentSession sender, Request request, CancellationToken cancellationToken) + { + var result = await _scriptService.QueryAsync(request.RequestData); + + await sender.SendAsync(new Response(request) + { + ResponseData = result.HadErrors ? result.Errors : result.Data, + ResponseError = result.HadErrors + }, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/DriveHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/DriveHandler.cs index 13fafe8..d18393c 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/DriveHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/DriveHandler.cs @@ -1,178 +1,182 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class DriveHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class DriveHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new DriveList(); - result.AddRange(GetDrives()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetDrives() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select index, name, caption, model, manufacturer, serialNumber, size, status, interfacetype, firmwarerevision, deviceid, pnpdeviceid from win32_diskdrive") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_diskdrive"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var drives = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) + case InventoryRequest: { - var drive = new Drive(); + var result = new Collection(); + result.AddRange(GetDrives()); - var properties = @object.GetPropertyHashes(); - - drive.Index = @object.GetValue(properties, "index"); - drive.Id = @object.GetValue(properties, "deviceid")?.Trim(); - drive.Name = @object.GetValue(properties, "model")?.Trim(); - drive.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - drive.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); - drive.Size = @object.GetValue(properties, "size"); - drive.Status = @object.GetValue(properties, "status")?.Trim(); - drive.InterfaceType = @object.GetValue(properties, "interfacetype")?.Trim(); - drive.FirmwareRevision = @object.GetValue(properties, "firmwarerevision")?.Trim(); - drive.PNPDeviceID = @object.GetValue(properties, "pnpdeviceid")?.Trim(); - drive.Volumes = new List(); - - var diskpartition = @object.GetRelated("win32_diskpartition"); - using (diskpartition) - { - foreach (ManagementObject dp in diskpartition.Cast()) - { - var volume = new Volume(); - var dpProperties = dp.GetPropertyHashes(); - - volume.NumberOfBlocks = dp.GetValue(dpProperties, "numberofblocks"); - volume.BootPartition = dp.GetValue(dpProperties, "bootpartition"); - volume.PrimaryPartition = dp.GetValue(dpProperties, "primarypartition"); - volume.Size = dp.GetValue(dpProperties, "size"); - volume.Index = dp.GetValue(dpProperties, "index"); - volume.Type = dp.GetValue(dpProperties, "type")?.Trim(); - volume.Bootable = dp.GetValue(dpProperties, "bootable"); - volume.BlockSize = dp.GetValue(dpProperties, "blocksize"); - volume.StartingOffset = dp.GetValue(dpProperties, "startingoffset"); - - var logicaldisk = dp.GetRelated("win32_logicaldisk"); - using (logicaldisk) - { - foreach (ManagementObject ld in logicaldisk.Cast()) - { - var ldProperties = ld.GetPropertyHashes(); - - volume.Id = ld.GetValue(ldProperties, "deviceid")?.Trim(); - volume.Name = ld.GetValue(ldProperties, "volumename")?.Trim(); - volume.SerialNumber = ld.GetValue(ldProperties, "volumeserialnumber")?.Trim(); - volume.DriveType = (DriveType)ld.GetValue(ldProperties, "drivetype"); - volume.FileSystem = ld.GetValue(ldProperties, "filesystem")?.Trim(); - volume.Compressed = ld.GetValue(ldProperties, "compressed"); - volume.Size = ld.GetValue(ldProperties, "size"); - volume.FreeSpace = ld.GetValue(ldProperties, "freespace"); - volume.ProviderName = ld.GetValue(ldProperties, "providername")?.Trim(); - } - } - - drive.Volumes.Add(volume); - } - } - - drives.Add(drive); + await sender.SendAsync(result, cancellationToken); + break; } - } - - return drives; - } - - private static List GetVolumes() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select deviceid, volumename, volumeserialnumber, drivetype, filesystem, compressed, size, freeSpace, providername from win32_logicaldisk") - }; - - // per device query - // "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + driveDeviceId + "'} WHERE AssocClass=Win32_DiskDriveToDiskPartition" - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_logicaldisk"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var volumes = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) - { - var volume = new Volume(); - - var properties = @object.GetPropertyHashes(); - - //volume.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); - //volume.VolumeName = @object.GetValue(properties, "volumename")?.Trim(); - //volume.VolumeSerialNumber = @object.GetValue(properties, "volumeserialnumber")?.Trim(); - volume.DriveType = (DriveType)@object.GetValue(properties, "drivetype"); - volume.FileSystem = @object.GetValue(properties, "filesystem")?.Trim(); - volume.Compressed = @object.GetValue(properties, "compressed"); - volume.Size = @object.GetValue(properties, "size"); - volume.FreeSpace = @object.GetValue(properties, "freespace"); - volume.ProviderName = @object.GetValue(properties, "providername")?.Trim(); - - if (volume.Id is not null) - { - searcher.Query = new ObjectQuery("associators of {win32_logicaldisk.deviceid='" + volume.Id + "'} where assocclass=win32_logicaldisktopartition"); - - if (searcher.TryGet(out var collection2)) - { - using (collection2) - { - foreach (ManagementObject @object2 in collection2) - { - var properties2 = @object2.GetPropertyHashes(); - - volume.Index = @object2.GetValue(properties2, "index"); - //volume.DiskIndex = @object2.GetValue(properties2, "diskindex"); - volume.Type = @object2.GetValue(properties2, "type")?.Trim(); - volume.Bootable = @object2.GetValue(properties2, "bootable"); - volume.PrimaryPartition = @object2.GetValue(properties2, "primarypartition"); - volume.BootPartition = @object2.GetValue(properties2, "bootpartition"); - volume.BlockSize = @object2.GetValue(properties2, "blocksize"); - volume.NumberOfBlocks = @object2.GetValue(properties2, "numberofblocks"); - volume.StartingOffset = @object2.GetValue(properties2, "startingoffset"); - } - } - } - } - - volumes.Add(volume); - } - } - - return volumes; } } + + private static List GetDrives() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select index, name, caption, model, manufacturer, serialNumber, size, status, interfacetype, firmwarerevision, deviceid, pnpdeviceid from win32_diskdrive") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_diskdrive"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var drives = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var drive = new Drive(); + + var properties = @object.GetPropertyHashes(); + + drive.Index = @object.GetValue(properties, "index"); + drive.Id = @object.GetValue(properties, "deviceid")?.Trim(); + drive.Name = @object.GetValue(properties, "model")?.Trim(); + drive.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + drive.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); + drive.Size = @object.GetValue(properties, "size"); + drive.Status = @object.GetValue(properties, "status")?.Trim(); + drive.InterfaceType = @object.GetValue(properties, "interfacetype")?.Trim(); + drive.FirmwareRevision = @object.GetValue(properties, "firmwarerevision")?.Trim(); + drive.PNPDeviceID = @object.GetValue(properties, "pnpdeviceid")?.Trim(); + drive.Volumes = new List(); + + var diskpartition = @object.GetRelated("win32_diskpartition"); + using (diskpartition) + { + foreach (ManagementObject dp in diskpartition.Cast()) + { + var volume = new Volume(); + var dpProperties = dp.GetPropertyHashes(); + + volume.NumberOfBlocks = dp.GetValue(dpProperties, "numberofblocks"); + volume.BootPartition = dp.GetValue(dpProperties, "bootpartition"); + volume.PrimaryPartition = dp.GetValue(dpProperties, "primarypartition"); + volume.Size = dp.GetValue(dpProperties, "size"); + volume.Index = dp.GetValue(dpProperties, "index"); + volume.Type = dp.GetValue(dpProperties, "type")?.Trim(); + volume.Bootable = dp.GetValue(dpProperties, "bootable"); + volume.BlockSize = dp.GetValue(dpProperties, "blocksize"); + volume.StartingOffset = dp.GetValue(dpProperties, "startingoffset"); + + var logicaldisk = dp.GetRelated("win32_logicaldisk"); + using (logicaldisk) + { + foreach (ManagementObject ld in logicaldisk.Cast()) + { + var ldProperties = ld.GetPropertyHashes(); + + volume.Id = ld.GetValue(ldProperties, "deviceid")?.Trim(); + volume.Name = ld.GetValue(ldProperties, "volumename")?.Trim(); + volume.SerialNumber = ld.GetValue(ldProperties, "volumeserialnumber")?.Trim(); + volume.DriveType = (DriveType)ld.GetValue(ldProperties, "drivetype"); + volume.FileSystem = ld.GetValue(ldProperties, "filesystem")?.Trim(); + volume.Compressed = ld.GetValue(ldProperties, "compressed"); + volume.Size = ld.GetValue(ldProperties, "size"); + volume.FreeSpace = ld.GetValue(ldProperties, "freespace"); + volume.ProviderName = ld.GetValue(ldProperties, "providername")?.Trim(); + } + } + + drive.Volumes.Add(volume); + } + } + + drives.Add(drive); + } + } + + return drives; + } + + private static List GetVolumes() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select deviceid, volumename, volumeserialnumber, drivetype, filesystem, compressed, size, freeSpace, providername from win32_logicaldisk") + }; + + // per device query + // "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + driveDeviceId + "'} WHERE AssocClass=Win32_DiskDriveToDiskPartition" + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_logicaldisk"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var volumes = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var volume = new Volume(); + + var properties = @object.GetPropertyHashes(); + + //volume.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); + //volume.VolumeName = @object.GetValue(properties, "volumename")?.Trim(); + //volume.VolumeSerialNumber = @object.GetValue(properties, "volumeserialnumber")?.Trim(); + volume.DriveType = (DriveType)@object.GetValue(properties, "drivetype"); + volume.FileSystem = @object.GetValue(properties, "filesystem")?.Trim(); + volume.Compressed = @object.GetValue(properties, "compressed"); + volume.Size = @object.GetValue(properties, "size"); + volume.FreeSpace = @object.GetValue(properties, "freespace"); + volume.ProviderName = @object.GetValue(properties, "providername")?.Trim(); + + if (volume.Id is not null) + { + searcher.Query = new ObjectQuery("associators of {win32_logicaldisk.deviceid='" + volume.Id + "'} where assocclass=win32_logicaldisktopartition"); + + if (searcher.TryGet(out var collection2)) + { + using (collection2) + { + foreach (ManagementObject @object2 in collection2) + { + var properties2 = @object2.GetPropertyHashes(); + + volume.Index = @object2.GetValue(properties2, "index"); + //volume.DiskIndex = @object2.GetValue(properties2, "diskindex"); + volume.Type = @object2.GetValue(properties2, "type")?.Trim(); + volume.Bootable = @object2.GetValue(properties2, "bootable"); + volume.PrimaryPartition = @object2.GetValue(properties2, "primarypartition"); + volume.BootPartition = @object2.GetValue(properties2, "bootpartition"); + volume.BlockSize = @object2.GetValue(properties2, "blocksize"); + volume.NumberOfBlocks = @object2.GetValue(properties2, "numberofblocks"); + volume.StartingOffset = @object2.GetValue(properties2, "startingoffset"); + } + } + } + } + + volumes.Add(volume); + } + } + + return volumes; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs index 2d6e294..e5b5b1b 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs @@ -1,274 +1,277 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Net; using System.Net.NetworkInformation; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using Route = Insight.Agent.Messages.Route; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class InterfaceHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class InterfaceHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new InterfaceList(); - result.AddRange(GetInterfaces()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetInterfaces() - { - if (NetworkInterface.GetIsNetworkAvailable() is false) return null; - if (NetworkInterface.GetAllNetworkInterfaces().Any() is false) return null; - - var interfaces = new List(); - - foreach (var ni in NetworkInterface.GetAllNetworkInterfaces()) - { - var ipProperties = ni.GetIPProperties(); - var ipStatistics = ni.GetIPStatistics(); - - var @interface = new Interface + case InventoryRequest: { - Mac = ni.GetPhysicalAddress().ToString(), - Name = ni.Name, - Description = ni.Description, - Type = ni.NetworkInterfaceType, - Speed = ni.Speed, - Status = ni.OperationalStatus, - Suffix = ipProperties.DnsSuffix, - Sent = ipStatistics.BytesSent, - Received = ipStatistics.BytesReceived, - IncomingPacketsDiscarded = ipStatistics.IncomingPacketsDiscarded, - IncomingPacketsWithErrors = ipStatistics.IncomingPacketsWithErrors, - IncomingUnknownProtocolPackets = ipStatistics.IncomingUnknownProtocolPackets, - OutgoingPacketsDiscarded = ipStatistics.OutgoingPacketsDiscarded, - OutgoingPacketsWithErrors = ipStatistics.OutgoingPacketsWithErrors - }; + var result = new Collection(); + result.AddRange(GetInterfaces()); - try - { - var propertiesV4 = ipProperties.GetIPv4Properties(); - @interface.Index = uint.Parse(propertiesV4.Index.ToString()); - @interface.Ipv4Mtu = propertiesV4.Mtu; - @interface.Ipv4Dhcp = propertiesV4.IsDhcpEnabled; - @interface.Ipv4Forwarding = propertiesV4.IsForwardingEnabled; + await sender.SendAsync(result, cancellationToken); + break; } - catch (Exception) { } - - try - { - var propertiesV6 = ipProperties.GetIPv6Properties(); - @interface.Index = uint.Parse(propertiesV6.Index.ToString()); - @interface.Ipv6Mtu = propertiesV6.Mtu; - } - catch (Exception) { } - - @interface.Gateways = GetAddresses(ipProperties.GatewayAddresses); - @interface.Addresses = GetAddresses(ipProperties.UnicastAddresses); - @interface.Dns = GetAddresses(ipProperties.DnsAddresses); - @interface.Dhcp = GetAddresses(ipProperties.DhcpServerAddresses); - - if (@interface.Index.HasValue) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery($"select interfaceindex, guid, physicaladapter, manufacturer from win32_networkadapter where interfaceindex = {@interface.Index}") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery($"select * from win32_networkadapter where interfaceindex = {@interface.Index}"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var properties = @object.GetPropertyHashes(); - - @interface.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - @interface.Guid = @object.GetValue(properties, "guid"); - @interface.Physical = @object.GetValue(properties, "physicaladapter"); - - break; - } - } - - @interface.Routes = QueryInterfaceRoutes(@interface.Index.Value); - } - } - - interfaces.Add(@interface); - } - - return interfaces; - } - - private static List QueryInterfaceRoutes(uint interfaceIndex) - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\standardcimv2"), - Query = new ObjectQuery($"select addressFamily, state, interfaceindex, routemetric, nexthop, destinationprefix from msft_netroute where interfaceindex = {interfaceIndex}") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery($"select * from msft_netroute where interfaceindex = {interfaceIndex}"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var routes = new List(); - - using (collection) - { - foreach (var @object in collection) - { - var route = new Route - { - InterfaceIndex = interfaceIndex - }; - - var properties = @object.GetPropertyHashes(); - - if (@object.TryGetValue(properties, "routemetric", out var routemetric)) - { - if (int.TryParse(routemetric?.ToString(), out var metric)) route.Metric = metric; - } - - if (@object.TryGetValue(properties, "nexthop", out var nexthop)) - { - if (IPAddress.TryParse(nexthop?.ToString(), out var gateway)) route.Gateway = new IPAddress2(gateway); - } - - if (@object.TryGetValue(properties, "destinationprefix", out var destinationprefix)) - { - var split = destinationprefix?.ToString()?.Split('/'); - var cidrData = split?[1]; - - if (IPAddress.TryParse(split?[0], out var destination)) - route.Destination = new IPAddress2(destination); - - if (int.TryParse(cidrData, out var cidr)) - { - var mask = ConvertCidr(cidr); - route.Mask = mask; - } - } - - routes.Add(route); - } - } - - return routes; - } - - private static List GetAddresses(UnicastIPAddressInformationCollection unicastCollection) - { - var addresses = new List(); - - if (unicastCollection.Any() is false) return addresses; - - foreach (var unicast in unicastCollection) - { - addresses.Add(new Unicast - { - IpAddress = new IPAddress2(unicast.Address), - AddressPreferredLifetime = unicast.AddressPreferredLifetime, - AddressValidLifetime = unicast.AddressValidLifetime, - DuplicateAddressDetectionState = unicast.DuplicateAddressDetectionState, - Ipv4Mask = new IPAddress2(unicast.IPv4Mask), - PrefixLength = unicast.PrefixLength, - PrefixOrigin = unicast.PrefixOrigin, - SuffixOrigin = unicast.SuffixOrigin, - DhcpLeaseLifetime = unicast.DhcpLeaseLifetime, - }); - } - - return addresses; - } - - private static List GetAddresses(IPAddressCollection addressCollection) - { - var addresses = new List(); - - if (addressCollection.Any() is false) return addresses; - - foreach (var address in addressCollection) - { - addresses.Add(new IPAddress2(address)); - } - - return addresses; - } - - private static List GetAddresses(GatewayIPAddressInformationCollection addressCollection) - { - var addresses = new List(); - - if (addressCollection.Any() is false) return addresses; - - foreach (var address in addressCollection) - { - addresses.Add(new IPAddress2(address.Address)); - } - - return addresses; - } - - private static string? ConvertCidr(int cidr) - { - return cidr switch - { - 0 => "0.0.0.0", - 1 => "128.0.0.0", - 2 => "192.0.0.0", - 3 => "224.0.0.0", - 4 => "240.0.0.0", - 5 => "248.0.0.0", - 6 => "252.0.0.0", - 7 => "254.0.0.0", - 8 => "255.0.0.0", - 9 => "255.128.0.0", - 10 => "255.192.0.0", - 11 => "255.224.0.0", - 12 => "255.240.0.0", - 13 => "255.248.0.0", - 14 => "255.252.0.0", - 15 => "255.254.0.0", - 16 => "255.255.0.0", - 17 => "255.255.128.0", - 18 => "255.255.192.0", - 19 => "255.255.224.0", - 20 => "255.255.240.0", - 21 => "255.255.248.0", - 22 => "255.255.252.0", - 23 => "255.255.254.0", - 24 => "255.255.255.0", - 25 => "255.255.255.128", - 26 => "255.255.255.192", - 27 => "255.255.255.224", - 28 => "255.255.255.240", - 29 => "255.255.255.248", - 30 => "255.255.255.252", - 31 => "255.255.255.254", - 32 => "255.255.255.255", - _ => null, - }; } } + + private static List GetInterfaces() + { + if (NetworkInterface.GetIsNetworkAvailable() is false) return null; + if (NetworkInterface.GetAllNetworkInterfaces().Any() is false) return null; + + var interfaces = new List(); + + foreach (var ni in NetworkInterface.GetAllNetworkInterfaces()) + { + var ipProperties = ni.GetIPProperties(); + var ipStatistics = ni.GetIPStatistics(); + + var @interface = new Interface + { + Mac = ni.GetPhysicalAddress().ToString(), + Name = ni.Name, + Description = ni.Description, + Type = ni.NetworkInterfaceType, + Speed = ni.Speed, + Status = ni.OperationalStatus, + Suffix = ipProperties.DnsSuffix, + Sent = ipStatistics.BytesSent, + Received = ipStatistics.BytesReceived, + IncomingPacketsDiscarded = ipStatistics.IncomingPacketsDiscarded, + IncomingPacketsWithErrors = ipStatistics.IncomingPacketsWithErrors, + IncomingUnknownProtocolPackets = ipStatistics.IncomingUnknownProtocolPackets, + OutgoingPacketsDiscarded = ipStatistics.OutgoingPacketsDiscarded, + OutgoingPacketsWithErrors = ipStatistics.OutgoingPacketsWithErrors + }; + + try + { + var propertiesV4 = ipProperties.GetIPv4Properties(); + @interface.Index = uint.Parse(propertiesV4.Index.ToString()); + @interface.Ipv4Mtu = propertiesV4.Mtu; + @interface.Ipv4Dhcp = propertiesV4.IsDhcpEnabled; + @interface.Ipv4Forwarding = propertiesV4.IsForwardingEnabled; + } + catch (Exception) { } + + try + { + var propertiesV6 = ipProperties.GetIPv6Properties(); + @interface.Index = uint.Parse(propertiesV6.Index.ToString()); + @interface.Ipv6Mtu = propertiesV6.Mtu; + } + catch (Exception) { } + + @interface.Gateways = GetAddresses(ipProperties.GatewayAddresses); + @interface.Addresses = GetAddresses(ipProperties.UnicastAddresses); + @interface.Dns = GetAddresses(ipProperties.DnsAddresses); + @interface.Dhcp = GetAddresses(ipProperties.DhcpServerAddresses); + + if (@interface.Index.HasValue) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery($"select interfaceindex, guid, physicaladapter, manufacturer from win32_networkadapter where interfaceindex = {@interface.Index}") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery($"select * from win32_networkadapter where interfaceindex = {@interface.Index}"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var properties = @object.GetPropertyHashes(); + + @interface.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + @interface.Guid = @object.GetValue(properties, "guid"); + @interface.Physical = @object.GetValue(properties, "physicaladapter"); + + break; + } + } + + @interface.Routes = QueryInterfaceRoutes(@interface.Index.Value); + } + } + + interfaces.Add(@interface); + } + + return interfaces; + } + + private static List QueryInterfaceRoutes(uint interfaceIndex) + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\standardcimv2"), + Query = new ObjectQuery($"select addressFamily, state, interfaceindex, routemetric, nexthop, destinationprefix from msft_netroute where interfaceindex = {interfaceIndex}") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery($"select * from msft_netroute where interfaceindex = {interfaceIndex}"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var routes = new List(); + + using (collection) + { + foreach (var @object in collection) + { + var route = new Route + { + InterfaceIndex = interfaceIndex + }; + + var properties = @object.GetPropertyHashes(); + + if (@object.TryGetValue(properties, "routemetric", out var routemetric)) + { + if (int.TryParse(routemetric?.ToString(), out var metric)) route.Metric = metric; + } + + if (@object.TryGetValue(properties, "nexthop", out var nexthop)) + { + if (IPAddress.TryParse(nexthop?.ToString(), out var gateway)) route.Gateway = new IPAddress2(gateway); + } + + if (@object.TryGetValue(properties, "destinationprefix", out var destinationprefix)) + { + var split = destinationprefix?.ToString()?.Split('/'); + var cidrData = split?[1]; + + if (IPAddress.TryParse(split?[0], out var destination)) + route.Destination = new IPAddress2(destination); + + if (int.TryParse(cidrData, out var cidr)) + { + var mask = ConvertCidr(cidr); + route.Mask = mask; + } + } + + routes.Add(route); + } + } + + return routes; + } + + private static List GetAddresses(UnicastIPAddressInformationCollection unicastCollection) + { + var addresses = new List(); + + if (unicastCollection.Any() is false) return addresses; + + foreach (var unicast in unicastCollection) + { + addresses.Add(new Unicast + { + IpAddress = new IPAddress2(unicast.Address), + AddressPreferredLifetime = unicast.AddressPreferredLifetime, + AddressValidLifetime = unicast.AddressValidLifetime, + DuplicateAddressDetectionState = unicast.DuplicateAddressDetectionState, + Ipv4Mask = new IPAddress2(unicast.IPv4Mask), + PrefixLength = unicast.PrefixLength, + PrefixOrigin = unicast.PrefixOrigin, + SuffixOrigin = unicast.SuffixOrigin, + DhcpLeaseLifetime = unicast.DhcpLeaseLifetime, + }); + } + + return addresses; + } + + private static List GetAddresses(IPAddressCollection addressCollection) + { + var addresses = new List(); + + if (addressCollection.Any() is false) return addresses; + + foreach (var address in addressCollection) + { + addresses.Add(new IPAddress2(address)); + } + + return addresses; + } + + private static List GetAddresses(GatewayIPAddressInformationCollection addressCollection) + { + var addresses = new List(); + + if (addressCollection.Any() is false) return addresses; + + foreach (var address in addressCollection) + { + addresses.Add(new IPAddress2(address.Address)); + } + + return addresses; + } + + private static string? ConvertCidr(int cidr) + { + return cidr switch + { + 0 => "0.0.0.0", + 1 => "128.0.0.0", + 2 => "192.0.0.0", + 3 => "224.0.0.0", + 4 => "240.0.0.0", + 5 => "248.0.0.0", + 6 => "252.0.0.0", + 7 => "254.0.0.0", + 8 => "255.0.0.0", + 9 => "255.128.0.0", + 10 => "255.192.0.0", + 11 => "255.224.0.0", + 12 => "255.240.0.0", + 13 => "255.248.0.0", + 14 => "255.252.0.0", + 15 => "255.254.0.0", + 16 => "255.255.0.0", + 17 => "255.255.128.0", + 18 => "255.255.192.0", + 19 => "255.255.224.0", + 20 => "255.255.240.0", + 21 => "255.255.248.0", + 22 => "255.255.252.0", + 23 => "255.255.254.0", + 24 => "255.255.255.0", + 25 => "255.255.255.128", + 26 => "255.255.255.192", + 27 => "255.255.255.224", + 28 => "255.255.255.240", + 29 => "255.255.255.248", + 30 => "255.255.255.252", + 31 => "255.255.255.254", + 32 => "255.255.255.255", + _ => null, + }; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs index 43f210b..4869735 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs @@ -1,86 +1,88 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class MainboardHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class MainboardHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { + case InventoryRequest: await sender.SendAsync(GetMainboard(), cancellationToken); - } - } - - private static Mainboard GetMainboard() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select manufacturer, product, serialnumber from win32_baseboard") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_baseboard"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var mainboard = new Mainboard(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) - { - var properties = @object.GetPropertyHashes(); - - mainboard.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - mainboard.Model = @object.GetValue(properties, "product")?.Trim(); - mainboard.Serial = @object.GetValue(properties, "serialnumber")?.Trim(); - - break; - } - } - - searcher.Query = new ObjectQuery("select manufacturer, serialnumber, smbiosbiosversion, releasedate from win32_bios"); - - if (searcher.TryGet(out var collection2) is false) - { - searcher.Query = new ObjectQuery("select * from win32_bios"); - - if (searcher.TryGet(out collection2) is false) return null; - } - - using (collection2) - { - foreach (ManagementObject @object in collection2.Cast()) - { - var properties = @object.GetPropertyHashes(); - - mainboard.BiosManufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - mainboard.Serial = @object.GetValue(properties, "serialnumber")?.Trim(); - mainboard.BiosVersion = @object.GetValue(properties, "smbiosbiosversion")?.Trim(); - - if (@object.TryGetValue(properties, "releasedate", out var releasedate)) - { - mainboard.BiosDate = ManagementDateTimeConverter.ToDateTime(releasedate?.ToString()); - } - - break; - } - } - - //Logger.LogWarning(JsonSerializer.Serialize(mainboard, new JsonSerializerOptions - //{ - // WriteIndented= true - //})); - - return mainboard; + break; } } + + private static Mainboard GetMainboard() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select manufacturer, product, serialnumber from win32_baseboard") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_baseboard"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var mainboard = new Mainboard(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var properties = @object.GetPropertyHashes(); + + mainboard.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + mainboard.Model = @object.GetValue(properties, "product")?.Trim(); + mainboard.Serial = @object.GetValue(properties, "serialnumber")?.Trim(); + + break; + } + } + + searcher.Query = new ObjectQuery("select manufacturer, serialnumber, smbiosbiosversion, releasedate from win32_bios"); + + if (searcher.TryGet(out var collection2) is false) + { + searcher.Query = new ObjectQuery("select * from win32_bios"); + + if (searcher.TryGet(out collection2) is false) return null; + } + + using (collection2) + { + foreach (ManagementObject @object in collection2.Cast()) + { + var properties = @object.GetPropertyHashes(); + + mainboard.BiosManufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + mainboard.Serial = @object.GetValue(properties, "serialnumber")?.Trim(); + mainboard.BiosVersion = @object.GetValue(properties, "smbiosbiosversion")?.Trim(); + + if (@object.TryGetValue(properties, "releasedate", out var releasedate)) + { + mainboard.BiosDate = ManagementDateTimeConverter.ToDateTime(releasedate?.ToString()); + } + + break; + } + } + + //Logger.LogWarning(JsonSerializer.Serialize(mainboard, new JsonSerializerOptions + //{ + // WriteIndented= true + //})); + + return mainboard; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs index 11ef77f..aea3bce 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs @@ -1,123 +1,127 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class MemoryHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class MemoryHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new MemoryList(); - result.AddRange(GetMemory()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetMemory() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select tag, devicelocator, manufacturer, partnumber, serialnumber, capacity, speed, maxvoltage, configuredclockspeed, configuredvoltage from win32_physicalmemory") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_physicalmemory"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var memorysticks = new List(); - - using (collection) - { - uint index = 0; - - foreach (ManagementObject @object in collection.Cast()) + case InventoryRequest: { - var @memory = new Memory(); + var result = new Collection(); + result.AddRange(GetMemory()); - var properties = @object.GetPropertyHashes(); - - @memory.Index = index; - @memory.Tag = @object.GetValue(properties, "tag")?.Trim(); - @memory.Location = @object.GetValue(properties, "devicelocator")?.Trim(); - @memory.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - @memory.Model = @object.GetValue(properties, "partnumber")?.Trim(); - @memory.Serial = @object.GetValue(properties, "serialnumber")?.Trim(); - @memory.Capacity = @object.GetValue(properties, "capacity"); - @memory.Speed = @object.GetValue(properties, "speed"); - @memory.Voltage = @object.GetValue(properties, "maxvoltage"); - @memory.ConfiguredSpeed = @object.GetValue(properties, "configuredclockspeed"); - @memory.ConfiguredVoltage = @object.GetValue(properties, "configuredvoltage"); - - memorysticks.Add(@memory); - index++; + await sender.SendAsync(result, cancellationToken); + break; } - } + } + } - return memorysticks; + private static List GetMemory() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select tag, devicelocator, manufacturer, partnumber, serialnumber, capacity, speed, maxvoltage, configuredclockspeed, configuredvoltage from win32_physicalmemory") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_physicalmemory"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); } - //private async ValueTask GetMemoryMetricAsync(CancellationToken cancellationToken) - //{ - // var metric = new Memory.Metric(); + var memorysticks = new List(); - // using var searcher = new ManagementObjectSearcher - // { - // Scope = new ManagementScope(@"root\cimv2"), - // Query = new ObjectQuery("select totalphysicalmemory from win32_computersystem") - // }; + using (collection) + { + uint index = 0; - // if (searcher.TryGet(out var collection) is false) - // { - // searcher.Query = new ObjectQuery("select * from win32_computersystem"); + foreach (ManagementObject @object in collection.Cast()) + { + var @memory = new Memory(); - // if (searcher.TryGet(out collection) is false) return metric; - // } + var properties = @object.GetPropertyHashes(); - // ulong capacity = 0; + @memory.Index = index; + @memory.Tag = @object.GetValue(properties, "tag")?.Trim(); + @memory.Location = @object.GetValue(properties, "devicelocator")?.Trim(); + @memory.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + @memory.Model = @object.GetValue(properties, "partnumber")?.Trim(); + @memory.Serial = @object.GetValue(properties, "serialnumber")?.Trim(); + @memory.Capacity = @object.GetValue(properties, "capacity"); + @memory.Speed = @object.GetValue(properties, "speed"); + @memory.Voltage = @object.GetValue(properties, "maxvoltage"); + @memory.ConfiguredSpeed = @object.GetValue(properties, "configuredclockspeed"); + @memory.ConfiguredVoltage = @object.GetValue(properties, "configuredvoltage"); - // using (collection) - // { - // foreach (var @object in collection) - // { - // var properties = @object.GetPropertyHashes(); + memorysticks.Add(@memory); + index++; + } + } - // if (@object.TryGetValue(properties, "totalphysicalmemory", out capacity)) - // { - // capacity = capacity / 1024 / 1024; - // } - - // break; - // } - // } - - // if (MemoryAvailableCounter is null) - // { - // MemoryAvailableCounter = new PerformanceCounter - // { - // CategoryName = "Memory", - // CounterName = "Available MBytes" - // }; - - // metric.MemoryAvailable = MemoryAvailableCounter.NextValue(); - // await Task.Delay(1000, cancellationToken).ConfigureAwait(false); - // } - - // metric.Timestamp = DateTime.Now; - // metric.MemoryAvailable = MemoryAvailableCounter.NextValue(); - // metric.MemoryUsed = capacity - metric.MemoryAvailable; - // metric.MemoryUsagePercentage = metric.MemoryUsed / capacity * 100; - // metric.MemoryAvailablePercentage = 100 - metric.MemoryUsagePercentage; - // return metric; - //} + return memorysticks; } + + //private async ValueTask GetMemoryMetricAsync(CancellationToken cancellationToken) + //{ + // var metric = new Memory.Metric(); + + // using var searcher = new ManagementObjectSearcher + // { + // Scope = new ManagementScope(@"root\cimv2"), + // Query = new ObjectQuery("select totalphysicalmemory from win32_computersystem") + // }; + + // if (searcher.TryGet(out var collection) is false) + // { + // searcher.Query = new ObjectQuery("select * from win32_computersystem"); + + // if (searcher.TryGet(out collection) is false) return metric; + // } + + // ulong capacity = 0; + + // using (collection) + // { + // foreach (var @object in collection) + // { + // var properties = @object.GetPropertyHashes(); + + // if (@object.TryGetValue(properties, "totalphysicalmemory", out capacity)) + // { + // capacity = capacity / 1024 / 1024; + // } + + // break; + // } + // } + + // if (MemoryAvailableCounter is null) + // { + // MemoryAvailableCounter = new PerformanceCounter + // { + // CategoryName = "Memory", + // CounterName = "Available MBytes" + // }; + + // metric.MemoryAvailable = MemoryAvailableCounter.NextValue(); + // await Task.Delay(1000, cancellationToken).ConfigureAwait(false); + // } + + // metric.Timestamp = DateTime.Now; + // metric.MemoryAvailable = MemoryAvailableCounter.NextValue(); + // metric.MemoryUsed = capacity - metric.MemoryAvailable; + // metric.MemoryUsagePercentage = metric.MemoryUsed / capacity * 100; + // metric.MemoryAvailablePercentage = 100 - metric.MemoryUsagePercentage; + // return metric; + //} } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs index 0a36aad..ef15546 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs @@ -1,91 +1,93 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Win32; using System.Management; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.AccessControl; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class OperationSystemHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class OperationSystemHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { + case InventoryRequest: await sender.SendAsync(GetOperatingSystem(), cancellationToken); - } - } - - private static OperationSystem GetOperatingSystem() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select caption, version, serialnumber, osarchitecture, installdate from win32_operatingsystem") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_operatingsystem"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var os = new OperationSystem(); - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var properties = @object.GetPropertyHashes(); - - os.Name = @object.GetValue(properties, "caption")?.Trim(); - os.Version = @object.GetValue(properties, "version")?.Trim(); - os.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); - - if (@object.TryGetValue(properties, "osarchitecture", out var architecture)) - { - if (architecture is not null && architecture.ToLower().Contains("64")) os.Architecture = Architecture.X64; - } - else - { - os.Architecture = Architecture.X86; - } - - if (@object.TryGetValue(properties, "installdate", out var installdate)) - { - os.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString()); - } - - break; - } - } - - using var registry = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default); - using var key = registry.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); - - if (key is not null && key?.GetValue("UBR")?.ToString() is string buildNumber) - { - os.Version = $"{os.Version}.{buildNumber}"; - } - - searcher.Query = new ObjectQuery("select * from win32_portconnector"); - - if (searcher.TryGet(out var collection2) is false) - { - os.Virtual = true; - } - else - { - os.Virtual = false; - } - - collection2.Dispose(); - - return os; + break; } } + + private static OperationSystem GetOperatingSystem() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select caption, version, serialnumber, osarchitecture, installdate from win32_operatingsystem") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_operatingsystem"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var os = new OperationSystem(); + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var properties = @object.GetPropertyHashes(); + + os.Name = @object.GetValue(properties, "caption")?.Trim(); + os.Version = @object.GetValue(properties, "version")?.Trim(); + os.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); + + if (@object.TryGetValue(properties, "osarchitecture", out var architecture)) + { + if (architecture is not null && architecture.ToLower().Contains("64")) os.Architecture = Architecture.X64; + } + else + { + os.Architecture = Architecture.X86; + } + + if (@object.TryGetValue(properties, "installdate", out var installdate)) + { + os.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString()); + } + + break; + } + } + + using var registry = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default); + using var key = registry.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); + + if (key is not null && key?.GetValue("UBR")?.ToString() is string buildNumber) + { + os.Version = $"{os.Version}.{buildNumber}"; + } + + searcher.Query = new ObjectQuery("select * from win32_portconnector"); + + if (searcher.TryGet(out var collection2) is false) + { + os.Virtual = true; + } + else + { + os.Virtual = false; + } + + collection2.Dispose(); + + return os; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs index f52c60d..0b241e6 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs @@ -1,60 +1,64 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class PrinterHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class PrinterHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new PrinterList(); - result.AddRange(GetPrinters()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetPrinters() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select drivername, name, portname, location, comment from win32_printer") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_printer"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var printers = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) + case InventoryRequest: { - var printer = new Printer(); + var result = new Collection(); + result.AddRange(GetPrinters()); - var properties = @object.GetPropertyHashes(); - - printer.Driver = @object.GetValue(properties, "drivername")?.Trim(); - printer.Name = @object.GetValue(properties, "name")?.Trim(); - printer.Port = @object.GetValue(properties, "portname")?.Trim(); - printer.Location = @object.GetValue(properties, "location")?.Trim(); - printer.Comment = @object.GetValue(properties, "comment")?.Trim(); - - printers.Add(printer); + await sender.SendAsync(result, cancellationToken); + break; } - } - - return printers; } } + + private static List GetPrinters() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select drivername, name, portname, location, comment from win32_printer") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_printer"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var printers = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var printer = new Printer(); + + var properties = @object.GetPropertyHashes(); + + printer.Driver = @object.GetValue(properties, "drivername")?.Trim(); + printer.Name = @object.GetValue(properties, "name")?.Trim(); + printer.Port = @object.GetValue(properties, "portname")?.Trim(); + printer.Location = @object.GetValue(properties, "location")?.Trim(); + printer.Comment = @object.GetValue(properties, "comment")?.Trim(); + + printers.Add(printer); + } + } + + return printers; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs index 47052cc..7953846 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs @@ -1,143 +1,147 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers -{ - [SupportedOSPlatform("windows")] - public class ProcessorHandler : IAgentMessageHandler - { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is GetInventory) - { - var result = new ProcessorList(); - result.AddRange(GetProcessors()); +namespace Insight.Agent.Network.Handlers; - await sender.SendAsync(result, cancellationToken); - } +[SupportedOSPlatform("windows")] +public class ProcessorHandler : IMessageHandler +{ + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetProcessors()); + + await sender.SendAsync(result, cancellationToken); + break; + } + } + } + + private static List GetProcessors() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select deviceid, name, manufacturer, socketdesignation, version, processorid, l2cachesize, l3cachesize, currentclockspeed, maxclockspeed, numberofcores, numberoflogicalprocessors, virtualizationfirmwareenabled from win32_processor") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_processor"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); } - private static List GetProcessors() + var processors = new List(); + + using (collection) { - using var searcher = new ManagementObjectSearcher + uint index = 0; + + foreach (ManagementObject @object in collection.Cast()) { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select deviceid, name, manufacturer, socketdesignation, version, processorid, l2cachesize, l3cachesize, currentclockspeed, maxclockspeed, numberofcores, numberoflogicalprocessors, virtualizationfirmwareenabled from win32_processor") - }; + var processor = new Processor(); - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_processor"); + var properties = @object.GetPropertyHashes(); - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } + processor.Index = index; + processor.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); + processor.Name = @object.GetValue(properties, "name")?.Trim(); + processor.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + processor.Socket = @object.GetValue(properties, "socketdesignation")?.Trim(); + processor.Version = @object.GetValue(properties, "version")?.Trim(); + processor.SerialNumber = @object.GetValue(properties, "processorid")?.Trim(); + processor.CurrentSpeed = @object.GetValue(properties, "currentclockspeed"); + processor.MaxSpeed = @object.GetValue(properties, "maxclockspeed"); + processor.Cores = @object.GetValue(properties, "numberofcores"); + processor.LogicalCores = @object.GetValue(properties, "numberoflogicalprocessors"); + processor.Virtualization = @object.GetValue(properties, "virtualizationfirmwareenabled"); - var processors = new List(); + searcher.Query = new ObjectQuery("select level, maxcachesize from win32_cachememory"); - using (collection) - { - uint index = 0; - - foreach (ManagementObject @object in collection.Cast()) + if (searcher.TryGet(out var collection2) is false) { - var processor = new Processor(); + searcher.Query = new ObjectQuery("select * from win32_cachememory"); - var properties = @object.GetPropertyHashes(); + if (searcher.TryGet(out collection2) is false) throw new InvalidOperationException("WMI Collection NULL"); + } - processor.Index = index; - processor.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); - processor.Name = @object.GetValue(properties, "name")?.Trim(); - processor.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - processor.Socket = @object.GetValue(properties, "socketdesignation")?.Trim(); - processor.Version = @object.GetValue(properties, "version")?.Trim(); - processor.SerialNumber = @object.GetValue(properties, "processorid")?.Trim(); - processor.CurrentSpeed = @object.GetValue(properties, "currentclockspeed"); - processor.MaxSpeed = @object.GetValue(properties, "maxclockspeed"); - processor.Cores = @object.GetValue(properties, "numberofcores"); - processor.LogicalCores = @object.GetValue(properties, "numberoflogicalprocessors"); - processor.Virtualization = @object.GetValue(properties, "virtualizationfirmwareenabled"); - - searcher.Query = new ObjectQuery("select level, maxcachesize from win32_cachememory"); - - if (searcher.TryGet(out var collection2) is false) + using (collection2) + { + foreach (ManagementObject @object2 in collection2.Cast()) { - searcher.Query = new ObjectQuery("select * from win32_cachememory"); + var properties2 = @object2.GetPropertyHashes(); - if (searcher.TryGet(out collection2) is false) throw new InvalidOperationException("WMI Collection NULL"); - } + ProcessorCacheLevelEnum? cacheLevel = null; - using (collection2) - { - foreach (ManagementObject @object2 in collection2.Cast()) + cacheLevel = (ProcessorCacheLevelEnum?)@object2.GetValue(properties2, "level"); + + if (cacheLevel is null) continue; + + var installedSize = @object2.GetValue(properties2, "maxcachesize"); + + switch (cacheLevel) { - var properties2 = @object2.GetPropertyHashes(); - - ProcessorCacheLevelEnum? cacheLevel = null; - - cacheLevel = (ProcessorCacheLevelEnum?)@object2.GetValue(properties2, "level"); - - if (cacheLevel is null) continue; - - var installedSize = @object2.GetValue(properties2, "maxcachesize"); - - switch (cacheLevel) - { - case ProcessorCacheLevelEnum.L1: - { - processor.L1Size = installedSize; - break; - } - case ProcessorCacheLevelEnum.L2: - { - processor.L2Size = installedSize; - break; - } - case ProcessorCacheLevelEnum.L3: - { - processor.L3Size = installedSize; - break; - } - } + case ProcessorCacheLevelEnum.L1: + { + processor.L1Size = installedSize; + break; + } + case ProcessorCacheLevelEnum.L2: + { + processor.L2Size = installedSize; + break; + } + case ProcessorCacheLevelEnum.L3: + { + processor.L3Size = installedSize; + break; + } } } - - processors.Add(processor); - index++; } + + processors.Add(processor); + index++; } - - return processors; } - private enum ProcessorCacheLevelEnum - { - L1 = 3, - L2 = 4, - L3 = 5, - } - - //private async ValueTask GetProcessorMetricAsync(CancellationToken cancellationToken) - //{ - // var metric = new Processor.Metric(); - - // if (ProcessorTimeCounter is null) - // { - // ProcessorTimeCounter = new PerformanceCounter - // { - // CategoryName = "Processor", - // CounterName = "% Processor Time", - // InstanceName = "_Total" - // }; - - // metric.ProcessorUsagePercentage = ProcessorTimeCounter.NextValue(); - // await Task.Delay(1000, cancellationToken).ConfigureAwait(false); - // } - - // metric.Timestamp = DateTime.Now; - // metric.ProcessorUsagePercentage = ProcessorTimeCounter?.NextValue(); - // return metric; - //} + return processors; } + + private enum ProcessorCacheLevelEnum + { + L1 = 3, + L2 = 4, + L3 = 5, + } + + //private async ValueTask GetProcessorMetricAsync(CancellationToken cancellationToken) + //{ + // var metric = new Processor.Metric(); + + // if (ProcessorTimeCounter is null) + // { + // ProcessorTimeCounter = new PerformanceCounter + // { + // CategoryName = "Processor", + // CounterName = "% Processor Time", + // InstanceName = "_Total" + // }; + + // metric.ProcessorUsagePercentage = ProcessorTimeCounter.NextValue(); + // await Task.Delay(1000, cancellationToken).ConfigureAwait(false); + // } + + // metric.Timestamp = DateTime.Now; + // metric.ProcessorUsagePercentage = ProcessorTimeCounter?.NextValue(); + // return metric; + //} } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/ProxyHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/ProxyHandler.cs new file mode 100644 index 0000000..e837e98 --- /dev/null +++ b/src/Agent/Insight.Agent/Network/Handlers/ProxyHandler.cs @@ -0,0 +1,44 @@ +using Insight.Agent.Services; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; +using Microsoft.Extensions.Logging; + +namespace Insight.Agent.Network.Handlers; + +public class ProxyHandler : IMessageHandler +{ + private readonly ScriptService _scriptService; + private readonly ILogger _logger; + + public ProxyHandler(ScriptService scriptService, ILogger logger) + { + _scriptService = scriptService; + _logger = logger; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Proxy proxyRequest: + await OnProxyRequestAsync(sender, proxyRequest, cancellationToken); + break; + } + } + + private async ValueTask OnProxyRequestAsync(AgentSession sender, Proxy proxyRequest, CancellationToken cancellationToken) + { + var result = await _scriptService.QueryAsync(proxyRequest.Message.RequestData); + + await sender.SendAsync(new Proxy() + { + ProxyId = proxyRequest.ProxyId, + Message = new Response(proxyRequest.Message) + { + ResponseData = result.HadErrors ? result.Errors : result.Data, + ResponseError = result.HadErrors + } + }, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/ServiceHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/ServiceHandler.cs index db3fb34..cfe9342 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/ServiceHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/ServiceHandler.cs @@ -1,118 +1,122 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; using System.ServiceProcess; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class ServiceHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class ServiceHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new ServiceList(); - result.AddRange(GetServices()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetServices() - { - var services = new List(); - - var serviceControllers = ServiceController.GetServices()?.OrderBy(s => s.DisplayName)?.ToList(); - if (serviceControllers is null || serviceControllers.Any() is false) throw new InvalidOperationException("SERVICE Collection NULL"); - - foreach (var sc in serviceControllers) - { - var status = sc.Status switch + case InventoryRequest: { - ServiceControllerStatus.Stopped => Service.ServiceStatus.Stopped, - ServiceControllerStatus.StartPending => Service.ServiceStatus.StartPending, - ServiceControllerStatus.StopPending => Service.ServiceStatus.StopPending, - ServiceControllerStatus.Running => Service.ServiceStatus.Running, - ServiceControllerStatus.ContinuePending => Service.ServiceStatus.ContinuePending, - ServiceControllerStatus.PausePending => Service.ServiceStatus.PausePending, - ServiceControllerStatus.Paused => Service.ServiceStatus.Paused, - _ => Service.ServiceStatus.Unknown - }; + var result = new Collection(); + result.AddRange(GetServices()); - var mode = sc.StartType switch - { - ServiceStartMode.Boot => Service.ServiceMode.Boot, - ServiceStartMode.System => Service.ServiceMode.System, - ServiceStartMode.Automatic => Service.ServiceMode.Automatic, - ServiceStartMode.Manual => Service.ServiceMode.Manual, - ServiceStartMode.Disabled => Service.ServiceMode.Disabled, - _ => Service.ServiceMode.Unknown, - }; - - var service = new Service - { - Name = sc.ServiceName?.Trim(), - Display = sc.DisplayName?.Trim(), - Status = status, - StartMode = mode - }; - - services.Add(service); - } - - // additional infos - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("SELECT processid, name, description, pathname, startname, delayedautostart from win32_service") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_service"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var services2 = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) - { - var service2 = new Service(); - - var properties = @object.GetPropertyHashes(); - - service2.Name = @object.GetValue(properties, "name")?.Trim(); - service2.ProcessId = @object.GetValue(properties, "processid"); - service2.Description = @object.GetValue(properties, "description")?.Trim(); - service2.PathName = @object.GetValue(properties, "pathname")?.Trim(); - service2.Account = @object.GetValue(properties, "startname")?.Trim(); - service2.Delay = @object.GetValue(properties, "delayedautostart"); - - services2.Add(service2); + await sender.SendAsync(result, cancellationToken); + break; } - } - - if (services2.Any() is false) return services; - - foreach (var svc in services) - { - var map = services2.Where(p => p.Name == svc.Name).FirstOrDefault(); - - if (map is null) continue; - - svc.ProcessId = map.ProcessId; - svc.Description = map.Description; - svc.PathName = map.PathName; - svc.Account = map.Account; - svc.Delay = map.Delay; - } - - return services.OrderBy(x => x.Name).ToList(); } } + + private static List GetServices() + { + var services = new List(); + + var serviceControllers = ServiceController.GetServices()?.OrderBy(s => s.DisplayName)?.ToList(); + if (serviceControllers is null || serviceControllers.Any() is false) throw new InvalidOperationException("SERVICE Collection NULL"); + + foreach (var sc in serviceControllers) + { + var status = sc.Status switch + { + ServiceControllerStatus.Stopped => Service.ServiceStatus.Stopped, + ServiceControllerStatus.StartPending => Service.ServiceStatus.StartPending, + ServiceControllerStatus.StopPending => Service.ServiceStatus.StopPending, + ServiceControllerStatus.Running => Service.ServiceStatus.Running, + ServiceControllerStatus.ContinuePending => Service.ServiceStatus.ContinuePending, + ServiceControllerStatus.PausePending => Service.ServiceStatus.PausePending, + ServiceControllerStatus.Paused => Service.ServiceStatus.Paused, + _ => Service.ServiceStatus.Unknown + }; + + var mode = sc.StartType switch + { + ServiceStartMode.Boot => Service.ServiceMode.Boot, + ServiceStartMode.System => Service.ServiceMode.System, + ServiceStartMode.Automatic => Service.ServiceMode.Automatic, + ServiceStartMode.Manual => Service.ServiceMode.Manual, + ServiceStartMode.Disabled => Service.ServiceMode.Disabled, + _ => Service.ServiceMode.Unknown, + }; + + var service = new Service + { + Name = sc.ServiceName?.Trim(), + Display = sc.DisplayName?.Trim(), + Status = status, + StartMode = mode + }; + + services.Add(service); + } + + // additional infos + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("SELECT processid, name, description, pathname, startname, delayedautostart from win32_service") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_service"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var services2 = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var service2 = new Service(); + + var properties = @object.GetPropertyHashes(); + + service2.Name = @object.GetValue(properties, "name")?.Trim(); + service2.ProcessId = @object.GetValue(properties, "processid"); + service2.Description = @object.GetValue(properties, "description")?.Trim(); + service2.PathName = @object.GetValue(properties, "pathname")?.Trim(); + service2.Account = @object.GetValue(properties, "startname")?.Trim(); + service2.Delay = @object.GetValue(properties, "delayedautostart"); + + services2.Add(service2); + } + } + + if (services2.Any() is false) return services; + + foreach (var svc in services) + { + var map = services2.Where(p => p.Name == svc.Name).FirstOrDefault(); + + if (map is null) continue; + + svc.ProcessId = map.ProcessId; + svc.Description = map.Description; + svc.PathName = map.PathName; + svc.Account = map.Account; + svc.Delay = map.Delay; + } + + return services.OrderBy(x => x.Name).ToList(); + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs index c857127..b393ae5 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs @@ -1,252 +1,254 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Runtime.InteropServices; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers -{ - [SupportedOSPlatform("windows")] - public class SessionHandler : IAgentMessageHandler - { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is GetInventory) - { - var result = new SessionList(); - result.AddRange(GetSessions()); +namespace Insight.Agent.Network.Handlers; - await sender.SendAsync(result, cancellationToken); - } +[SupportedOSPlatform("windows")] +public class SessionHandler : IMessageHandler +{ + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetSessions()); + + await sender.SendAsync(result, cancellationToken); + break; + } + } + } + + private static List GetSessions() + { + var sessions = new List(); + + foreach (var s in NativeMethods.GetSessions()) + { + sessions.Add(new Session + { + Sid = s.SessionId.ToString(), + User = s.Username, + Type = s.Workstation, + Status = s.State.ToString(), + Remote = s.IPAddress + }); } - private static List GetSessions() + return sessions; + } + + private static partial class NativeMethods + { + //public const int WTS_CURRENT_SESSION = -1; + + [DllImport("wtsapi32.dll")] + static extern int WTSEnumerateSessions( + nint pServer, + [MarshalAs(UnmanagedType.U4)] int iReserved, + [MarshalAs(UnmanagedType.U4)] int iVersion, + ref nint pSessionInfo, + [MarshalAs(UnmanagedType.U4)] ref int iCount); + + [DllImport("Wtsapi32.dll")] + private static extern bool WTSQuerySessionInformation( + nint pServer, + int iSessionID, + WTS_INFO_CLASS oInfoClass, + out nint pBuffer, + out uint iBytesReturned); + + [DllImport("wtsapi32.dll")] + static extern void WTSFreeMemory( + nint pMemory); + + [StructLayout(LayoutKind.Sequential)] + private struct WTS_CLIENT_ADDRESS { - var query = NativeMethods.GetSessions(); + public int iAddressFamily; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public byte[] bAddress; + } - var sessions = new List(); + [StructLayout(LayoutKind.Sequential)] + private struct WTS_SESSION_INFO + { + public int iSessionID; + [MarshalAs(UnmanagedType.LPStr)] + public string sWinsWorkstationName; + public WTS_CONNECTSTATE_CLASS oState; + } - foreach (var s in NativeMethods.GetSessions()) + [StructLayout(LayoutKind.Sequential)] + private struct WTS_CLIENT_DISPLAY + { + public int iHorizontalResolution; + public int iVerticalResolution; + //1 = The display uses 4 bits per pixel for a maximum of 16 colors. + //2 = The display uses 8 bits per pixel for a maximum of 256 colors. + //4 = The display uses 16 bits per pixel for a maximum of 2^16 colors. + //8 = The display uses 3-byte RGB values for a maximum of 2^24 colors. + //16 = The display uses 15 bits per pixel for a maximum of 2^15 colors. + public int iColorDepth; + } + + public enum WTS_CONNECTSTATE_CLASS + { + WTSActive, + WTSConnected, + WTSConnectQuery, + WTSShadow, + WTSDisconnected, + WTSIdle, + WTSListen, + WTSReset, + WTSDown, + WTSInit + } + + public enum WTS_INFO_CLASS + { + WTSInitialProgram, + WTSApplicationName, + WTSWorkingDirectory, + WTSOEMId, + WTSSessionId, + WTSUserName, + WTSWinStationName, + WTSDomainName, + WTSConnectState, + WTSClientBuildNumber, + WTSClientName, + WTSClientDirectory, + WTSClientProductId, + WTSClientHardwareId, + WTSClientAddress, + WTSClientDisplay, + WTSClientProtocolType, + WTSIdleTime, + WTSLogonTime, + WTSIncomingBytes, + WTSOutgoingBytes, + WTSIncomingFrames, + WTSOutgoingFrames, + WTSClientInfo, + WTSSessionInfo, + WTSConfigInfo, + WTSValidationInfo, + WTSSessionAddressV4, + WTSIsRemoteSession + } + + public class WTSSession + { + public int SessionId { get; set; } + public WTS_CONNECTSTATE_CLASS State { get; set; } + public string? Workstation { get; set; } + public string? IPAddress { get; set; } + public string? Username { get; set; } + public int HorizontalResolution { get; set; } + public int VerticalResolution { get; set; } + public int ColorDepth { get; set; } + public string? ClientApplicationDirectory { get; set; } + } + + public static IEnumerable GetSessions() + { + var sessions = new List(); + + var pServer = nint.Zero; + var pSessionInfo = nint.Zero; + + try { - sessions.Add(new Session + var count = 0; + var sessionCount = WTSEnumerateSessions(pServer, 0, 1, ref pSessionInfo, ref count); + var dataSize = (long)Marshal.SizeOf(typeof(WTS_SESSION_INFO)); + var current = (long)pSessionInfo; + + if (sessionCount <= 0) + return sessions; + + for (int i = 0; i < count; i++) { - Sid = s.SessionId.ToString(), - User = s.Username, - Type = s.Workstation, - Status = s.State.ToString(), - Remote = s.IPAddress - }); + if (Marshal.PtrToStructure((nint)current, typeof(WTS_SESSION_INFO)) is not object sessionStructure) + continue; + + var sessionInfo = (WTS_SESSION_INFO)sessionStructure; + current += dataSize; + + var session = new WTSSession + { + SessionId = sessionInfo.iSessionID, + State = sessionInfo.oState, + Workstation = sessionInfo.sWinsWorkstationName, + }; + + var returned = (uint)0; + + // get terminal user address + var address = nint.Zero; + var clientAddress = new WTS_CLIENT_ADDRESS(); + + if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientAddress, out address, out returned) == true) + { + if (Marshal.PtrToStructure(address, clientAddress.GetType()) is not object addressStructure) + break; + + clientAddress = (WTS_CLIENT_ADDRESS)addressStructure; + session.IPAddress = clientAddress.bAddress[2] + "." + clientAddress.bAddress[3] + "." + clientAddress.bAddress[4] + "." + clientAddress.bAddress[5]; + } + + // get terminal user name + if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSUserName, out address, out returned) == true) + { + session.Username = Marshal.PtrToStringAnsi(address); + } + + // get terminal user domain name + if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSDomainName, out address, out returned) == true) + { + session.Username = Marshal.PtrToStringAnsi(address) + @"\" + session.Username; + } + + // get terminal user display informations + var clientDisplay = new WTS_CLIENT_DISPLAY(); + + if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDisplay, out address, out returned) == true) + { + if (Marshal.PtrToStructure(address, clientDisplay.GetType()) is not object displayStructure) + break; + + clientDisplay = (WTS_CLIENT_DISPLAY)displayStructure; + session.HorizontalResolution = clientDisplay.iHorizontalResolution; + session.VerticalResolution = clientDisplay.iVerticalResolution; + session.ColorDepth = clientDisplay.iColorDepth; + } + + // get terminal user application directory + if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDirectory, out address, out returned) == true) + { + session.ClientApplicationDirectory = Marshal.PtrToStringAnsi(address); + } + + sessions.Add(session); + } + } + catch (Exception) + { + throw; + } + finally + { + WTSFreeMemory(pSessionInfo); } return sessions; } - - private static partial class NativeMethods - { - //public const int WTS_CURRENT_SESSION = -1; - - [DllImport("wtsapi32.dll")] - static extern int WTSEnumerateSessions( - nint pServer, - [MarshalAs(UnmanagedType.U4)] int iReserved, - [MarshalAs(UnmanagedType.U4)] int iVersion, - ref nint pSessionInfo, - [MarshalAs(UnmanagedType.U4)] ref int iCount); - - [DllImport("Wtsapi32.dll")] - private static extern bool WTSQuerySessionInformation( - nint pServer, - int iSessionID, - WTS_INFO_CLASS oInfoClass, - out nint pBuffer, - out uint iBytesReturned); - - [DllImport("wtsapi32.dll")] - static extern void WTSFreeMemory( - nint pMemory); - - [StructLayout(LayoutKind.Sequential)] - private struct WTS_CLIENT_ADDRESS - { - public int iAddressFamily; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public byte[] bAddress; - } - - [StructLayout(LayoutKind.Sequential)] - private struct WTS_SESSION_INFO - { - public int iSessionID; - [MarshalAs(UnmanagedType.LPStr)] - public string sWinsWorkstationName; - public WTS_CONNECTSTATE_CLASS oState; - } - - [StructLayout(LayoutKind.Sequential)] - private struct WTS_CLIENT_DISPLAY - { - public int iHorizontalResolution; - public int iVerticalResolution; - //1 = The display uses 4 bits per pixel for a maximum of 16 colors. - //2 = The display uses 8 bits per pixel for a maximum of 256 colors. - //4 = The display uses 16 bits per pixel for a maximum of 2^16 colors. - //8 = The display uses 3-byte RGB values for a maximum of 2^24 colors. - //16 = The display uses 15 bits per pixel for a maximum of 2^15 colors. - public int iColorDepth; - } - - public enum WTS_CONNECTSTATE_CLASS - { - WTSActive, - WTSConnected, - WTSConnectQuery, - WTSShadow, - WTSDisconnected, - WTSIdle, - WTSListen, - WTSReset, - WTSDown, - WTSInit - } - - public enum WTS_INFO_CLASS - { - WTSInitialProgram, - WTSApplicationName, - WTSWorkingDirectory, - WTSOEMId, - WTSSessionId, - WTSUserName, - WTSWinStationName, - WTSDomainName, - WTSConnectState, - WTSClientBuildNumber, - WTSClientName, - WTSClientDirectory, - WTSClientProductId, - WTSClientHardwareId, - WTSClientAddress, - WTSClientDisplay, - WTSClientProtocolType, - WTSIdleTime, - WTSLogonTime, - WTSIncomingBytes, - WTSOutgoingBytes, - WTSIncomingFrames, - WTSOutgoingFrames, - WTSClientInfo, - WTSSessionInfo, - WTSConfigInfo, - WTSValidationInfo, - WTSSessionAddressV4, - WTSIsRemoteSession - } - - public class WTSSession - { - public int SessionId { get; set; } - public WTS_CONNECTSTATE_CLASS State { get; set; } - public string? Workstation { get; set; } - public string? IPAddress { get; set; } - public string? Username { get; set; } - public int HorizontalResolution { get; set; } - public int VerticalResolution { get; set; } - public int ColorDepth { get; set; } - public string? ClientApplicationDirectory { get; set; } - } - - public static IEnumerable GetSessions() - { - var sessions = new List(); - - var pServer = nint.Zero; - var pSessionInfo = nint.Zero; - - try - { - var count = 0; - var sessionCount = WTSEnumerateSessions(pServer, 0, 1, ref pSessionInfo, ref count); - var dataSize = (long)Marshal.SizeOf(typeof(WTS_SESSION_INFO)); - var current = (long)pSessionInfo; - - if (sessionCount <= 0) - return sessions; - - for (int i = 0; i < count; i++) - { - if (Marshal.PtrToStructure((nint)current, typeof(WTS_SESSION_INFO)) is not object sessionStructure) - continue; - - var sessionInfo = (WTS_SESSION_INFO)sessionStructure; - current += dataSize; - - var session = new WTSSession - { - SessionId = sessionInfo.iSessionID, - State = sessionInfo.oState, - Workstation = sessionInfo.sWinsWorkstationName, - }; - - var returned = (uint)0; - - // get terminal user address - var address = nint.Zero; - var clientAddress = new WTS_CLIENT_ADDRESS(); - - if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientAddress, out address, out returned) == true) - { - if (Marshal.PtrToStructure(address, clientAddress.GetType()) is not object addressStructure) - break; - - clientAddress = (WTS_CLIENT_ADDRESS)addressStructure; - session.IPAddress = clientAddress.bAddress[2] + "." + clientAddress.bAddress[3] + "." + clientAddress.bAddress[4] + "." + clientAddress.bAddress[5]; - } - - // get terminal user name - if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSUserName, out address, out returned) == true) - { - session.Username = Marshal.PtrToStringAnsi(address); - } - - // get terminal user domain name - if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSDomainName, out address, out returned) == true) - { - session.Username = Marshal.PtrToStringAnsi(address) + @"\" + session.Username; - } - - // get terminal user display informations - var clientDisplay = new WTS_CLIENT_DISPLAY(); - - if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDisplay, out address, out returned) == true) - { - if (Marshal.PtrToStructure(address, clientDisplay.GetType()) is not object displayStructure) - break; - - clientDisplay = (WTS_CLIENT_DISPLAY)displayStructure; - session.HorizontalResolution = clientDisplay.iHorizontalResolution; - session.VerticalResolution = clientDisplay.iVerticalResolution; - session.ColorDepth = clientDisplay.iColorDepth; - } - - // get terminal user application directory - if (WTSQuerySessionInformation(pServer, sessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDirectory, out address, out returned) == true) - { - session.ClientApplicationDirectory = Marshal.PtrToStringAnsi(address); - } - - sessions.Add(session); - } - } - catch (Exception) - { - throw; - } - finally - { - WTSFreeMemory(pSessionInfo); - } - - return sessions; - } - } } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs index 806c2d4..e2c04ca 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs @@ -1,119 +1,123 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Win32; using System.Globalization; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.AccessControl; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +internal class SoftwareHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - internal class SoftwareHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var x64 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry64), cancellationToken); - var x86 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry32), cancellationToken); - - await Task.WhenAll(x64, x86).ConfigureAwait(false); - - var result = new ApplicationList(); - result.AddRange(x64.Result); - result.AddRange(x86.Result.Where(p => result.All(app => p.Name != app.Name && p.Version != app.Version))); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static IEnumerable ApplicationRegistryQuery(RegistryView registryView) - { - using var registry = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView); - - using var key = registry.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); - if (key is null) throw new NullReferenceException(nameof(key)); - - var apps = new List(); - - var architecture = registryView switch - { - RegistryView.Registry32 => Architecture.X86, - _ => Architecture.X64 - }; - - foreach (string name in key.GetSubKeyNames()) - { - using var query = key.OpenSubKey(name); - if (query is null) continue; - - var app = new Application + case InventoryRequest: { - Architecture = architecture - }; + var x64 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry64), cancellationToken); + var x86 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry32), cancellationToken); - if (query.GetValue("DisplayName")?.ToString()?.Trim() is string displayName && string.IsNullOrWhiteSpace(displayName) is false) - { - app.Name = displayName; + await Task.WhenAll(x64, x86).ConfigureAwait(false); + + var result = new Collection(); + result.AddRange(x64.Result); + result.AddRange(x86.Result.Where(p => result.All(app => p.Name != app.Name && p.Version != app.Version))); + + await sender.SendAsync(result, cancellationToken); + break; } - - if (query.GetValue("Publisher")?.ToString()?.Trim() is string publisher && string.IsNullOrWhiteSpace(publisher) is false) - { - app.Publisher = publisher; - } - - if (query.GetValue("DisplayVersion")?.ToString()?.Trim() is string version && string.IsNullOrWhiteSpace(version) is false) - { - app.Version = version; - } - - if (query.GetValue("InstallLocation")?.ToString()?.Trim() is string location && string.IsNullOrWhiteSpace(location) is false) - { - app.Location = location; - } - - if (query.GetValue("InstallSource")?.ToString()?.Trim() is string source && string.IsNullOrWhiteSpace(source) is false) - { - app.Source = source; - } - - if (query.GetValue("UninstallString")?.ToString()?.Trim() is string uninstall && string.IsNullOrWhiteSpace(uninstall) is false) - { - app.Uninstall = uninstall; - } - - if (app.Uninstall is null) - { - if (query.GetValue("UninstallString_Hidden")?.ToString()?.Trim() is string uninstall2 && string.IsNullOrWhiteSpace(uninstall2) is false) - { - app.Uninstall = uninstall2; - } - } - - if (query.GetValue("InstallDate")?.ToString()?.Trim() is string installDate) - { - if (DateTime.TryParseExact(installDate, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime valid)) - { - app.InstallDate = valid; - } - - if (app.InstallDate is null) - { - if (DateTime.TryParseExact(installDate, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime valid2)) - { - app.InstallDate = valid2; - } - } - } - - if (app.Name is not null) - { - apps.Add(app); - } - } - - return apps; } } + + private static IEnumerable ApplicationRegistryQuery(RegistryView registryView) + { + using var registry = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView); + + using var key = registry.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); + if (key is null) throw new NullReferenceException(nameof(key)); + + var apps = new List(); + + var architecture = registryView switch + { + RegistryView.Registry32 => Architecture.X86, + _ => Architecture.X64 + }; + + foreach (string name in key.GetSubKeyNames()) + { + using var query = key.OpenSubKey(name); + if (query is null) continue; + + var app = new Application + { + Architecture = architecture + }; + + if (query.GetValue("DisplayName")?.ToString()?.Trim() is string displayName && string.IsNullOrWhiteSpace(displayName) is false) + { + app.Name = displayName; + } + + if (query.GetValue("Publisher")?.ToString()?.Trim() is string publisher && string.IsNullOrWhiteSpace(publisher) is false) + { + app.Publisher = publisher; + } + + if (query.GetValue("DisplayVersion")?.ToString()?.Trim() is string version && string.IsNullOrWhiteSpace(version) is false) + { + app.Version = version; + } + + if (query.GetValue("InstallLocation")?.ToString()?.Trim() is string location && string.IsNullOrWhiteSpace(location) is false) + { + app.Location = location; + } + + if (query.GetValue("InstallSource")?.ToString()?.Trim() is string source && string.IsNullOrWhiteSpace(source) is false) + { + app.Source = source; + } + + if (query.GetValue("UninstallString")?.ToString()?.Trim() is string uninstall && string.IsNullOrWhiteSpace(uninstall) is false) + { + app.Uninstall = uninstall; + } + + if (app.Uninstall is null) + { + if (query.GetValue("UninstallString_Hidden")?.ToString()?.Trim() is string uninstall2 && string.IsNullOrWhiteSpace(uninstall2) is false) + { + app.Uninstall = uninstall2; + } + } + + if (query.GetValue("InstallDate")?.ToString()?.Trim() is string installDate) + { + if (DateTime.TryParseExact(installDate, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime valid)) + { + app.InstallDate = valid; + } + + if (app.InstallDate is null) + { + if (DateTime.TryParseExact(installDate, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime valid2)) + { + app.InstallDate = valid2; + } + } + } + + if (app.Name is not null) + { + apps.Add(app); + } + } + + return apps; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs index 84b5506..18bb8a1 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs @@ -1,308 +1,312 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -using static Insight.Agent.Messages.PhysicalDisk; -using static Insight.Agent.Messages.StoragePool; -using static Insight.Agent.Messages.VirtualDisk; +using static Insight.Domain.Network.Agent.Messages.PhysicalDisk; +using static Insight.Domain.Network.Agent.Messages.StoragePool; +using static Insight.Domain.Network.Agent.Messages.VirtualDisk; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class StoragePoolHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class StoragePoolHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new StoragePoolList(); - result.AddRange(GetStoragePool()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetStoragePool() - { - if (Environment.OSVersion.Version.Major < 6 || Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor < 2) - { - throw new PlatformNotSupportedException(); - } - - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\microsoft\windows\storage"), - Query = new ObjectQuery("select objectid, uniqueid, name, friendlyname, resiliencysettingnamedefault, isprimordial, isreadonly, isclustered, size, allocatedsize, logicalsectorsize, operationalstatus, healthstatus from msft_storagepool") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from msft_storagepool"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var pools = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) + case InventoryRequest: { - var pool = new StoragePool(); + var result = new Collection(); + result.AddRange(GetStoragePool()); - var properties = @object.GetPropertyHashes(); - - pool.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); - pool.Name = @object.GetValue(properties, "name")?.Trim(); - pool.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); - - if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) - { - pool.States = operationals.Select(p => (StoragePool.OperationalState)p).ToList(); - } - - pool.Health = (StoragePool.HealthState)@object.GetValue(properties, "healthstatus"); - pool.RetireMissingPhysicalDisks = (RetireMissingPhysicalDisksEnum)@object.GetValue(properties, "retiremissingphysicaldisks"); - pool.Resiliency = @object.GetValue(properties, "resiliencysettingnamedefault")?.Trim(); - pool.IsPrimordial = @object.GetValue(properties, "isprimordial"); - pool.IsReadOnly = @object.GetValue(properties, "isreadonly"); - pool.IsClustered = @object.GetValue(properties, "isclustered"); - pool.Size = @object.GetValue(properties, "size"); - pool.AllocatedSize = @object.GetValue(properties, "allocatedsize"); - pool.SectorSize = @object.GetValue(properties, "logicalsectorsize"); - - if (@object.GetValue(properties, "objectid") is string objectId) - { - pool.PhysicalDisks = QueryPhysicalDisksByStoragePool(objectId); - pool.VirtualDisks = QueryVirtualDisksByStoragePool(objectId); - } - - pools.Add(pool); + await sender.SendAsync(result, cancellationToken); + break; } - } - - return pools; - } - - private static List GetPhysicalDisks() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\microsoft\windows\storage"), - Query = new ObjectQuery("select objectid, uniqueid, name, friendlyname from msft_physicaldisk") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from msft_physicaldisk"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var disks = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) - { - var disk = new PhysicalDisk(); - - var properties = @object.GetPropertyHashes(); - - disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); - disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); - disk.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - disk.Model = @object.GetValue(properties, "model")?.Trim(); - disk.MediaType = @object.GetValue(properties, "mediatype"); - disk.BusType = @object.GetValue(properties, "bustype"); - - if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) - { - disk.States = operationals.Select(p => (PhysicalDisk.OperationalState)p).ToList(); - } - - disk.Health = (PhysicalDisk.HealthState)@object.GetValue(properties, "healthstatus"); - - if (@object.TryGetValue(properties, "supportedusages", out var supportedusages) && supportedusages is not null) - { - disk.SupportedUsages = supportedusages.Select(p => (SupportedUsagesEnum)p).ToList(); - } - - disk.Usage = @object.GetValue(properties, "usage"); - disk.PhysicalLocation = @object.GetValue(properties, "physicallocation")?.Trim(); - disk.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); - disk.FirmwareVersion = @object.GetValue(properties, "firmwareversion")?.Trim(); - disk.Size = @object.GetValue(properties, "size"); - disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); - disk.LogicalSectorSize = @object.GetValue(properties, "logicalsectorsize"); - disk.PhysicalSectorSize = @object.GetValue(properties, "physicalsectorsize"); - disk.VirtualDiskFootprint = @object.GetValue(properties, "virtualdiskfootprint"); - - disks.Add(disk); - } - } - - return disks; - } - - private static List GetVirtualDisks() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\microsoft\windows\storage"), - Query = new ObjectQuery("select objectid, uniqueid, name, friendlyname, access, provisioningtype, physicaldiskredundancy, resiliencysettingname, isdeduplicationenabled, issnapshot, operationalstatus, healthstatus, size, allocatedsize, footprintonpool, readcachesize, writecachesize from msft_virtualdisk") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from msft_virtualdisk"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var disks = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) - { - var disk = new VirtualDisk(); - - var properties = @object.GetPropertyHashes(); - - disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); - disk.Name = @object.GetValue(properties, "name")?.Trim(); - disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); - disk.AccessType = (AccessTypeEnum)@object.GetValue(properties, "access"); - disk.ProvisioningType = (ProvisioningTypeEnum)@object.GetValue(properties, "provisioningtype"); - disk.PhysicalDiskRedundancy = @object.GetValue(properties, "physicaldiskredundancy"); - disk.ResiliencySettingName = @object.GetValue(properties, "resiliencysettingname")?.Trim(); - disk.Deduplication = @object.GetValue(properties, "isdeduplicationenabled"); - disk.IsSnapshot = @object.GetValue(properties, "issnapshot"); - - if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) - { - disk.States = operationals.Select(p => (VirtualDisk.OperationalState)p).ToList(); - } - - disk.Health = (VirtualDisk.HealthState)@object.GetValue(properties, "healthstatus"); - disk.Size = @object.GetValue(properties, "size"); - disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); - disk.FootprintOnPool = @object.GetValue(properties, "footprintonpool"); - disk.ReadCacheSize = @object.GetValue(properties, "readcachesize"); - disk.WriteCacheSize = @object.GetValue(properties, "writecachesize"); - - disks.Add(disk); - } - } - - return disks; - } - - private static List QueryPhysicalDisksByStoragePool(string storagePoolObjectId) - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\microsoft\windows\storage"), - Query = new ObjectQuery("ASSOCIATORS OF {MSFT_StoragePool.ObjectId=\"" + Helpers.EscapeWql(storagePoolObjectId) + "\"} WHERE AssocClass = MSFT_StoragePoolToPhysicalDisk") - }; - - if (searcher.TryGet(out var collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - - var disks = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var disk = new PhysicalDisk(); - - var properties = @object.GetPropertyHashes(); - - disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); - disk.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); - disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); - disk.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); - disk.Model = @object.GetValue(properties, "model")?.Trim(); - disk.MediaType = @object.GetValue(properties, "mediatype"); - disk.BusType = @object.GetValue(properties, "bustype"); - - if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) - { - disk.States = operationals.Select(p => (PhysicalDisk.OperationalState)p).ToList(); - } - - disk.Health = (PhysicalDisk.HealthState)@object.GetValue(properties, "healthstatus"); - - if (@object.TryGetValue(properties, "supportedusages", out var supportedusages) && supportedusages is not null) - { - disk.SupportedUsages = supportedusages.Select(p => (SupportedUsagesEnum)p).ToList(); - } - - disk.Usage = @object.GetValue(properties, "usage"); - disk.PhysicalLocation = @object.GetValue(properties, "physicallocation")?.Trim(); - disk.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); - disk.FirmwareVersion = @object.GetValue(properties, "firmwareversion")?.Trim(); - disk.Size = @object.GetValue(properties, "size"); - disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); - disk.LogicalSectorSize = @object.GetValue(properties, "logicalsectorsize"); - disk.PhysicalSectorSize = @object.GetValue(properties, "physicalsectorsize"); - disk.VirtualDiskFootprint = @object.GetValue(properties, "virtualdiskfootprint"); - - disks.Add(disk); - } - } - - return disks; - } - - private static List QueryVirtualDisksByStoragePool(string storagePoolObjectId) - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\microsoft\windows\storage"), - Query = new ObjectQuery("ASSOCIATORS OF {MSFT_StoragePool.ObjectId=\"" + Helpers.EscapeWql(storagePoolObjectId) + "\"} WHERE AssocClass = MSFT_StoragePoolToVirtualDisk") - }; - - if (searcher.TryGet(out var collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - - var disks = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var disk = new VirtualDisk(); - - var properties = @object.GetPropertyHashes(); - - disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); - disk.Name = @object.GetValue(properties, "name")?.Trim(); - disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); - disk.AccessType = (AccessTypeEnum)@object.GetValue(properties, "access"); - disk.ProvisioningType = (ProvisioningTypeEnum)@object.GetValue(properties, "provisioningtype"); - disk.PhysicalDiskRedundancy = @object.GetValue(properties, "physicaldiskredundancy"); - disk.ResiliencySettingName = @object.GetValue(properties, "resiliencysettingname")?.Trim(); - disk.Deduplication = @object.GetValue(properties, "isdeduplicationenabled"); - disk.IsSnapshot = @object.GetValue(properties, "issnapshot"); - - if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) - { - disk.States = operationals.Select(p => (VirtualDisk.OperationalState)p).ToList(); - } - - disk.Health = (VirtualDisk.HealthState)@object.GetValue(properties, "healthstatus"); - disk.Size = @object.GetValue(properties, "size"); - disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); - disk.FootprintOnPool = @object.GetValue(properties, "footprintonpool"); - disk.ReadCacheSize = @object.GetValue(properties, "readcachesize"); - disk.WriteCacheSize = @object.GetValue(properties, "writecachesize"); - - disks.Add(disk); - } - } - - return disks; } } + + private static List GetStoragePool() + { + if (Environment.OSVersion.Version.Major < 6 || Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor < 2) + { + throw new PlatformNotSupportedException(); + } + + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\microsoft\windows\storage"), + Query = new ObjectQuery("select objectid, uniqueid, name, friendlyname, resiliencysettingnamedefault, isprimordial, isreadonly, isclustered, size, allocatedsize, logicalsectorsize, operationalstatus, healthstatus from msft_storagepool") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from msft_storagepool"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var pools = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var pool = new StoragePool(); + + var properties = @object.GetPropertyHashes(); + + pool.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); + pool.Name = @object.GetValue(properties, "name")?.Trim(); + pool.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); + + if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) + { + pool.States = operationals.Select(p => (StoragePool.OperationalState)p).ToList(); + } + + pool.Health = (StoragePool.HealthState)@object.GetValue(properties, "healthstatus"); + pool.RetireMissingPhysicalDisks = (RetireMissingPhysicalDisksEnum)@object.GetValue(properties, "retiremissingphysicaldisks"); + pool.Resiliency = @object.GetValue(properties, "resiliencysettingnamedefault")?.Trim(); + pool.IsPrimordial = @object.GetValue(properties, "isprimordial"); + pool.IsReadOnly = @object.GetValue(properties, "isreadonly"); + pool.IsClustered = @object.GetValue(properties, "isclustered"); + pool.Size = @object.GetValue(properties, "size"); + pool.AllocatedSize = @object.GetValue(properties, "allocatedsize"); + pool.SectorSize = @object.GetValue(properties, "logicalsectorsize"); + + if (@object.GetValue(properties, "objectid") is string objectId) + { + pool.PhysicalDisks = QueryPhysicalDisksByStoragePool(objectId); + pool.VirtualDisks = QueryVirtualDisksByStoragePool(objectId); + } + + pools.Add(pool); + } + } + + return pools; + } + + private static List GetPhysicalDisks() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\microsoft\windows\storage"), + Query = new ObjectQuery("select objectid, uniqueid, name, friendlyname from msft_physicaldisk") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from msft_physicaldisk"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var disks = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var disk = new PhysicalDisk(); + + var properties = @object.GetPropertyHashes(); + + disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); + disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); + disk.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + disk.Model = @object.GetValue(properties, "model")?.Trim(); + disk.MediaType = @object.GetValue(properties, "mediatype"); + disk.BusType = @object.GetValue(properties, "bustype"); + + if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) + { + disk.States = operationals.Select(p => (PhysicalDisk.OperationalState)p).ToList(); + } + + disk.Health = (PhysicalDisk.HealthState)@object.GetValue(properties, "healthstatus"); + + if (@object.TryGetValue(properties, "supportedusages", out var supportedusages) && supportedusages is not null) + { + disk.SupportedUsages = supportedusages.Select(p => (SupportedUsagesEnum)p).ToList(); + } + + disk.Usage = @object.GetValue(properties, "usage"); + disk.PhysicalLocation = @object.GetValue(properties, "physicallocation")?.Trim(); + disk.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); + disk.FirmwareVersion = @object.GetValue(properties, "firmwareversion")?.Trim(); + disk.Size = @object.GetValue(properties, "size"); + disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); + disk.LogicalSectorSize = @object.GetValue(properties, "logicalsectorsize"); + disk.PhysicalSectorSize = @object.GetValue(properties, "physicalsectorsize"); + disk.VirtualDiskFootprint = @object.GetValue(properties, "virtualdiskfootprint"); + + disks.Add(disk); + } + } + + return disks; + } + + private static List GetVirtualDisks() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\microsoft\windows\storage"), + Query = new ObjectQuery("select objectid, uniqueid, name, friendlyname, access, provisioningtype, physicaldiskredundancy, resiliencysettingname, isdeduplicationenabled, issnapshot, operationalstatus, healthstatus, size, allocatedsize, footprintonpool, readcachesize, writecachesize from msft_virtualdisk") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from msft_virtualdisk"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var disks = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var disk = new VirtualDisk(); + + var properties = @object.GetPropertyHashes(); + + disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); + disk.Name = @object.GetValue(properties, "name")?.Trim(); + disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); + disk.AccessType = (AccessTypeEnum)@object.GetValue(properties, "access"); + disk.ProvisioningType = (ProvisioningTypeEnum)@object.GetValue(properties, "provisioningtype"); + disk.PhysicalDiskRedundancy = @object.GetValue(properties, "physicaldiskredundancy"); + disk.ResiliencySettingName = @object.GetValue(properties, "resiliencysettingname")?.Trim(); + disk.Deduplication = @object.GetValue(properties, "isdeduplicationenabled"); + disk.IsSnapshot = @object.GetValue(properties, "issnapshot"); + + if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) + { + disk.States = operationals.Select(p => (VirtualDisk.OperationalState)p).ToList(); + } + + disk.Health = (VirtualDisk.HealthState)@object.GetValue(properties, "healthstatus"); + disk.Size = @object.GetValue(properties, "size"); + disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); + disk.FootprintOnPool = @object.GetValue(properties, "footprintonpool"); + disk.ReadCacheSize = @object.GetValue(properties, "readcachesize"); + disk.WriteCacheSize = @object.GetValue(properties, "writecachesize"); + + disks.Add(disk); + } + } + + return disks; + } + + private static List QueryPhysicalDisksByStoragePool(string storagePoolObjectId) + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\microsoft\windows\storage"), + Query = new ObjectQuery("ASSOCIATORS OF {MSFT_StoragePool.ObjectId=\"" + Helpers.EscapeWql(storagePoolObjectId) + "\"} WHERE AssocClass = MSFT_StoragePoolToPhysicalDisk") + }; + + if (searcher.TryGet(out var collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + + var disks = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var disk = new PhysicalDisk(); + + var properties = @object.GetPropertyHashes(); + + disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); + disk.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); + disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); + disk.Manufacturer = @object.GetValue(properties, "manufacturer")?.Trim(); + disk.Model = @object.GetValue(properties, "model")?.Trim(); + disk.MediaType = @object.GetValue(properties, "mediatype"); + disk.BusType = @object.GetValue(properties, "bustype"); + + if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) + { + disk.States = operationals.Select(p => (PhysicalDisk.OperationalState)p).ToList(); + } + + disk.Health = (PhysicalDisk.HealthState)@object.GetValue(properties, "healthstatus"); + + if (@object.TryGetValue(properties, "supportedusages", out var supportedusages) && supportedusages is not null) + { + disk.SupportedUsages = supportedusages.Select(p => (SupportedUsagesEnum)p).ToList(); + } + + disk.Usage = @object.GetValue(properties, "usage"); + disk.PhysicalLocation = @object.GetValue(properties, "physicallocation")?.Trim(); + disk.SerialNumber = @object.GetValue(properties, "serialnumber")?.Trim(); + disk.FirmwareVersion = @object.GetValue(properties, "firmwareversion")?.Trim(); + disk.Size = @object.GetValue(properties, "size"); + disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); + disk.LogicalSectorSize = @object.GetValue(properties, "logicalsectorsize"); + disk.PhysicalSectorSize = @object.GetValue(properties, "physicalsectorsize"); + disk.VirtualDiskFootprint = @object.GetValue(properties, "virtualdiskfootprint"); + + disks.Add(disk); + } + } + + return disks; + } + + private static List QueryVirtualDisksByStoragePool(string storagePoolObjectId) + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\microsoft\windows\storage"), + Query = new ObjectQuery("ASSOCIATORS OF {MSFT_StoragePool.ObjectId=\"" + Helpers.EscapeWql(storagePoolObjectId) + "\"} WHERE AssocClass = MSFT_StoragePoolToVirtualDisk") + }; + + if (searcher.TryGet(out var collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + + var disks = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var disk = new VirtualDisk(); + + var properties = @object.GetPropertyHashes(); + + disk.UniqueId = @object.GetValue(properties, "uniqueid")?.Trim(); + disk.Name = @object.GetValue(properties, "name")?.Trim(); + disk.FriendlyName = @object.GetValue(properties, "friendlyname")?.Trim(); + disk.AccessType = (AccessTypeEnum)@object.GetValue(properties, "access"); + disk.ProvisioningType = (ProvisioningTypeEnum)@object.GetValue(properties, "provisioningtype"); + disk.PhysicalDiskRedundancy = @object.GetValue(properties, "physicaldiskredundancy"); + disk.ResiliencySettingName = @object.GetValue(properties, "resiliencysettingname")?.Trim(); + disk.Deduplication = @object.GetValue(properties, "isdeduplicationenabled"); + disk.IsSnapshot = @object.GetValue(properties, "issnapshot"); + + if (@object.TryGetValue(properties, "operationalstatus", out var operationals) && operationals is not null) + { + disk.States = operationals.Select(p => (VirtualDisk.OperationalState)p).ToList(); + } + + disk.Health = (VirtualDisk.HealthState)@object.GetValue(properties, "healthstatus"); + disk.Size = @object.GetValue(properties, "size"); + disk.AllocatedSize = @object.GetValue(properties, "allocatedsize"); + disk.FootprintOnPool = @object.GetValue(properties, "footprintonpool"); + disk.ReadCacheSize = @object.GetValue(properties, "readcachesize"); + disk.WriteCacheSize = @object.GetValue(properties, "writecachesize"); + + disks.Add(disk); + } + } + + return disks; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs index 6f9ee19..49269d7 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs @@ -1,175 +1,177 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Win32; using System.Collections; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class SystemInfoHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class SystemInfoHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { + case InventoryRequest: await sender.SendAsync(GetSystem(), cancellationToken); - } - } - - private static SystemInfo GetSystem() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select lastbootuptime, localdatetime, numberofprocesses from win32_operatingsystem") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_operatingsystem"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var system = new SystemInfo(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) - { - var properties = @object.GetPropertyHashes(); - - if (@object.TryGetValue(properties, "lastbootuptime", out var lastbootuptime)) - { - system.LastBootUpTime = ManagementDateTimeConverter.ToDateTime(lastbootuptime?.ToString()); - } - - if (@object.TryGetValue(properties, "localdatetime", out var localdatetime)) - { - system.LocalDateTime = ManagementDateTimeConverter.ToDateTime(localdatetime?.ToString()); - } - - system.Processes = @object.GetValue(properties, "numberofprocesses"); - - break; - } - } - - system.License = GetWindowsProductKeyFromRegistry(); - - return system; - } - - private static string GetWindowsProductKeyFromRegistry() - { - var localKey = - RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, Environment.Is64BitOperatingSystem - ? RegistryView.Registry64 - : RegistryView.Registry32); - - var registryKeyValue = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")?.GetValue("DigitalProductId"); - if (registryKeyValue == null) - return "Failed to get DigitalProductId from registry"; - var digitalProductId = (byte[])registryKeyValue; - localKey.Close(); - var isWin8OrUp = - Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2 - || - Environment.OSVersion.Version.Major > 6; - return GetWindowsProductKeyFromDigitalProductId(digitalProductId, - isWin8OrUp ? DigitalProductIdVersion.Windows8AndUp : DigitalProductIdVersion.UpToWindows7); - } - - private static string GetWindowsProductKeyFromDigitalProductId(byte[] digitalProductId, DigitalProductIdVersion digitalProductIdVersion) - { - - var productKey = digitalProductIdVersion == DigitalProductIdVersion.Windows8AndUp - ? DecodeProductKeyWin8AndUp(digitalProductId) - : DecodeProductKey(digitalProductId); - return productKey; - } - - private static string DecodeProductKey(byte[] digitalProductId) - { - const int keyStartIndex = 52; - const int keyEndIndex = keyStartIndex + 15; - var digits = new[] - { - 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R', - 'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9', - }; - const int decodeLength = 29; - const int decodeStringLength = 15; - var decodedChars = new char[decodeLength]; - var hexPid = new ArrayList(); - for (var i = keyStartIndex; i <= keyEndIndex; i++) - { - hexPid.Add(digitalProductId[i]); - } - for (var i = decodeLength - 1; i >= 0; i--) - { - // Every sixth char is a separator. - if ((i + 1) % 6 == 0) - { - decodedChars[i] = '-'; - } - else - { - // Do the actual decoding. - var digitMapIndex = 0; - for (var j = decodeStringLength - 1; j >= 0; j--) - { - var byteValue = digitMapIndex << 8 | (byte)hexPid[j]; - hexPid[j] = (byte)(byteValue / 24); - digitMapIndex = byteValue % 24; - decodedChars[i] = digits[digitMapIndex]; - } - } - } - return new string(decodedChars); - } - - private static string DecodeProductKeyWin8AndUp(byte[] digitalProductId) - { - var key = string.Empty; - const int keyOffset = 52; - var isWin8 = (byte)(digitalProductId[66] / 6 & 1); - digitalProductId[66] = (byte)(digitalProductId[66] & 0xf7 | (isWin8 & 2) * 4); - - const string digits = "BCDFGHJKMPQRTVWXY2346789"; - var last = 0; - for (var i = 24; i >= 0; i--) - { - var current = 0; - for (var j = 14; j >= 0; j--) - { - current = current * 256; - current = digitalProductId[j + keyOffset] + current; - digitalProductId[j + keyOffset] = (byte)(current / 24); - current = current % 24; - last = current; - } - key = digits[current] + key; - } - - var keypart1 = key.Substring(1, last); - var keypart2 = key.Substring(last + 1, key.Length - (last + 1)); - key = keypart1 + "N" + keypart2; - - for (var i = 5; i < key.Length; i += 6) - { - key = key.Insert(i, "-"); - } - - return key; - } - - private enum DigitalProductIdVersion - { - UpToWindows7, - Windows8AndUp + break; } } + + private static SystemInfo GetSystem() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select lastbootuptime, localdatetime, numberofprocesses from win32_operatingsystem") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_operatingsystem"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var system = new SystemInfo(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var properties = @object.GetPropertyHashes(); + + if (@object.TryGetValue(properties, "lastbootuptime", out var lastbootuptime)) + { + system.LastBootUpTime = ManagementDateTimeConverter.ToDateTime(lastbootuptime?.ToString()); + } + + if (@object.TryGetValue(properties, "localdatetime", out var localdatetime)) + { + system.LocalDateTime = ManagementDateTimeConverter.ToDateTime(localdatetime?.ToString()); + } + + system.Processes = @object.GetValue(properties, "numberofprocesses"); + + break; + } + } + + system.License = GetWindowsProductKeyFromRegistry(); + + return system; + } + + private static string GetWindowsProductKeyFromRegistry() + { + var localKey = + RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, Environment.Is64BitOperatingSystem + ? RegistryView.Registry64 + : RegistryView.Registry32); + + var registryKeyValue = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")?.GetValue("DigitalProductId"); + if (registryKeyValue == null) + return "Failed to get DigitalProductId from registry"; + var digitalProductId = (byte[])registryKeyValue; + localKey.Close(); + var isWin8OrUp = + Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2 + || + Environment.OSVersion.Version.Major > 6; + return GetWindowsProductKeyFromDigitalProductId(digitalProductId, + isWin8OrUp ? DigitalProductIdVersion.Windows8AndUp : DigitalProductIdVersion.UpToWindows7); + } + + private static string GetWindowsProductKeyFromDigitalProductId(byte[] digitalProductId, DigitalProductIdVersion digitalProductIdVersion) + { + + var productKey = digitalProductIdVersion == DigitalProductIdVersion.Windows8AndUp + ? DecodeProductKeyWin8AndUp(digitalProductId) + : DecodeProductKey(digitalProductId); + return productKey; + } + + private static string DecodeProductKey(byte[] digitalProductId) + { + const int keyStartIndex = 52; + const int keyEndIndex = keyStartIndex + 15; + var digits = new[] + { + 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R', + 'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9', + }; + const int decodeLength = 29; + const int decodeStringLength = 15; + var decodedChars = new char[decodeLength]; + var hexPid = new ArrayList(); + for (var i = keyStartIndex; i <= keyEndIndex; i++) + { + hexPid.Add(digitalProductId[i]); + } + for (var i = decodeLength - 1; i >= 0; i--) + { + // Every sixth char is a separator. + if ((i + 1) % 6 == 0) + { + decodedChars[i] = '-'; + } + else + { + // Do the actual decoding. + var digitMapIndex = 0; + for (var j = decodeStringLength - 1; j >= 0; j--) + { + var byteValue = digitMapIndex << 8 | (byte)hexPid[j]; + hexPid[j] = (byte)(byteValue / 24); + digitMapIndex = byteValue % 24; + decodedChars[i] = digits[digitMapIndex]; + } + } + } + return new string(decodedChars); + } + + private static string DecodeProductKeyWin8AndUp(byte[] digitalProductId) + { + var key = string.Empty; + const int keyOffset = 52; + var isWin8 = (byte)(digitalProductId[66] / 6 & 1); + digitalProductId[66] = (byte)(digitalProductId[66] & 0xf7 | (isWin8 & 2) * 4); + + const string digits = "BCDFGHJKMPQRTVWXY2346789"; + var last = 0; + for (var i = 24; i >= 0; i--) + { + var current = 0; + for (var j = 14; j >= 0; j--) + { + current = current * 256; + current = digitalProductId[j + keyOffset] + current; + digitalProductId[j + keyOffset] = (byte)(current / 24); + current = current % 24; + last = current; + } + key = digits[current] + key; + } + + var keypart1 = key.Substring(1, last); + var keypart2 = key.Substring(last + 1, key.Length - (last + 1)); + key = keypart1 + "N" + keypart2; + + for (var i = 5; i < key.Length; i += 6) + { + key = key.Insert(i, "-"); + } + + return key; + } + + private enum DigitalProductIdVersion + { + UpToWindows7, + Windows8AndUp + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs index 287c181..80454a2 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs @@ -1,130 +1,128 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Runtime.Versioning; using System.Text.RegularExpressions; using WUApiLib; -using static Insight.Agent.Messages.Update; +using static Insight.Domain.Network.Agent.Messages.Update; +using UpdateCollection = Insight.Domain.Network.Agent.Messages.UpdateCollection; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class UpdateHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class UpdateHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - await sender.SendAsync(GetUpdates(), cancellationToken); - } - } - - private static UpdateList GetUpdates() - { - return new UpdateList - { - Installed = QueryInstalledUpdates(), - Pending = QueryPendingUpdates() - }; - } - - private static List QueryInstalledUpdates() - { - var updates = new List(); - - var session = new UpdateSessionClass(); - var searcher = session.CreateUpdateSearcher(); - searcher.Online = false; - - var count = searcher.GetTotalHistoryCount(); - var result = searcher.QueryHistory(0, count); - - foreach (IUpdateHistoryEntry wupdate in result) - { - var update = new Update + case InventoryRequest: + await sender.SendAsync(new UpdateCollection { - Id = wupdate.UpdateIdentity.UpdateID, - Date = wupdate.Date, - Name = wupdate.Title, - Description = wupdate.Description, - Result = wupdate.ResultCode switch - { - OperationResultCode.orcNotStarted => OsUpdateResultCodeEnum.NotStarted, - OperationResultCode.orcInProgress => OsUpdateResultCodeEnum.InProgress, - OperationResultCode.orcSucceeded => OsUpdateResultCodeEnum.Succeeded, - OperationResultCode.orcSucceededWithErrors => OsUpdateResultCodeEnum.SucceededWithErrors, - OperationResultCode.orcFailed => OsUpdateResultCodeEnum.Failed, - OperationResultCode.orcAborted => OsUpdateResultCodeEnum.Aborted, - _ => null - }, - SupportUrl = wupdate.SupportUrl, - }; - - try - { - var rx = new Regex(@"KB(\d+)"); - update.Hotfix = rx.Match(wupdate.Title).Value; - } - catch (Exception) - { - - } - - updates.Add(update); - } - - return updates; - } - - private static List QueryPendingUpdates() - { - var updates = new List(); - - var session = new UpdateSessionClass(); - var searcher = session.CreateUpdateSearcher(); - searcher.Online = true; - - var result = searcher.Search("IsInstalled=0"); - - foreach (IUpdate wupdate in result.Updates) - { - var update = new Update - { - Id = wupdate.Identity.UpdateID, - Type = wupdate.Type switch - { - UpdateType.utSoftware => OsUpdateTypeEnum.Software, - UpdateType.utDriver => OsUpdateTypeEnum.Driver, - _ => null - }, - Date = wupdate.LastDeploymentChangeTime, - Name = wupdate.Title, - Description = wupdate.Description, - SupportUrl = wupdate.SupportUrl, - Size = wupdate.MaxDownloadSize, - IsDownloaded = wupdate.IsDownloaded, - CanRequestUserInput = wupdate.InstallationBehavior.CanRequestUserInput, - RebootBehavior = wupdate.InstallationBehavior.RebootBehavior switch - { - InstallationRebootBehavior.irbNeverReboots => OsUpdateRebootBehaviorEnum.NeverReboots, - InstallationRebootBehavior.irbAlwaysRequiresReboot => OsUpdateRebootBehaviorEnum.AlwaysRequiresReboot, - InstallationRebootBehavior.irbCanRequestReboot => OsUpdateRebootBehaviorEnum.CanRequestReboot, - _ => null - }, - }; - - if (wupdate.KBArticleIDs.Count > 0) - { - foreach (var id in wupdate.KBArticleIDs) - { - update.Hotfix = $"KB{id}"; - break; - } - } - - updates.Add(update); - } - - return updates; + Installed = QueryInstalledUpdates(), + Pending = QueryPendingUpdates() + }, cancellationToken); + break; } } + + private static List QueryInstalledUpdates() + { + var updates = new List(); + + var session = new UpdateSessionClass(); + var searcher = session.CreateUpdateSearcher(); + searcher.Online = false; + + var count = searcher.GetTotalHistoryCount(); + var result = searcher.QueryHistory(0, count); + + foreach (IUpdateHistoryEntry wupdate in result) + { + var update = new Update + { + Id = wupdate.UpdateIdentity.UpdateID, + Date = wupdate.Date, + Name = wupdate.Title, + Description = wupdate.Description, + Result = wupdate.ResultCode switch + { + OperationResultCode.orcNotStarted => OsUpdateResultCodeEnum.NotStarted, + OperationResultCode.orcInProgress => OsUpdateResultCodeEnum.InProgress, + OperationResultCode.orcSucceeded => OsUpdateResultCodeEnum.Succeeded, + OperationResultCode.orcSucceededWithErrors => OsUpdateResultCodeEnum.SucceededWithErrors, + OperationResultCode.orcFailed => OsUpdateResultCodeEnum.Failed, + OperationResultCode.orcAborted => OsUpdateResultCodeEnum.Aborted, + _ => null + }, + SupportUrl = wupdate.SupportUrl, + }; + + try + { + var rx = new Regex(@"KB(\d+)"); + update.Hotfix = rx.Match(wupdate.Title).Value; + } + catch (Exception) + { + + } + + updates.Add(update); + } + + return updates; + } + + private static List QueryPendingUpdates() + { + var updates = new List(); + + var session = new UpdateSessionClass(); + var searcher = session.CreateUpdateSearcher(); + searcher.Online = true; + + var result = searcher.Search("IsInstalled=0"); + + foreach (IUpdate wupdate in result.Updates) + { + var update = new Update + { + Id = wupdate.Identity.UpdateID, + Type = wupdate.Type switch + { + UpdateType.utSoftware => OsUpdateTypeEnum.Software, + UpdateType.utDriver => OsUpdateTypeEnum.Driver, + _ => null + }, + Date = wupdate.LastDeploymentChangeTime, + Name = wupdate.Title, + Description = wupdate.Description, + SupportUrl = wupdate.SupportUrl, + Size = wupdate.MaxDownloadSize, + IsDownloaded = wupdate.IsDownloaded, + CanRequestUserInput = wupdate.InstallationBehavior.CanRequestUserInput, + RebootBehavior = wupdate.InstallationBehavior.RebootBehavior switch + { + InstallationRebootBehavior.irbNeverReboots => OsUpdateRebootBehaviorEnum.NeverReboots, + InstallationRebootBehavior.irbAlwaysRequiresReboot => OsUpdateRebootBehaviorEnum.AlwaysRequiresReboot, + InstallationRebootBehavior.irbCanRequestReboot => OsUpdateRebootBehaviorEnum.CanRequestReboot, + _ => null + }, + }; + + if (wupdate.KBArticleIDs.Count > 0) + { + foreach (var id in wupdate.KBArticleIDs) + { + update.Hotfix = $"KB{id}"; + break; + } + } + + updates.Add(update); + } + + return updates; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs index b232e3a..76b5602 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs @@ -1,188 +1,192 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class UserHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class UserHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new UserList(); - result.AddRange(GetUsers()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetUsers() - { - var users = QueryUsers(); - var groups = GetGroups(); - var usergrouping = QueryUserGroupMaps(); - - foreach (var u in users) - { - u.Groups = new List(); - - foreach (var ug in usergrouping.Where(ug => ug.UserDomain == u.Domain && ug.UserName == u.Name)) + case InventoryRequest: { - var grps = groups.Where(g => g.Domain == ug.GroupDomain && g.Name == ug.GroupName); + var result = new Collection(); + result.AddRange(GetUsers()); - if (grps is not null) - { - u.Groups.AddRange(grps); - } + await sender.SendAsync(result, cancellationToken); + break; } - } - - return users; - } - - private static List GetGroups() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select sid, domain, name, description, localaccount from win32_group") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_group"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var groups = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var group = new Group(); - - var properties = @object.GetPropertyHashes(); - - group.Sid = @object.GetValue(properties, "sid")?.Trim(); - group.Domain = @object.GetValue(properties, "domain")?.Trim(); - group.Name = @object.GetValue(properties, "name")?.Trim(); - group.Description = @object.GetValue(properties, "description")?.Trim(); - group.LocalAccount = @object.GetValue(properties, "localaccount"); - - groups.Add(group); - } - } - - return groups.OrderBy(x => x.Name)?.ToList(); - } - - private static List QueryUsers() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select sid, name, fullname, description, domain, localaccount, disabled, lockout, status, passwordchangeable, passwordexpires, passwordrequired from win32_useraccount") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_useraccount"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var users = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var user = new User(); - - var properties = @object.GetPropertyHashes(); - - user.Sid = @object.GetValue(properties, "sid")?.Trim(); - user.Name = @object.GetValue(properties, "name")?.Trim(); - user.FullName = @object.GetValue(properties, "fullname")?.Trim(); - user.Description = @object.GetValue(properties, "description")?.Trim(); - user.Domain = @object.GetValue(properties, "domain")?.Trim(); - user.LocalAccount = @object.GetValue(properties, "localaccount"); - user.Disabled = @object.GetValue(properties, "disabled"); - user.Lockout = @object.GetValue(properties, "lockout"); - user.Status = @object.GetValue(properties, "status")?.Trim(); - user.PasswordChangeable = @object.GetValue(properties, "passwordchangeable"); - user.PasswordExpires = @object.GetValue(properties, "passwordexpires"); - user.PasswordRequired = @object.GetValue(properties, "passwordrequired"); - - users.Add(user); - } - } - - return users; - } - - private static List QueryUserGroupMaps() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select groupcomponent, partcomponent from win32_groupuser") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_groupuser"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var usergroups = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection) - { - var usergroup = new UserGroupMap(); - - var properties = @object.GetPropertyHashes(); - - var raw = @object.GetValue(properties, "groupcomponent"); - var split = raw?.Split(".Domain=")[1]?.Split(",Name="); - - if (split is not null && split.Length > 1) - { - usergroup.GroupDomain = split[0].TrimStart('"').TrimEnd('"'); - usergroup.GroupName = split[1].TrimStart('"').TrimEnd('"'); - } - - raw = @object.GetValue(properties, "partcomponent"); - split = raw?.Split(".Domain=")[1]?.Split(",Name="); - - if (split is not null && split.Length > 1) - { - usergroup.UserDomain = split[0].TrimStart('"').TrimEnd('"'); - usergroup.UserName = split[1].TrimStart('"').TrimEnd('"'); - } - - usergroups.Add(usergroup); - } - } - - return usergroups; - } - - private class UserGroupMap - { - public string? GroupDomain { get; set; } - public string? GroupName { get; set; } - - public string? UserDomain { get; set; } - public string? UserName { get; set; } } } + + private static List GetUsers() + { + var users = QueryUsers(); + var groups = GetGroups(); + var usergrouping = QueryUserGroupMaps(); + + foreach (var u in users) + { + u.Groups = new List(); + + foreach (var ug in usergrouping.Where(ug => ug.UserDomain == u.Domain && ug.UserName == u.Name)) + { + var grps = groups.Where(g => g.Domain == ug.GroupDomain && g.Name == ug.GroupName); + + if (grps is not null) + { + u.Groups.AddRange(grps); + } + } + } + + return users; + } + + private static List GetGroups() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select sid, domain, name, description, localaccount from win32_group") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_group"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var groups = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var group = new Group(); + + var properties = @object.GetPropertyHashes(); + + group.Sid = @object.GetValue(properties, "sid")?.Trim(); + group.Domain = @object.GetValue(properties, "domain")?.Trim(); + group.Name = @object.GetValue(properties, "name")?.Trim(); + group.Description = @object.GetValue(properties, "description")?.Trim(); + group.LocalAccount = @object.GetValue(properties, "localaccount"); + + groups.Add(group); + } + } + + return groups.OrderBy(x => x.Name)?.ToList(); + } + + private static List QueryUsers() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select sid, name, fullname, description, domain, localaccount, disabled, lockout, status, passwordchangeable, passwordexpires, passwordrequired from win32_useraccount") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_useraccount"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var users = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var user = new User(); + + var properties = @object.GetPropertyHashes(); + + user.Sid = @object.GetValue(properties, "sid")?.Trim(); + user.Name = @object.GetValue(properties, "name")?.Trim(); + user.FullName = @object.GetValue(properties, "fullname")?.Trim(); + user.Description = @object.GetValue(properties, "description")?.Trim(); + user.Domain = @object.GetValue(properties, "domain")?.Trim(); + user.LocalAccount = @object.GetValue(properties, "localaccount"); + user.Disabled = @object.GetValue(properties, "disabled"); + user.Lockout = @object.GetValue(properties, "lockout"); + user.Status = @object.GetValue(properties, "status")?.Trim(); + user.PasswordChangeable = @object.GetValue(properties, "passwordchangeable"); + user.PasswordExpires = @object.GetValue(properties, "passwordexpires"); + user.PasswordRequired = @object.GetValue(properties, "passwordrequired"); + + users.Add(user); + } + } + + return users; + } + + private static List QueryUserGroupMaps() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select groupcomponent, partcomponent from win32_groupuser") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_groupuser"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var usergroups = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection) + { + var usergroup = new UserGroupMap(); + + var properties = @object.GetPropertyHashes(); + + var raw = @object.GetValue(properties, "groupcomponent"); + var split = raw?.Split(".Domain=")[1]?.Split(",Name="); + + if (split is not null && split.Length > 1) + { + usergroup.GroupDomain = split[0].TrimStart('"').TrimEnd('"'); + usergroup.GroupName = split[1].TrimStart('"').TrimEnd('"'); + } + + raw = @object.GetValue(properties, "partcomponent"); + split = raw?.Split(".Domain=")[1]?.Split(",Name="); + + if (split is not null && split.Length > 1) + { + usergroup.UserDomain = split[0].TrimStart('"').TrimEnd('"'); + usergroup.UserName = split[1].TrimStart('"').TrimEnd('"'); + } + + usergroups.Add(usergroup); + } + } + + return usergroups; + } + + private class UserGroupMap + { + public string? GroupDomain { get; set; } + public string? GroupName { get; set; } + + public string? UserDomain { get; set; } + public string? UserName { get; set; } + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs index 0e8f1bb..078c36a 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs @@ -1,64 +1,68 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class VideocardHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class VideocardHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new VideocardList(); - result.AddRange(GetVideocards()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetVideocards() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\cimv2"), - Query = new ObjectQuery("select deviceid, name, adapterram, driverdate, driverversion from win32_videocontroller") - }; - - if (searcher.TryGet(out var collection) is false) - { - searcher.Query = new ObjectQuery("select * from win32_videocontroller"); - - if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var videocards = new List(); - - using (collection) - { - foreach (ManagementObject @object in collection.Cast()) + case InventoryRequest: { - var videocard = new Videocard(); + var result = new Collection(); + result.AddRange(GetVideocards()); - var properties = @object.GetPropertyHashes(); - - videocard.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); - videocard.Model = @object.GetValue(properties, "name")?.Trim(); - - if (@object.TryGetValue(properties, "driverdate", out var driverdate)) - { - videocard.DriverDate = ManagementDateTimeConverter.ToDateTime(driverdate?.ToString()); - } - - videocard.DriverVersion = @object.GetValue(properties, "driverversion")?.Trim(); - - videocards.Add(videocard); + await sender.SendAsync(result, cancellationToken); + break; } - } - - return videocards; } } + + private static List GetVideocards() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\cimv2"), + Query = new ObjectQuery("select deviceid, name, adapterram, driverdate, driverversion from win32_videocontroller") + }; + + if (searcher.TryGet(out var collection) is false) + { + searcher.Query = new ObjectQuery("select * from win32_videocontroller"); + + if (searcher.TryGet(out collection) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var videocards = new List(); + + using (collection) + { + foreach (ManagementObject @object in collection.Cast()) + { + var videocard = new Videocard(); + + var properties = @object.GetPropertyHashes(); + + videocard.DeviceId = @object.GetValue(properties, "deviceid")?.Trim(); + videocard.Model = @object.GetValue(properties, "name")?.Trim(); + + if (@object.TryGetValue(properties, "driverdate", out var driverdate)) + { + videocard.DriverDate = ManagementDateTimeConverter.ToDateTime(driverdate?.ToString()); + } + + videocard.DriverVersion = @object.GetValue(properties, "driverversion")?.Trim(); + + videocards.Add(videocard); + } + } + + return videocards; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs index a6cc0ff..f8c843f 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs @@ -1,359 +1,363 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -using static Insight.Agent.Messages.VirtualMaschine; -using static Insight.Agent.Messages.VirtualMaschineConfiguration; +using static Insight.Domain.Network.Agent.Messages.VirtualMaschine; +using static Insight.Domain.Network.Agent.Messages.VirtualMaschineConfiguration; -namespace Insight.Agent.Network.Handlers +namespace Insight.Agent.Network.Handlers; + +[SupportedOSPlatform("windows")] +public class VirtualMaschineHandler : IMessageHandler { - [SupportedOSPlatform("windows")] - public class VirtualMaschineHandler : IAgentMessageHandler + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + switch (message) { - if (message is GetInventory) - { - var result = new VirtualMaschineList(); - result.AddRange(GetVirtualMaschines()); - - await sender.SendAsync(result, cancellationToken); - } - } - - private static List GetVirtualMaschines() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\virtualization\v2"), - Query = new ObjectQuery("select * msvm_computersystem") - }; - - if (searcher.TryGet(out var computersystems) is false) - { - searcher.Query = new ObjectQuery("select * from msvm_computersystem"); - - if (searcher.TryGet(out computersystems) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var vms = new List(); - - using (computersystems) - { - foreach (ManagementObject cs in computersystems.Cast()) + case InventoryRequest: { - var vm = new VirtualMaschine(); + var result = new Collection(); + result.AddRange(GetVirtualMaschines()); - var csProperties = cs.GetPropertyHashes(); - - var vmId = cs.GetValue(csProperties, "name")?.Trim(); - - if (Guid.TryParse(vmId, out var vmGuid) is false) continue; - - vm.Id = vmGuid; - vm.ProcessId = cs.GetValue(csProperties, "processid"); - vm.Caption = cs.GetValue(csProperties, "caption")?.Trim(); - vm.Name = cs.GetValue(csProperties, "elementname")?.Trim(); - vm.Enabled = (EnabledEnum)cs.GetValue(csProperties, "enabledstate"); - vm.EnabledDefault = (EnabledDefaultEnum)cs.GetValue(csProperties, "enableddefault"); - vm.HealthState = (HealthStatusEnum)cs.GetValue(csProperties, "healthstate"); - vm.Status = cs.GetValue(csProperties, "status")?.Trim(); - vm.OnTime = cs.GetValue(csProperties, "ontimeinmilliseconds"); - vm.ReplicationMode = cs.GetValue(csProperties, "replicationmode"); - vm.ReplicationState = (ReplicationStateEnum)cs.GetValue(csProperties, "replicationstate"); - vm.ReplicationHealth = (ReplicationHealthEnum)cs.GetValue(csProperties, "replicationhealth"); - - if (cs.TryGetValue(csProperties, "installdate", out var installdate)) - { - vm.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString()); - } - - if (cs.TryGetValue(csProperties, "timeoflastconfigurationchange", out var timeoflastconfigurationchange)) - { - vm.TimeOfLastConfigurationChange = ManagementDateTimeConverter.ToDateTime(timeoflastconfigurationchange?.ToString()); - } - - if (cs.TryGetValue(csProperties, "timeoflaststatechange", out var timeoflaststatechange)) - { - vm.TimeOfLastStateChange = ManagementDateTimeConverter.ToDateTime(timeoflaststatechange?.ToString()); - } - - if (cs.TryGetValue(csProperties, "lastreplicationtime", out var lastreplicationtime)) - { - vm.LastReplicationTime = ManagementDateTimeConverter.ToDateTime(lastreplicationtime?.ToString()); - } - - var summaryinformation = cs.GetRelated("msvm_summaryinformation"); - using (summaryinformation) - { - foreach (ManagementObject si in summaryinformation.Cast()) - { - var siProperties = si.GetPropertyHashes(); - - vm.Notes = si.GetValue(siProperties, "Notes"); - vm.ConfigurationVersion = si.GetValue(siProperties, "Version"); - vm.IntegrationServicesVersionState = (IntegrationServicesVersionStateEnum)si.GetValue(siProperties, "IntegrationServicesVersionState"); - vm.GuestOperatingSystem = si.GetValue(siProperties, "GuestOperatingSystem"); - vm.NumberOfProcessors = si.GetValue(siProperties, "NumberOfProcessors"); - vm.ProcessorLoad = si.GetValue(siProperties, "ProcessorLoad"); - vm.MemoryAvailable = si.GetValue(siProperties, "MemoryAvailable"); - vm.MemoryUsage = si.GetValue(siProperties, "MemoryUsage"); - } - } - - var virtualSystemSettingData = cs.GetRelated("Msvm_VirtualSystemSettingData"); - using (virtualSystemSettingData) - { - var configs = new List(); - - foreach (ManagementObject vssd in virtualSystemSettingData.Cast()) - { - var vmc = new VirtualMaschineConfiguration(); - - var vssdProperties = vssd.GetPropertyHashes(); - - var vmcId = vssd.GetValue(vssdProperties, "ConfigurationID")?.Trim(); - - if (Guid.TryParse(vmcId, out var vmcGuid) is false) continue; - - vmc.Id = vmcGuid.ToString(); - - vmc.Type = vssd.GetValue(vssdProperties, "VirtualSystemType"); - vmc.Name = vssd.GetValue(vssdProperties, "ElementName"); - - if (vssd.TryGetValue(vssdProperties, "CreationTime", out var creationtime)) - { - vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(creationtime?.ToString()); - } - - vmc.Generation = vssd.GetValue(vssdProperties, "VirtualSystemSubType"); - vmc.Architecture = vssd.GetValue(vssdProperties, "Architecture"); - vmc.AutomaticStartupAction = (AutomaticStartupActionEnum)vssd.GetValue(vssdProperties, "AutomaticStartupAction"); - vmc.AutomaticShutdownAction = (AutomaticShutdownActionEnum)vssd.GetValue(vssdProperties, "AutomaticShutdownAction"); - vmc.AutomaticRecoveryAction = (AutomaticRecoveryActionEnum)vssd.GetValue(vssdProperties, "AutomaticRecoveryAction"); - vmc.AutomaticSnapshotsEnabled = vssd.GetValue(vssdProperties, "AutomaticSnapshotsEnabled"); - - //if (vssd.TryGetValue(vssdProperties, "AutomaticStartupActionDelay", out var automaticstartupactiondelay)) - //{ - // vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(automaticstartupactiondelay?.ToString()); - //} - - vmc.BaseBoardSerialNumber = vssd.GetValue(vssdProperties, "BaseBoardSerialNumber"); - vmc.BIOSGUID = vssd.GetValue(vssdProperties, "BIOSGUID"); - vmc.BIOSSerialNumber = vssd.GetValue(vssdProperties, "BIOSSerialNumber"); - vmc.BootOrder = vssd.GetValue(vssdProperties, "BootOrder"); - vmc.ConfigurationDataRoot = vssd.GetValue(vssdProperties, "ConfigurationDataRoot"); - vmc.ConfigurationFile = vssd.GetValue(vssdProperties, "ConfigurationFile"); - vmc.GuestStateDataRoot = vssd.GetValue(vssdProperties, "GuestStateDataRoot"); - vmc.GuestStateFile = vssd.GetValue(vssdProperties, "GuestStateFile"); - vmc.SnapshotDataRoot = vssd.GetValue(vssdProperties, "SnapshotDataRoot"); - vmc.SuspendDataRoot = vssd.GetValue(vssdProperties, "SuspendDataRoot"); - vmc.SwapFileDataRoot = vssd.GetValue(vssdProperties, "SwapFileDataRoot"); - vmc.SecureBootEnabled = vssd.GetValue(vssdProperties, "SecureBootEnabled"); - vmc.IsAutomaticSnapshot = vssd.GetValue(vssdProperties, "IsAutomaticSnapshot"); - vmc.Notes = vssd.GetValue(vssdProperties, "Notes"); - - if (vssd.GetValue(vssdProperties, "Parent") is string parent) - { - using var vmcp = new ManagementObject(parent); - vmcp.Get(); - - if (Guid.TryParse(vmcp["ConfigurationID"]?.ToString(), out var parentGuid) is false) continue; - vmc.ParentId = parentGuid.ToString(); - } - - //var storageallocationsettingdata = cs.GetRelated("Msvm_StorageAllocationSettingData"); - //using (storageallocationsettingdata) - //{ - - //} - - configs.Add(vmc); - } - - vm.Configurations = configs.GroupBy(p => p.Id).Select(p => p.First()).ToList(); - } - - vms.Add(vm); + await sender.SendAsync(result, cancellationToken); + break; } - } - - return vms; - } - - private static List QueryVirtualMaschines0() - { - using var searcher = new ManagementObjectSearcher - { - Scope = new ManagementScope(@"root\virtualization\v2"), - Query = new ObjectQuery("select * msvm_computersystem") - }; - - if (searcher.TryGet(out var computersystems) is false) - { - searcher.Query = new ObjectQuery("select * from msvm_computersystem"); - - if (searcher.TryGet(out computersystems) is false) throw new InvalidOperationException("WMI Collection NULL"); - } - - var vms = new List(); - - using (computersystems) - { - foreach (ManagementObject cs in computersystems.Cast()) - { - var vm = new VirtualMaschine(); - - var csProperties = cs.GetPropertyHashes(); - - var vmId = cs.GetValue(csProperties, "name")?.Trim(); - - if (Guid.TryParse(vmId, out var vmGuid) is false) continue; - - vm.Id = vmGuid; - vm.ProcessId = cs.GetValue(csProperties, "processid"); - vm.Caption = cs.GetValue(csProperties, "caption")?.Trim(); - vm.Name = cs.GetValue(csProperties, "elementname")?.Trim(); - vm.Enabled = (EnabledEnum)cs.GetValue(csProperties, "enabledstate"); - vm.EnabledDefault = (EnabledDefaultEnum)cs.GetValue(csProperties, "enableddefault"); - vm.HealthState = (HealthStatusEnum)cs.GetValue(csProperties, "healthstate"); - vm.Status = cs.GetValue(csProperties, "status")?.Trim(); - vm.OnTime = cs.GetValue(csProperties, "ontimeinmilliseconds"); - vm.ReplicationMode = cs.GetValue(csProperties, "replicationmode"); - vm.ReplicationState = (ReplicationStateEnum)cs.GetValue(csProperties, "replicationstate"); - vm.ReplicationHealth = (ReplicationHealthEnum)cs.GetValue(csProperties, "replicationhealth"); - - if (cs.TryGetValue(csProperties, "installdate", out var installdate)) - { - vm.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString()); - } - - if (cs.TryGetValue(csProperties, "timeoflastconfigurationchange", out var timeoflastconfigurationchange)) - { - vm.TimeOfLastConfigurationChange = ManagementDateTimeConverter.ToDateTime(timeoflastconfigurationchange?.ToString()); - } - - if (cs.TryGetValue(csProperties, "timeoflaststatechange", out var timeoflaststatechange)) - { - vm.TimeOfLastStateChange = ManagementDateTimeConverter.ToDateTime(timeoflaststatechange?.ToString()); - } - - if (cs.TryGetValue(csProperties, "lastreplicationtime", out var lastreplicationtime)) - { - vm.LastReplicationTime = ManagementDateTimeConverter.ToDateTime(lastreplicationtime?.ToString()); - } - - var summaryinformation = cs.GetRelated("msvm_summaryinformation"); - using (summaryinformation) - { - foreach (ManagementObject si in summaryinformation.Cast()) - { - var siProperties = si.GetPropertyHashes(); - - vm.Notes = si.GetValue(siProperties, "Notes"); - vm.ConfigurationVersion = si.GetValue(siProperties, "Version"); - vm.IntegrationServicesVersionState = (IntegrationServicesVersionStateEnum)si.GetValue(siProperties, "IntegrationServicesVersionState"); - vm.GuestOperatingSystem = si.GetValue(siProperties, "GuestOperatingSystem"); - vm.NumberOfProcessors = si.GetValue(siProperties, "NumberOfProcessors"); - vm.ProcessorLoad = si.GetValue(siProperties, "ProcessorLoad"); - vm.MemoryAvailable = si.GetValue(siProperties, "MemoryAvailable"); - vm.MemoryUsage = si.GetValue(siProperties, "MemoryUsage"); - } - } - - var virtualSystemSettingData = cs.GetRelated("Msvm_VirtualSystemSettingData"); - using (virtualSystemSettingData) - { - var configs = new List(); - - foreach (ManagementObject vssd in virtualSystemSettingData.Cast()) - { - var vmc = new VirtualMaschineConfiguration(); - - var vssdProperties = vssd.GetPropertyHashes(); - - var vmcId = vssd.GetValue(vssdProperties, "ConfigurationID")?.Trim(); - - if (Guid.TryParse(vmcId, out var vmcGuid) is false) continue; - - vmc.Id = vmcGuid.ToString(); - - vmc.Type = vssd.GetValue(vssdProperties, "VirtualSystemType"); - vmc.Name = vssd.GetValue(vssdProperties, "ElementName"); - - if (vssd.TryGetValue(vssdProperties, "CreationTime", out var creationtime)) - { - vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(creationtime?.ToString()); - } - - vmc.Generation = vssd.GetValue(vssdProperties, "VirtualSystemSubType"); - vmc.Architecture = vssd.GetValue(vssdProperties, "Architecture"); - vmc.AutomaticStartupAction = (AutomaticStartupActionEnum)vssd.GetValue(vssdProperties, "AutomaticStartupAction"); - - //if (vssd.TryGetValue(vssdProperties, "AutomaticStartupActionDelay", out var automaticstartupactiondelay)) - //{ - // vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(automaticstartupactiondelay?.ToString()); - //} - - vmc.AutomaticShutdownAction = (AutomaticShutdownActionEnum)vssd.GetValue(vssdProperties, "AutomaticShutdownAction"); - vmc.AutomaticRecoveryAction = (AutomaticRecoveryActionEnum)vssd.GetValue(vssdProperties, "AutomaticRecoveryAction"); - vmc.AutomaticSnapshotsEnabled = vssd.GetValue(vssdProperties, "AutomaticSnapshotsEnabled"); - - vmc.BaseBoardSerialNumber = vssd.GetValue(vssdProperties, "BaseBoardSerialNumber"); - vmc.BIOSGUID = vssd.GetValue(vssdProperties, "BIOSGUID"); - vmc.BIOSSerialNumber = vssd.GetValue(vssdProperties, "BIOSSerialNumber"); - vmc.BootOrder = vssd.GetValue(vssdProperties, "BootOrder"); - vmc.ConfigurationDataRoot = vssd.GetValue(vssdProperties, "ConfigurationDataRoot"); - vmc.ConfigurationFile = vssd.GetValue(vssdProperties, "ConfigurationFile"); - vmc.GuestStateDataRoot = vssd.GetValue(vssdProperties, "GuestStateDataRoot"); - vmc.GuestStateFile = vssd.GetValue(vssdProperties, "GuestStateFile"); - vmc.SnapshotDataRoot = vssd.GetValue(vssdProperties, "SnapshotDataRoot"); - vmc.SuspendDataRoot = vssd.GetValue(vssdProperties, "SuspendDataRoot"); - vmc.SwapFileDataRoot = vssd.GetValue(vssdProperties, "SwapFileDataRoot"); - vmc.SecureBootEnabled = vssd.GetValue(vssdProperties, "SecureBootEnabled"); - vmc.IsAutomaticSnapshot = vssd.GetValue(vssdProperties, "IsAutomaticSnapshot"); - vmc.Notes = vssd.GetValue(vssdProperties, "Notes"); - vmc.ParentId = vssd.GetValue(vssdProperties, "Parent"); - - var storageallocationsettingdata = cs.GetRelated("Msvm_StorageAllocationSettingData"); - using (storageallocationsettingdata) - { - - } - - configs.Add(vmc); - } - - configs = configs.GroupBy(p => p.Id).Select(p => p.First()).ToList(); - if (configs.Any(p => p.ParentId is not null)) - { - foreach (var conf in configs.Where(p => p.ParentId is not null)) - { - using var parent = new ManagementObject(conf.ParentId); - parent.Get(); - - if (Guid.TryParse(parent["ConfigurationID"]?.ToString(), out var parentGuid) && configs.FirstOrDefault(p => p.Id == parentGuid.ToString()) is VirtualMaschineConfiguration parentConfig) - { - conf.ParentId = parentGuid.ToString(); - - parentConfig.Childs ??= new List(); - parentConfig.Childs.Add(conf); - } - else - { - conf.ParentId = null; - } - } - } - - vm.Configurations = configs.Where(p => p.ParentId is null).ToList(); - } - - vms.Add(vm); - } - } - - return vms; } } + + private static List GetVirtualMaschines() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\virtualization\v2"), + Query = new ObjectQuery("select * msvm_computersystem") + }; + + if (searcher.TryGet(out var computersystems) is false) + { + searcher.Query = new ObjectQuery("select * from msvm_computersystem"); + + if (searcher.TryGet(out computersystems) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var vms = new List(); + + using (computersystems) + { + foreach (ManagementObject cs in computersystems.Cast()) + { + var vm = new VirtualMaschine(); + + var csProperties = cs.GetPropertyHashes(); + + var vmId = cs.GetValue(csProperties, "name")?.Trim(); + + if (Guid.TryParse(vmId, out var vmGuid) is false) continue; + + vm.Id = vmGuid; + vm.ProcessId = cs.GetValue(csProperties, "processid"); + vm.Caption = cs.GetValue(csProperties, "caption")?.Trim(); + vm.Name = cs.GetValue(csProperties, "elementname")?.Trim(); + vm.Enabled = (EnabledEnum)cs.GetValue(csProperties, "enabledstate"); + vm.EnabledDefault = (EnabledDefaultEnum)cs.GetValue(csProperties, "enableddefault"); + vm.HealthState = (HealthStatusEnum)cs.GetValue(csProperties, "healthstate"); + vm.Status = cs.GetValue(csProperties, "status")?.Trim(); + vm.OnTime = cs.GetValue(csProperties, "ontimeinmilliseconds"); + vm.ReplicationMode = cs.GetValue(csProperties, "replicationmode"); + vm.ReplicationState = (ReplicationStateEnum)cs.GetValue(csProperties, "replicationstate"); + vm.ReplicationHealth = (ReplicationHealthEnum)cs.GetValue(csProperties, "replicationhealth"); + + if (cs.TryGetValue(csProperties, "installdate", out var installdate)) + { + vm.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString()); + } + + if (cs.TryGetValue(csProperties, "timeoflastconfigurationchange", out var timeoflastconfigurationchange)) + { + vm.TimeOfLastConfigurationChange = ManagementDateTimeConverter.ToDateTime(timeoflastconfigurationchange?.ToString()); + } + + if (cs.TryGetValue(csProperties, "timeoflaststatechange", out var timeoflaststatechange)) + { + vm.TimeOfLastStateChange = ManagementDateTimeConverter.ToDateTime(timeoflaststatechange?.ToString()); + } + + if (cs.TryGetValue(csProperties, "lastreplicationtime", out var lastreplicationtime)) + { + vm.LastReplicationTime = ManagementDateTimeConverter.ToDateTime(lastreplicationtime?.ToString()); + } + + var summaryinformation = cs.GetRelated("msvm_summaryinformation"); + using (summaryinformation) + { + foreach (ManagementObject si in summaryinformation.Cast()) + { + var siProperties = si.GetPropertyHashes(); + + vm.Notes = si.GetValue(siProperties, "Notes"); + vm.ConfigurationVersion = si.GetValue(siProperties, "Version"); + vm.IntegrationServicesVersionState = (IntegrationServicesVersionStateEnum)si.GetValue(siProperties, "IntegrationServicesVersionState"); + vm.GuestOperatingSystem = si.GetValue(siProperties, "GuestOperatingSystem"); + vm.NumberOfProcessors = si.GetValue(siProperties, "NumberOfProcessors"); + vm.ProcessorLoad = si.GetValue(siProperties, "ProcessorLoad"); + vm.MemoryAvailable = si.GetValue(siProperties, "MemoryAvailable"); + vm.MemoryUsage = si.GetValue(siProperties, "MemoryUsage"); + } + } + + var virtualSystemSettingData = cs.GetRelated("Msvm_VirtualSystemSettingData"); + using (virtualSystemSettingData) + { + var configs = new List(); + + foreach (ManagementObject vssd in virtualSystemSettingData.Cast()) + { + var vmc = new VirtualMaschineConfiguration(); + + var vssdProperties = vssd.GetPropertyHashes(); + + var vmcId = vssd.GetValue(vssdProperties, "ConfigurationID")?.Trim(); + + if (Guid.TryParse(vmcId, out var vmcGuid) is false) continue; + + vmc.Id = vmcGuid.ToString(); + + vmc.Type = vssd.GetValue(vssdProperties, "VirtualSystemType"); + vmc.Name = vssd.GetValue(vssdProperties, "ElementName"); + + if (vssd.TryGetValue(vssdProperties, "CreationTime", out var creationtime)) + { + vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(creationtime?.ToString()); + } + + vmc.Generation = vssd.GetValue(vssdProperties, "VirtualSystemSubType"); + vmc.Architecture = vssd.GetValue(vssdProperties, "Architecture"); + vmc.AutomaticStartupAction = (AutomaticStartupActionEnum)vssd.GetValue(vssdProperties, "AutomaticStartupAction"); + vmc.AutomaticShutdownAction = (AutomaticShutdownActionEnum)vssd.GetValue(vssdProperties, "AutomaticShutdownAction"); + vmc.AutomaticRecoveryAction = (AutomaticRecoveryActionEnum)vssd.GetValue(vssdProperties, "AutomaticRecoveryAction"); + vmc.AutomaticSnapshotsEnabled = vssd.GetValue(vssdProperties, "AutomaticSnapshotsEnabled"); + + //if (vssd.TryGetValue(vssdProperties, "AutomaticStartupActionDelay", out var automaticstartupactiondelay)) + //{ + // vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(automaticstartupactiondelay?.ToString()); + //} + + vmc.BaseBoardSerialNumber = vssd.GetValue(vssdProperties, "BaseBoardSerialNumber"); + vmc.BIOSGUID = vssd.GetValue(vssdProperties, "BIOSGUID"); + vmc.BIOSSerialNumber = vssd.GetValue(vssdProperties, "BIOSSerialNumber"); + vmc.BootOrder = vssd.GetValue(vssdProperties, "BootOrder"); + vmc.ConfigurationDataRoot = vssd.GetValue(vssdProperties, "ConfigurationDataRoot"); + vmc.ConfigurationFile = vssd.GetValue(vssdProperties, "ConfigurationFile"); + vmc.GuestStateDataRoot = vssd.GetValue(vssdProperties, "GuestStateDataRoot"); + vmc.GuestStateFile = vssd.GetValue(vssdProperties, "GuestStateFile"); + vmc.SnapshotDataRoot = vssd.GetValue(vssdProperties, "SnapshotDataRoot"); + vmc.SuspendDataRoot = vssd.GetValue(vssdProperties, "SuspendDataRoot"); + vmc.SwapFileDataRoot = vssd.GetValue(vssdProperties, "SwapFileDataRoot"); + vmc.SecureBootEnabled = vssd.GetValue(vssdProperties, "SecureBootEnabled"); + vmc.IsAutomaticSnapshot = vssd.GetValue(vssdProperties, "IsAutomaticSnapshot"); + vmc.Notes = vssd.GetValue(vssdProperties, "Notes"); + + if (vssd.GetValue(vssdProperties, "Parent") is string parent) + { + using var vmcp = new ManagementObject(parent); + vmcp.Get(); + + if (Guid.TryParse(vmcp["ConfigurationID"]?.ToString(), out var parentGuid) is false) continue; + vmc.ParentId = parentGuid.ToString(); + } + + //var storageallocationsettingdata = cs.GetRelated("Msvm_StorageAllocationSettingData"); + //using (storageallocationsettingdata) + //{ + + //} + + configs.Add(vmc); + } + + vm.Configurations = configs.GroupBy(p => p.Id).Select(p => p.First()).ToList(); + } + + vms.Add(vm); + } + } + + return vms; + } + + private static List QueryVirtualMaschines0() + { + using var searcher = new ManagementObjectSearcher + { + Scope = new ManagementScope(@"root\virtualization\v2"), + Query = new ObjectQuery("select * msvm_computersystem") + }; + + if (searcher.TryGet(out var computersystems) is false) + { + searcher.Query = new ObjectQuery("select * from msvm_computersystem"); + + if (searcher.TryGet(out computersystems) is false) throw new InvalidOperationException("WMI Collection NULL"); + } + + var vms = new List(); + + using (computersystems) + { + foreach (ManagementObject cs in computersystems.Cast()) + { + var vm = new VirtualMaschine(); + + var csProperties = cs.GetPropertyHashes(); + + var vmId = cs.GetValue(csProperties, "name")?.Trim(); + + if (Guid.TryParse(vmId, out var vmGuid) is false) continue; + + vm.Id = vmGuid; + vm.ProcessId = cs.GetValue(csProperties, "processid"); + vm.Caption = cs.GetValue(csProperties, "caption")?.Trim(); + vm.Name = cs.GetValue(csProperties, "elementname")?.Trim(); + vm.Enabled = (EnabledEnum)cs.GetValue(csProperties, "enabledstate"); + vm.EnabledDefault = (EnabledDefaultEnum)cs.GetValue(csProperties, "enableddefault"); + vm.HealthState = (HealthStatusEnum)cs.GetValue(csProperties, "healthstate"); + vm.Status = cs.GetValue(csProperties, "status")?.Trim(); + vm.OnTime = cs.GetValue(csProperties, "ontimeinmilliseconds"); + vm.ReplicationMode = cs.GetValue(csProperties, "replicationmode"); + vm.ReplicationState = (ReplicationStateEnum)cs.GetValue(csProperties, "replicationstate"); + vm.ReplicationHealth = (ReplicationHealthEnum)cs.GetValue(csProperties, "replicationhealth"); + + if (cs.TryGetValue(csProperties, "installdate", out var installdate)) + { + vm.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString()); + } + + if (cs.TryGetValue(csProperties, "timeoflastconfigurationchange", out var timeoflastconfigurationchange)) + { + vm.TimeOfLastConfigurationChange = ManagementDateTimeConverter.ToDateTime(timeoflastconfigurationchange?.ToString()); + } + + if (cs.TryGetValue(csProperties, "timeoflaststatechange", out var timeoflaststatechange)) + { + vm.TimeOfLastStateChange = ManagementDateTimeConverter.ToDateTime(timeoflaststatechange?.ToString()); + } + + if (cs.TryGetValue(csProperties, "lastreplicationtime", out var lastreplicationtime)) + { + vm.LastReplicationTime = ManagementDateTimeConverter.ToDateTime(lastreplicationtime?.ToString()); + } + + var summaryinformation = cs.GetRelated("msvm_summaryinformation"); + using (summaryinformation) + { + foreach (ManagementObject si in summaryinformation.Cast()) + { + var siProperties = si.GetPropertyHashes(); + + vm.Notes = si.GetValue(siProperties, "Notes"); + vm.ConfigurationVersion = si.GetValue(siProperties, "Version"); + vm.IntegrationServicesVersionState = (IntegrationServicesVersionStateEnum)si.GetValue(siProperties, "IntegrationServicesVersionState"); + vm.GuestOperatingSystem = si.GetValue(siProperties, "GuestOperatingSystem"); + vm.NumberOfProcessors = si.GetValue(siProperties, "NumberOfProcessors"); + vm.ProcessorLoad = si.GetValue(siProperties, "ProcessorLoad"); + vm.MemoryAvailable = si.GetValue(siProperties, "MemoryAvailable"); + vm.MemoryUsage = si.GetValue(siProperties, "MemoryUsage"); + } + } + + var virtualSystemSettingData = cs.GetRelated("Msvm_VirtualSystemSettingData"); + using (virtualSystemSettingData) + { + var configs = new List(); + + foreach (ManagementObject vssd in virtualSystemSettingData.Cast()) + { + var vmc = new VirtualMaschineConfiguration(); + + var vssdProperties = vssd.GetPropertyHashes(); + + var vmcId = vssd.GetValue(vssdProperties, "ConfigurationID")?.Trim(); + + if (Guid.TryParse(vmcId, out var vmcGuid) is false) continue; + + vmc.Id = vmcGuid.ToString(); + + vmc.Type = vssd.GetValue(vssdProperties, "VirtualSystemType"); + vmc.Name = vssd.GetValue(vssdProperties, "ElementName"); + + if (vssd.TryGetValue(vssdProperties, "CreationTime", out var creationtime)) + { + vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(creationtime?.ToString()); + } + + vmc.Generation = vssd.GetValue(vssdProperties, "VirtualSystemSubType"); + vmc.Architecture = vssd.GetValue(vssdProperties, "Architecture"); + vmc.AutomaticStartupAction = (AutomaticStartupActionEnum)vssd.GetValue(vssdProperties, "AutomaticStartupAction"); + + //if (vssd.TryGetValue(vssdProperties, "AutomaticStartupActionDelay", out var automaticstartupactiondelay)) + //{ + // vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(automaticstartupactiondelay?.ToString()); + //} + + vmc.AutomaticShutdownAction = (AutomaticShutdownActionEnum)vssd.GetValue(vssdProperties, "AutomaticShutdownAction"); + vmc.AutomaticRecoveryAction = (AutomaticRecoveryActionEnum)vssd.GetValue(vssdProperties, "AutomaticRecoveryAction"); + vmc.AutomaticSnapshotsEnabled = vssd.GetValue(vssdProperties, "AutomaticSnapshotsEnabled"); + + vmc.BaseBoardSerialNumber = vssd.GetValue(vssdProperties, "BaseBoardSerialNumber"); + vmc.BIOSGUID = vssd.GetValue(vssdProperties, "BIOSGUID"); + vmc.BIOSSerialNumber = vssd.GetValue(vssdProperties, "BIOSSerialNumber"); + vmc.BootOrder = vssd.GetValue(vssdProperties, "BootOrder"); + vmc.ConfigurationDataRoot = vssd.GetValue(vssdProperties, "ConfigurationDataRoot"); + vmc.ConfigurationFile = vssd.GetValue(vssdProperties, "ConfigurationFile"); + vmc.GuestStateDataRoot = vssd.GetValue(vssdProperties, "GuestStateDataRoot"); + vmc.GuestStateFile = vssd.GetValue(vssdProperties, "GuestStateFile"); + vmc.SnapshotDataRoot = vssd.GetValue(vssdProperties, "SnapshotDataRoot"); + vmc.SuspendDataRoot = vssd.GetValue(vssdProperties, "SuspendDataRoot"); + vmc.SwapFileDataRoot = vssd.GetValue(vssdProperties, "SwapFileDataRoot"); + vmc.SecureBootEnabled = vssd.GetValue(vssdProperties, "SecureBootEnabled"); + vmc.IsAutomaticSnapshot = vssd.GetValue(vssdProperties, "IsAutomaticSnapshot"); + vmc.Notes = vssd.GetValue(vssdProperties, "Notes"); + vmc.ParentId = vssd.GetValue(vssdProperties, "Parent"); + + var storageallocationsettingdata = cs.GetRelated("Msvm_StorageAllocationSettingData"); + using (storageallocationsettingdata) + { + + } + + configs.Add(vmc); + } + + configs = configs.GroupBy(p => p.Id).Select(p => p.First()).ToList(); + if (configs.Any(p => p.ParentId is not null)) + { + foreach (var conf in configs.Where(p => p.ParentId is not null)) + { + using var parent = new ManagementObject(conf.ParentId); + parent.Get(); + + if (Guid.TryParse(parent["ConfigurationID"]?.ToString(), out var parentGuid) && configs.FirstOrDefault(p => p.Id == parentGuid.ToString()) is VirtualMaschineConfiguration parentConfig) + { + conf.ParentId = parentGuid.ToString(); + + parentConfig.Childs ??= new List(); + parentConfig.Childs.Add(conf); + } + else + { + conf.ParentId = null; + } + } + } + + vm.Configurations = configs.Where(p => p.ParentId is null).ToList(); + } + + vms.Add(vm); + } + } + + return vms; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Program.cs b/src/Agent/Insight.Agent/Program.cs index 15ed188..807ac7a 100644 --- a/src/Agent/Insight.Agent/Program.cs +++ b/src/Agent/Insight.Agent/Program.cs @@ -1,101 +1,102 @@ using Insight.Agent.Extensions; -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; using Insight.Agent.Network; using Insight.Agent.Network.Handlers; using Insight.Agent.Services; using Insight.Domain.Constants; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Vaitr.Network; +using Vaitr.Network.Hosting; -namespace Insight.Agent.Windows +namespace Insight.Agent.Windows; + +internal class Program { - internal class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) + var builder = Host.CreateDefaultBuilder(args); + builder.UseWindowsService(); + builder.UseSystemd(); + + builder.ConfigureAppConfiguration(config => { - var builder = Host.CreateDefaultBuilder(args); - builder.UseWindowsService(); - builder.UseSystemd(); + config.Defaults(); + }); - builder.ConfigureAppConfiguration(config => + builder.ConfigureLogging(options => + { + options.ClearProviders(); + options.SetMinimumLevel(LogLevel.Trace); + + options.AddSimpleConsole(options => { - config.Defaults(); + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; }); - builder.ConfigureLogging(options => + options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/agent_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); + }); + + builder.ConfigureServices((host, services) => + { + // HOST-SERVICES + services.AddHostedService(); + services.AddHostedService(); + + // SERVICES (WINDOWS) + if (OperatingSystem.IsWindows()) services.AddHostedService(); + if (OperatingSystem.IsWindows()) services.AddSingleton(); + + // AGENT NETWORKING + services.UseHostedClient(options => { - options.ClearProviders(); - options.SetMinimumLevel(LogLevel.Trace); + options.Host = host.Configuration.GetValue(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} value not set (appsettings)"); + options.Port = host.Configuration.GetValue(Appsettings.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} value not set (appsettings)"); + options.Keepalive = 10000; + options.Timeout = 30000; - options.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; - }); + options.Compression = true; + options.Encryption = Encryption.Tls12; - options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/agent_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); + options.UseSerializer>(); }); - builder.ConfigureServices((host, services) => + services.AddSingleton, CustomHandler>(); + services.AddSingleton, ProxyHandler>(); + services.AddSingleton, AuthenticationHandler>(); + services.AddSingleton, DriveHandler>(); + services.AddSingleton, InterfaceHandler>(); + services.AddSingleton, MainboardHandler>(); + services.AddSingleton, MemoryHandler>(); + services.AddSingleton, OperationSystemHandler>(); + services.AddSingleton, PrinterHandler>(); + services.AddSingleton, ProcessorHandler>(); + services.AddSingleton, ServiceHandler>(); + services.AddSingleton, SessionHandler>(); + services.AddSingleton, SoftwareHandler>(); + services.AddSingleton, StoragePoolHandler>(); + services.AddSingleton, SystemInfoHandler>(); + services.AddSingleton, UpdateHandler>(); + services.AddSingleton, UserHandler>(); + services.AddSingleton, VideocardHandler>(); + services.AddSingleton, VirtualMaschineHandler>(); + + + // GLOBAL DEPENDENCIES + //services.AddSingleton(); + services.AddTransient(provider => new HttpClient(new HttpClientHandler { - // SERVICES - services.AddHostedService(); - services.AddHostedService(); + ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true + })); + }); - // SERVICES (WINDOWS) - if (OperatingSystem.IsWindows()) services.AddHostedService(); - - // AGENT NETWORKING - services.UseHostedTcpClient(options => - { - options.Host = host.Configuration.GetValue(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} value not set (appsettings)"); - options.Port = host.Configuration.GetValue(Appsettings.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} value not set (appsettings)"); - options.Buffer = 1024 * 1024; - options.KeepAlive = 10000; - options.Timeout = 30000; - options.Parallelism = 0; - options.Compression = Compression.None; - options.Encryption = Encryption.Tls12; - options.SslPolicy = SslPolicy.None; - - //options.InputRateLimit = 1024 * 1024 * 100; - //options.OutputRateLimit = 1024 * 1024 * 100; - }); - - services.AddSingleton, AuthenticationHandler>(); - services.AddSingleton, DriveHandler>(); - services.AddSingleton, InterfaceHandler>(); - services.AddSingleton, MainboardHandler>(); - services.AddSingleton, MemoryHandler>(); - services.AddSingleton, OperationSystemHandler>(); - services.AddSingleton, PrinterHandler>(); - services.AddSingleton, ProcessorHandler>(); - services.AddSingleton, ServiceHandler>(); - services.AddSingleton, SessionHandler>(); - services.AddSingleton, SoftwareHandler>(); - services.AddSingleton, StoragePoolHandler>(); - services.AddSingleton, SystemInfoHandler>(); - services.AddSingleton, UpdateHandler>(); - services.AddSingleton, UserHandler>(); - services.AddSingleton, VideocardHandler>(); - services.AddSingleton, VirtualMaschineHandler>(); - services.AddSingleton, ConsoleHandler>(); - - // GLOBAL DEPENDENCIES - services.AddTransient(provider => new HttpClient(new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true - })); - }); - - var host = builder.Build(); - await host.RunAsync().ConfigureAwait(false); - } + var host = builder.Build(); + await host.RunAsync().ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/CollectorService.cs b/src/Agent/Insight.Agent/Services/CollectorService.cs index 6b8d224..03ac923 100644 --- a/src/Agent/Insight.Agent/Services/CollectorService.cs +++ b/src/Agent/Insight.Agent/Services/CollectorService.cs @@ -2,16 +2,15 @@ using Microsoft.Extensions.Logging.Abstractions; using System.Runtime.Versioning; -namespace Insight.Agent.Services -{ - [SupportedOSPlatform("linux")] - public partial class CollectorService - { - public ILogger Logger { get; } +namespace Insight.Agent.Services; - public CollectorService(ILogger? logger = null) - { - Logger = logger ?? NullLogger.Instance; - } +[SupportedOSPlatform("linux")] +public partial class CollectorService +{ + public ILogger Logger { get; } + + public CollectorService(ILogger? logger = null) + { + Logger = logger ?? NullLogger.Instance; } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/Configurator.cs b/src/Agent/Insight.Agent/Services/Configurator.cs index 0778ac2..3dbfa76 100644 --- a/src/Agent/Insight.Agent/Services/Configurator.cs +++ b/src/Agent/Insight.Agent/Services/Configurator.cs @@ -1,97 +1,96 @@ using System.Text.Json; -namespace Insight.Agent.Services +namespace Insight.Agent.Services; + +public static class Configurator { - public static class Configurator + public static async ValueTask ReadAsync(string file, CancellationToken cancellationToken = default) + where TConfig : class { - public static async ValueTask ReadAsync(string file, CancellationToken cancellationToken = default) - where TConfig : class + var json = await File.ReadAllTextAsync(file, cancellationToken); + + if (JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, WriteIndented = true }) is not TConfig config) { - var json = await File.ReadAllTextAsync(file, cancellationToken); - - if (JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, WriteIndented = true }) is not TConfig config) - { - throw new InvalidDataException($"Failed to deserialize ({file})"); - } - - return config; - } - public static async ValueTask ReadJsonAsync(string file, CancellationToken cancellationToken = default) - { - var json = await File.ReadAllTextAsync(file, cancellationToken); - - if (JsonDocument.Parse(json) is not JsonDocument doc) - { - throw new InvalidDataException($"Failed to deserialize ({file})"); - } - - return doc; - } - public static async ValueTask> ReadDictionaryAsync(string file, CancellationToken cancellationToken = default) - { - var json = await File.ReadAllTextAsync(file, cancellationToken); - - if (JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, WriteIndented = true }) is not IDictionary config) - { - throw new InvalidDataException($"Failed to deserialize ({file})"); - } - - return config; + throw new InvalidDataException($"Failed to deserialize ({file})"); } - public static async ValueTask WriteAsync(TConfig config, string file, CancellationToken cancellationToken) where TConfig : class + return config; + } + public static async ValueTask ReadJsonAsync(string file, CancellationToken cancellationToken = default) + { + var json = await File.ReadAllTextAsync(file, cancellationToken); + + if (JsonDocument.Parse(json) is not JsonDocument doc) { - await WriteToFileAsync(config, file, cancellationToken); - } - public static async ValueTask WriteAsync(JsonDocument config, string file, CancellationToken cancellationToken) - { - await WriteToFileAsync(config, file, cancellationToken); - } - public static async ValueTask WriteAsync(IDictionary config, string file, CancellationToken cancellationToken) - { - await WriteToFileAsync(config, file, cancellationToken); + throw new InvalidDataException($"Failed to deserialize ({file})"); } - public static async ValueTask AddOrUpdateAsync(KeyValuePair data, string file, CancellationToken cancellationToken) + return doc; + } + public static async ValueTask> ReadDictionaryAsync(string file, CancellationToken cancellationToken = default) + { + var json = await File.ReadAllTextAsync(file, cancellationToken); + + if (JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, WriteIndented = true }) is not IDictionary config) { - var readData = await ReadDictionaryAsync(file, cancellationToken); - - var key = readData.Keys.FirstOrDefault(dic => string.Compare(dic, data.Key, StringComparison.OrdinalIgnoreCase) == 0); - - if (key is null) - { - readData.Add(data.Key, data.Value); - } - else - { - readData[key] = data.Value; - } - - await WriteToFileAsync(readData, file, cancellationToken); - } - public static async ValueTask RemoveAsync(string data, string file, CancellationToken cancellationToken) - { - var readData = await ReadDictionaryAsync(file, cancellationToken); - - var key = readData.Keys.FirstOrDefault(dic => string.Compare(dic, data, StringComparison.OrdinalIgnoreCase) == 0); - - if (key is null) - { - return; - } - else - { - readData.Remove(key); - } - - await WriteToFileAsync(readData, file, cancellationToken); + throw new InvalidDataException($"Failed to deserialize ({file})"); } - private static async ValueTask WriteToFileAsync(TData data, string file, CancellationToken cancellationToken) - { - var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); + return config; + } - await File.WriteAllTextAsync(file, json, cancellationToken); + public static async ValueTask WriteAsync(TConfig config, string file, CancellationToken cancellationToken) where TConfig : class + { + await WriteToFileAsync(config, file, cancellationToken); + } + public static async ValueTask WriteAsync(JsonDocument config, string file, CancellationToken cancellationToken) + { + await WriteToFileAsync(config, file, cancellationToken); + } + public static async ValueTask WriteAsync(IDictionary config, string file, CancellationToken cancellationToken) + { + await WriteToFileAsync(config, file, cancellationToken); + } + + public static async ValueTask AddOrUpdateAsync(KeyValuePair data, string file, CancellationToken cancellationToken) + { + var readData = await ReadDictionaryAsync(file, cancellationToken); + + var key = readData.Keys.FirstOrDefault(dic => string.Compare(dic, data.Key, StringComparison.OrdinalIgnoreCase) == 0); + + if (key is null) + { + readData.Add(data.Key, data.Value); } + else + { + readData[key] = data.Value; + } + + await WriteToFileAsync(readData, file, cancellationToken); + } + public static async ValueTask RemoveAsync(string data, string file, CancellationToken cancellationToken) + { + var readData = await ReadDictionaryAsync(file, cancellationToken); + + var key = readData.Keys.FirstOrDefault(dic => string.Compare(dic, data, StringComparison.OrdinalIgnoreCase) == 0); + + if (key is null) + { + return; + } + else + { + readData.Remove(key); + } + + await WriteToFileAsync(readData, file, cancellationToken); + } + + private static async ValueTask WriteToFileAsync(TData data, string file, CancellationToken cancellationToken) + { + var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); + + await File.WriteAllTextAsync(file, json, cancellationToken); } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/EventService.cs b/src/Agent/Insight.Agent/Services/EventService.cs index 2400023..b2520a9 100644 --- a/src/Agent/Insight.Agent/Services/EventService.cs +++ b/src/Agent/Insight.Agent/Services/EventService.cs @@ -1,170 +1,170 @@ -using Insight.Agent.Messages; -using Insight.Agent.Network; +using Insight.Agent.Network; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Diagnostics.Eventing.Reader; using System.Runtime.Versioning; using System.Threading.Channels; using Vaitr.Network; -using static Insight.Agent.Messages.Event; +using static Insight.Domain.Network.Agent.Messages.Event; using EventLevel = System.Diagnostics.Tracing.EventLevel; -namespace Insight.Agent.Services +namespace Insight.Agent.Services; + +[SupportedOSPlatform("windows")] +internal class EventService : BackgroundService { - [SupportedOSPlatform("windows")] - internal class EventService : BackgroundService + private readonly Channel _queue; + private readonly ISessionPool _pool; + private readonly ILogger _logger; + + public EventService(ISessionPool pool, ILogger logger) { - private readonly Channel _queue; - private readonly TcpSessionPool _pool; - private readonly ILogger _logger; + _pool = pool; + _logger = logger; - public EventService(TcpSessionPool pool, ILogger logger) + _queue = Channel.CreateBounded(new BoundedChannelOptions(1000) { - _pool = pool; - _logger = logger; + SingleReader = true, + SingleWriter = true, + AllowSynchronousContinuations = false, + FullMode = BoundedChannelFullMode.DropOldest + }); + } - _queue = Channel.CreateBounded(new BoundedChannelOptions(1000) - { - SingleReader = true, - SingleWriter = true, - AllowSynchronousContinuations = false, - FullMode = BoundedChannelFullMode.DropOldest - }); - } + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + while (cancellationToken.IsCancellationRequested is false) { - _logger.LogTrace("ExecuteAsync"); - - while (cancellationToken.IsCancellationRequested is false) - { - try - { - var tasks = new List - { - HandleQueueAsync(cancellationToken), - WatchAsync("Application", "*", cancellationToken), - WatchAsync("Security", "*", cancellationToken), - WatchAsync("System", "*", cancellationToken), - WatchAsync("Microsoft-Windows-PrintService/Admin", "*", cancellationToken), - WatchAsync("Microsoft-Windows-TaskScheduler/Operational", "*", cancellationToken), - WatchAsync("Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational", "*", cancellationToken), - WatchAsync("Microsoft-Windows-TerminalServices-LocalSessionManager/Operational", "*", cancellationToken), - WatchAsync("Microsoft-Windows-TerminalServices-RDPClient/Operational", "*", cancellationToken), - WatchAsync("Microsoft-Windows-SmbClient/Connectivity", "*", cancellationToken), - WatchAsync("Microsoft-Windows-SmbClient/Security", "*", cancellationToken), - WatchAsync("Microsoft-Windows-SMBServer/Security", "*", cancellationToken), - WatchAsync("Microsoft-Windows-StorageSpaces-Driver/Operational", "*", cancellationToken), - WatchAsync("Microsoft-Windows-Diagnostics-Performance/Operational", "*", cancellationToken) - }; - - await Task.WhenAll(tasks); - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - _logger.LogError("{ex}", ex); - } - } - } - - private async Task WatchAsync(string path, string query, CancellationToken cancellationToken) - { - var config = new EventLogConfiguration(path); - if (config is null) return; - - if (config.IsEnabled is false) - { - config.IsEnabled = true; - config.SaveChanges(); - } - - var watcher = new EventLogWatcher(new EventLogQuery(path, PathType.LogName, query) - { - TolerateQueryErrors = true, - Session = EventLogSession.GlobalSession - }); - try { - watcher.EventRecordWritten += new EventHandler(OnEvent); - watcher.Enabled = true; - - using var semaphore = new SemaphoreSlim(0, 1); - await semaphore.WaitAsync(cancellationToken); - } - catch (Exception) { } - finally - { - watcher.EventRecordWritten -= new EventHandler(OnEvent); - watcher.Enabled = false; - - watcher?.Dispose(); - } - } - - private async Task HandleQueueAsync(CancellationToken cancellationToken) - { - while (await _queue.Reader.WaitToReadAsync(cancellationToken)) - { - var session = _pool.FirstOrDefault(); - - if (session.Value is null) + var tasks = new List { - await Task.Delay(10000, cancellationToken); - continue; - } - - if (_queue.Reader.TryRead(out var item) is false) - { - await Task.Delay(1000, cancellationToken); - continue; - } - - try - { - await session.Value.SendAsync(item, cancellationToken); - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - _logger.LogError("{ex}", ex); - await Task.Delay(10000, cancellationToken); - } - } - } - - private void OnEvent(object? sender, EventRecordWrittenEventArgs e) - { - if (e is null || e.EventRecord is null) return; - - try - { - var @event = new Event - { - Timestamp = e?.EventRecord?.TimeCreated, - EventId = e?.EventRecord?.Id, - Source = e?.EventRecord?.ProviderName, - Category = e?.EventRecord?.LogName, - Task = e?.EventRecord?.TaskDisplayName, - Message = e?.EventRecord?.FormatDescription(), + HandleQueueAsync(cancellationToken), + WatchAsync("Application", "*", cancellationToken), + WatchAsync("Security", "*", cancellationToken), + WatchAsync("System", "*", cancellationToken), + WatchAsync("Microsoft-Windows-PrintService/Admin", "*", cancellationToken), + WatchAsync("Microsoft-Windows-TaskScheduler/Operational", "*", cancellationToken), + WatchAsync("Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational", "*", cancellationToken), + WatchAsync("Microsoft-Windows-TerminalServices-LocalSessionManager/Operational", "*", cancellationToken), + WatchAsync("Microsoft-Windows-TerminalServices-RDPClient/Operational", "*", cancellationToken), + WatchAsync("Microsoft-Windows-SmbClient/Connectivity", "*", cancellationToken), + WatchAsync("Microsoft-Windows-SmbClient/Security", "*", cancellationToken), + WatchAsync("Microsoft-Windows-SMBServer/Security", "*", cancellationToken), + WatchAsync("Microsoft-Windows-StorageSpaces-Driver/Operational", "*", cancellationToken), + WatchAsync("Microsoft-Windows-Diagnostics-Performance/Operational", "*", cancellationToken) }; - if (e?.EventRecord?.Level is not null) - { - @event.Status = (EventLevel)Convert.ToInt32(e?.EventRecord?.Level) switch - { - EventLevel.Informational => StatusType.Information, - EventLevel.Warning => StatusType.Warning, - EventLevel.Error => StatusType.Error, - EventLevel.Critical => StatusType.Critical, - _ => StatusType.Information, - }; - } - - _queue.Writer.WriteAsync(@event, default); + await Task.WhenAll(tasks); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + _logger.LogError("{ex}", ex); } - catch (Exception) { } // app crash } } + + private async Task WatchAsync(string path, string query, CancellationToken cancellationToken) + { + var config = new EventLogConfiguration(path); + if (config is null) return; + + if (config.IsEnabled is false) + { + config.IsEnabled = true; + config.SaveChanges(); + } + + var watcher = new EventLogWatcher(new EventLogQuery(path, PathType.LogName, query) + { + TolerateQueryErrors = true, + Session = EventLogSession.GlobalSession + }); + + try + { + watcher.EventRecordWritten += new EventHandler(OnEvent); + watcher.Enabled = true; + + using var semaphore = new SemaphoreSlim(0, 1); + await semaphore.WaitAsync(cancellationToken); + } + catch (Exception) { } + finally + { + watcher.EventRecordWritten -= new EventHandler(OnEvent); + watcher.Enabled = false; + + watcher?.Dispose(); + } + } + + private async Task HandleQueueAsync(CancellationToken cancellationToken) + { + while (await _queue.Reader.WaitToReadAsync(cancellationToken)) + { + var session = _pool.FirstOrDefault(); + + if (session.Value is null) + { + await Task.Delay(10000, cancellationToken); + continue; + } + + if (_queue.Reader.TryRead(out var item) is false) + { + await Task.Delay(1000, cancellationToken); + continue; + } + + try + { + await session.Value.SendAsync(item, cancellationToken); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + _logger.LogError("{ex}", ex); + await Task.Delay(10000, cancellationToken); + } + } + } + + private void OnEvent(object? sender, EventRecordWrittenEventArgs e) + { + if (e is null || e.EventRecord is null) return; + + try + { + var @event = new Event + { + Timestamp = e?.EventRecord?.TimeCreated, + EventId = e?.EventRecord?.Id, + Source = e?.EventRecord?.ProviderName, + Category = e?.EventRecord?.LogName, + Task = e?.EventRecord?.TaskDisplayName, + Message = e?.EventRecord?.FormatDescription(), + }; + + if (e?.EventRecord?.Level is not null) + { + @event.Status = (EventLevel)Convert.ToInt32(e?.EventRecord?.Level) switch + { + EventLevel.Informational => StatusType.Information, + EventLevel.Warning => StatusType.Warning, + EventLevel.Error => StatusType.Error, + EventLevel.Critical => StatusType.Critical, + _ => StatusType.Information, + }; + } + + _queue.Writer.WriteAsync(@event, default); + } + catch (Exception) { } // app crash + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/ConsoleHandler.cs b/src/Agent/Insight.Agent/Services/ScriptService.cs similarity index 64% rename from src/Agent/Insight.Agent/Network/Handlers/ConsoleHandler.cs rename to src/Agent/Insight.Agent/Services/ScriptService.cs index 267bb5d..32e4eda 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/ConsoleHandler.cs +++ b/src/Agent/Insight.Agent/Services/ScriptService.cs @@ -1,36 +1,19 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; +using Microsoft.Extensions.Logging; using System.Management.Automation; using System.Management.Automation.Runspaces; -namespace Insight.Agent.Network.Handlers; +namespace Insight.Agent.Services; -public class ConsoleHandler : IAgentMessageHandler +public class ScriptService { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + private readonly ILogger _logger; + + public ScriptService(ILogger logger) { - if (message is ConsoleQueryRequest consoleQueryRequest) - { - await OnConsoleQueryRequestAsync(sender, consoleQueryRequest, cancellationToken); - } + _logger = logger; } - private async ValueTask OnConsoleQueryRequestAsync(AgentSession sender, ConsoleQueryRequest consoleQueryRequest, CancellationToken cancellationToken) - { - var result = await QueryScriptAsync(consoleQueryRequest.Query); - - await sender.SendAsync(new ConsoleQuery - { - Id = consoleQueryRequest.Id, - HostId = consoleQueryRequest.HostId, - Query = consoleQueryRequest.Query, - Data = result.Data, - Errors = result.Errors, - HadErrors = result.HadErrors - }, cancellationToken); - } - - private static async Task QueryScriptAsync(string query) + public async Task QueryAsync(string query) { var result = new QueryResult(); var errors = new List(); @@ -44,7 +27,7 @@ public class ConsoleHandler : IAgentMessageHandler using var ps = PowerShell.Create(runspace); ps.AddScript("Set-ExecutionPolicy unrestricted -Scope Process"); ps.AddScript(query); - ps.AddCommand("ConvertTo-Json"); // -Depth 10 + //ps.AddCommand("ConvertTo-Json"); // -Depth 10 result.Query = query; @@ -57,7 +40,15 @@ public class ConsoleHandler : IAgentMessageHandler } else { - result.Data = queryResult[0].ToString(); + var newLine = false; + + foreach (var data in queryResult) + { + if (newLine) result.Data += "\n"; + else newLine = true; + + result.Data += data.ToString(); + } //if (string.IsNullOrWhiteSpace(jsonString)) return result; @@ -100,12 +91,12 @@ public class ConsoleHandler : IAgentMessageHandler result.Errors = string.Join("\n", errors); return result; } -} -public class QueryResult -{ - public bool HadErrors { get; set; } - public string? Query { get; set; } - public string? Data { get; set; } - public string? Errors { get; set; } + public class QueryResult + { + public bool HadErrors { get; set; } + public string? Query { get; set; } + public string? Data { get; set; } + public string? Errors { get; set; } + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/TrapService.cs b/src/Agent/Insight.Agent/Services/TrapService.cs index 13051d5..cd1a1a9 100644 --- a/src/Agent/Insight.Agent/Services/TrapService.cs +++ b/src/Agent/Insight.Agent/Services/TrapService.cs @@ -1,5 +1,6 @@ -using Insight.Agent.Messages; -using Insight.Agent.Network; +using Insight.Agent.Network; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -9,192 +10,190 @@ using System.Net.Sockets; using System.Threading.Channels; using Vaitr.Network; -namespace Insight.Agent.Services +namespace Insight.Agent.Services; + +public class TrapService : BackgroundService { - public class TrapService : BackgroundService + private static IPEndPoint EndpointCache { get; } = new(IPAddress.Any, 0); + private readonly Channel _queue; + + private readonly int _port; + private readonly ISessionPool _pool; + private readonly ILogger _logger; + + public TrapService(ISessionPool pool, IConfiguration configuration, ILogger logger) { - private static IPEndPoint EndpointCache { get; } = new(IPAddress.Any, 0); - private readonly Channel _queue; + _port = configuration.GetValue(Appsettings.TrapPort) ?? throw new Exception($"{Appsettings.TrapPort} value not set (appsettings)"); + _pool = pool; + _logger = logger; - - private readonly int _port; - private readonly TcpSessionPool _pool; - private readonly ILogger _logger; - - public TrapService(TcpSessionPool pool, IConfiguration configuration, ILogger logger) + _queue = Channel.CreateBounded(new BoundedChannelOptions(100) { - _port = configuration.GetValue(Appsettings.TrapPort) ?? throw new Exception($"{Appsettings.TrapPort} value not set (appsettings)"); - _pool = pool; - _logger = logger; + SingleReader = false, + SingleWriter = true, + AllowSynchronousContinuations = false, + FullMode = BoundedChannelFullMode.DropOldest + }); + } - _queue = Channel.CreateBounded(new BoundedChannelOptions(100) - { - SingleReader = false, - SingleWriter = true, - AllowSynchronousContinuations = false, - FullMode = BoundedChannelFullMode.DropOldest - }); - } + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + while (cancellationToken.IsCancellationRequested is false) { - _logger.LogTrace("ExecuteAsync"); - - while (cancellationToken.IsCancellationRequested is false) - { - try - { - var tasks = new List - { - HandleQueueAsync(cancellationToken), - ListenAsync(cancellationToken) - }; - - await Task.WhenAll(tasks); - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - _logger.LogError("{ex}", ex); - } - } - } - - private async Task HandleQueueAsync(CancellationToken cancellationToken) - { - while (await _queue.Reader.WaitToReadAsync(cancellationToken)) - { - var session = _pool.FirstOrDefault(); - - if (session.Value is null) - { - await Task.Delay(10000, cancellationToken); - continue; - } - - if (_queue.Reader.TryRead(out var item) is false) - { - await Task.Delay(1000, cancellationToken); - continue; - } - - try - { - await session.Value.SendAsync(item, cancellationToken); - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - _logger.LogError("{ex}", ex); - await Task.Delay(10000, cancellationToken); - } - } - } - - private async Task ListenAsync(CancellationToken cancellationToken) - { - using var udpSocket = new Socket(SocketType.Dgram, ProtocolType.Udp); - - cancellationToken.Register(udpSocket.Dispose); - - udpSocket.Bind(new IPEndPoint(IPAddress.Any, _port)); - - var buffer = GC.AllocateArray(length: 65527, pinned: true); - var bufferMem = buffer.AsMemory(); - - while (cancellationToken.IsCancellationRequested is false) - { - try - { - var result = await udpSocket.ReceiveFromAsync(bufferMem, SocketFlags.None, EndpointCache, cancellationToken); - - if (result.ReceivedBytes == 0) continue; - - var actualBytes = bufferMem[..result.ReceivedBytes].ToArray(); - - var ep = (IPEndPoint)result.RemoteEndPoint; - - await ProcessAsync(ep, actualBytes, cancellationToken); - } - catch (SocketException) { continue; } - catch (IOException) { continue; } - catch (Exception) { continue; } - } - } - - private async ValueTask ProcessAsync(IPEndPoint endpoint, byte[] buffer, CancellationToken cancellationToken) - { - var trap = new Trap - { - Timestamp = DateTime.Now, - Endpoint = endpoint.ToString(), - }; - try { - var protocol = (SnmpVersion)SnmpPacket.GetProtocolVersion(buffer, buffer.Length); - - trap.Version = protocol.ToString(); - trap.Hostname = Dns.GetHostEntry(endpoint.Address)?.HostName; - - if (protocol == SnmpVersion.Ver1) + var tasks = new List { - var packet = new SnmpV1TrapPacket(); - packet.decode(buffer, buffer.Length); + HandleQueueAsync(cancellationToken), + ListenAsync(cancellationToken) + }; - trap.Community = packet?.Community?.ToString(); - - if (packet?.Pdu?.VbList is not null) - { - trap.Data = ConvertVbs(packet.Pdu.VbList); - } - } - - if (protocol == SnmpVersion.Ver2) - { - var packet = new SnmpV2Packet(); - packet.decode(buffer, buffer.Length); - - trap.Community = packet?.Community?.ToString(); - - if (packet?.Pdu?.VbList is not null) - { - trap.Data = ConvertVbs(packet.Pdu.VbList); - } - } - - if (protocol == SnmpVersion.Ver3) - { - var packet = new SnmpV3Packet(); - } + await Task.WhenAll(tasks); } + catch (OperationCanceledException) { } catch (Exception ex) { _logger.LogError("{ex}", ex); } - - await _queue.Writer.WriteAsync(trap, cancellationToken); - } - - private List>? ConvertVbs(VbCollection vbs) - { - var data = new List>(); - - try - { - foreach (var item in vbs) - { - if (item.Oid is null) continue; - - data.Add(new KeyValuePair(item.Oid.ToString(), item?.Value?.ToString())); - } - } - catch (Exception ex) - { - _logger.LogError("{ex}", ex); - } - - return data; } } + + private async Task HandleQueueAsync(CancellationToken cancellationToken) + { + while (await _queue.Reader.WaitToReadAsync(cancellationToken)) + { + var session = _pool.FirstOrDefault(); + + if (session.Value is null) + { + await Task.Delay(10000, cancellationToken); + continue; + } + + if (_queue.Reader.TryRead(out var item) is false) + { + await Task.Delay(1000, cancellationToken); + continue; + } + + try + { + await session.Value.SendAsync(item, cancellationToken); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + _logger.LogError("{ex}", ex); + await Task.Delay(10000, cancellationToken); + } + } + } + + private async Task ListenAsync(CancellationToken cancellationToken) + { + using var udpSocket = new Socket(SocketType.Dgram, ProtocolType.Udp); + + cancellationToken.Register(udpSocket.Dispose); + + udpSocket.Bind(new IPEndPoint(IPAddress.Any, _port)); + + var buffer = GC.AllocateArray(length: 65527, pinned: true); + var bufferMem = buffer.AsMemory(); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + var result = await udpSocket.ReceiveFromAsync(bufferMem, SocketFlags.None, EndpointCache, cancellationToken); + + if (result.ReceivedBytes == 0) continue; + + var actualBytes = bufferMem[..result.ReceivedBytes].ToArray(); + + var ep = (IPEndPoint)result.RemoteEndPoint; + + await ProcessAsync(ep, actualBytes, cancellationToken); + } + catch (SocketException) { continue; } + catch (IOException) { continue; } + catch (Exception) { continue; } + } + } + + private async ValueTask ProcessAsync(IPEndPoint endpoint, byte[] buffer, CancellationToken cancellationToken) + { + var trap = new Trap + { + Timestamp = DateTime.Now, + Endpoint = endpoint.ToString(), + }; + + try + { + var protocol = (SnmpVersion)SnmpPacket.GetProtocolVersion(buffer, buffer.Length); + + trap.Version = protocol.ToString(); + trap.Hostname = Dns.GetHostEntry(endpoint.Address)?.HostName; + + if (protocol == SnmpVersion.Ver1) + { + var packet = new SnmpV1TrapPacket(); + packet.decode(buffer, buffer.Length); + + trap.Community = packet?.Community?.ToString(); + + if (packet?.Pdu?.VbList is not null) + { + trap.Data = ConvertVbs(packet.Pdu.VbList); + } + } + + if (protocol == SnmpVersion.Ver2) + { + var packet = new SnmpV2Packet(); + packet.decode(buffer, buffer.Length); + + trap.Community = packet?.Community?.ToString(); + + if (packet?.Pdu?.VbList is not null) + { + trap.Data = ConvertVbs(packet.Pdu.VbList); + } + } + + if (protocol == SnmpVersion.Ver3) + { + var packet = new SnmpV3Packet(); + } + } + catch (Exception ex) + { + _logger.LogError("{ex}", ex); + } + + await _queue.Writer.WriteAsync(trap, cancellationToken); + } + + private List>? ConvertVbs(VbCollection vbs) + { + var data = new List>(); + + try + { + foreach (var item in vbs) + { + if (item.Oid is null) continue; + + data.Add(new KeyValuePair(item.Oid.ToString(), item?.Value?.ToString())); + } + } + catch (Exception ex) + { + _logger.LogError("{ex}", ex); + } + + return data; + } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/UpdateService.cs b/src/Agent/Insight.Agent/Services/UpdateService.cs index 6869d58..f879069 100644 --- a/src/Agent/Insight.Agent/Services/UpdateService.cs +++ b/src/Agent/Insight.Agent/Services/UpdateService.cs @@ -10,351 +10,350 @@ using System.Runtime.Versioning; using System.ServiceProcess; using System.Text.Json; -namespace Insight.Agent.Services +namespace Insight.Agent.Services; + +internal class UpdateService : BackgroundService { - internal class UpdateService : BackgroundService + private readonly Uri _uri; + + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + public UpdateService(HttpClient httpClient, IConfiguration configuration, ILogger logger) { - private readonly Uri _uri; + _httpClient = httpClient; + _uri = configuration.GetValue("api") ?? throw new Exception($"api value not set (appsettings)"); + _logger = logger; + } - private readonly HttpClient _httpClient; - private readonly ILogger _logger; + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); - public UpdateService(HttpClient httpClient, IConfiguration configuration, ILogger logger) + while (cancellationToken.IsCancellationRequested is false) { - _httpClient = httpClient; - _uri = configuration.GetValue("api") ?? throw new Exception($"api value not set (appsettings)"); - _logger = logger; + try + { + UpdateResult? result = null; + + if (OperatingSystem.IsWindows()) result = await WindowsUpdateAsync(cancellationToken); + if (OperatingSystem.IsLinux()) result = await LinuxUpdateAsync(cancellationToken); + + _logger.LogInformation("Update Result: {result}", result?.Success); + if (result?.UpdateErrors is not null) + { + _logger.LogError("Update Errors: {errors}", string.Concat(result?.UpdateErrors)); + } + } + catch (OperationCanceledException) { } + catch (Exception ex) // may inform via client / api about errors + { + _logger.LogError("{ex}", ex.Message); + } + finally + { + await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); + } } + } - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + [SupportedOSPlatform("windows")] + private async ValueTask WindowsUpdateAsync(CancellationToken cancellationToken) + { + return await Windows.Service.UpdateAsync( + _httpClient, + Deploy.GetUpdateHref(_uri, Deploy.Updater.Name), + Deploy.GetAppExecutable(Deploy.Updater.Name), + Deploy.Updater.ServiceName, + cancellationToken); + } + + [SupportedOSPlatform("linux")] + private ValueTask LinuxUpdateAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + [SupportedOSPlatform("windows")] + private static class Windows + { + public static class Service { - _logger.LogTrace("ExecuteAsync"); - - while (cancellationToken.IsCancellationRequested is false) + public static bool ServiceExistence(string serviceName) { try { - UpdateResult? result = null; + if (ServiceController.GetServices().Any(s => s.ServiceName.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase))) return true; + return false; + } + catch (Exception) { } - if (OperatingSystem.IsWindows()) result = await WindowsUpdateAsync(cancellationToken); - if (OperatingSystem.IsLinux()) result = await LinuxUpdateAsync(cancellationToken); - - _logger.LogInformation("Update Result: {result}", result?.Success); - if (result?.UpdateErrors is not null) - { - _logger.LogError("Update Errors: {errors}", string.Concat(result?.UpdateErrors)); - } - } - catch (OperationCanceledException) { } - catch (Exception ex) // may inform via client / api about errors - { - _logger.LogError("{ex}", ex.Message); - } - finally - { - await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); - } + return false; } - } - [SupportedOSPlatform("windows")] - private async ValueTask WindowsUpdateAsync(CancellationToken cancellationToken) - { - return await Windows.Service.UpdateAsync( - _httpClient, - Deploy.GetUpdateHref(_uri, Deploy.Updater.Name), - Deploy.GetAppExecutable(Deploy.Updater.Name), - Deploy.Updater.ServiceName, - cancellationToken); - } - - [SupportedOSPlatform("linux")] - private ValueTask LinuxUpdateAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - [SupportedOSPlatform("windows")] - private static class Windows - { - public static class Service + public static bool SetServiceState(string app, ServiceControllerStatus status, TimeSpan timeout) { - public static bool ServiceExistence(string serviceName) + try { - try + using var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName.Equals(app, StringComparison.InvariantCultureIgnoreCase)); + if (sc is null) return false; + + if (sc.Status != status) { - if (ServiceController.GetServices().Any(s => s.ServiceName.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase))) return true; - return false; + switch (status) + { + case ServiceControllerStatus.Running: + sc.Start(); + break; + + case ServiceControllerStatus.Stopped: + sc.Stop(); + break; + } + + sc.WaitForStatus(status, timeout); } - catch (Exception) { } - return false; + return true; } + catch (Exception) { } - public static bool SetServiceState(string app, ServiceControllerStatus status, TimeSpan timeout) + return false; + } + + public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, CancellationToken cancellationToken) + { + var result = new UpdateResult { - try + Api = api?.ToString(), + SourceDirectory = bin.Directory?.FullName, + App = bin.Name, + ServiceName = serviceName + }; + + try + { + // check if service exists + if (ServiceExistence(serviceName) is false) { - using var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName.Equals(app, StringComparison.InvariantCultureIgnoreCase)); - if (sc is null) return false; - - if (sc.Status != status) - { - switch (status) - { - case ServiceControllerStatus.Running: - sc.Start(); - break; - - case ServiceControllerStatus.Stopped: - sc.Stop(); - break; - } - - sc.WaitForStatus(status, timeout); - } - - return true; + result.UpdateErrors.Add("service not found"); + return result; } - catch (Exception) { } - return false; - } - - public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, CancellationToken cancellationToken) - { - var result = new UpdateResult + // get service update details + var response = await httpClient.GetFromJsonAsync(api, cancellationToken); + if (response is null) { - Api = api?.ToString(), - SourceDirectory = bin.Directory?.FullName, - App = bin.Name, - ServiceName = serviceName - }; + result.ApiErrors.Add("not available / response null"); + return result; + } - try + result.ApiAvailable = true; + + // check if local binary exists + if (bin is null) { - // check if service exists - if (ServiceExistence(serviceName) is false) - { - result.UpdateErrors.Add("service not found"); - return result; - } + result.UpdateErrors.Add("source binary not found"); + return result; + } - // get service update details - var response = await httpClient.GetFromJsonAsync(api, cancellationToken); - if (response is null) - { - result.ApiErrors.Add("not available / response null"); - return result; - } - - result.ApiAvailable = true; - - // check if local binary exists - if (bin is null) - { - result.UpdateErrors.Add("source binary not found"); - return result; - } - - // get local file binary version - if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) - { - result.UpdateErrors.Add("source binary fileversion not valid"); - return result; - } - - // compare local against update version, skip lower or equal update version - var actualVersion = Version.Parse(binVersionString); - if (actualVersion >= response.Version) - { - result.Success = true; - return result; - } - else - { - result.UpdateAvailable = true; - } - - // get update file (bytes) to memory - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) - { - result.ApiErrors.Add("update source not available"); - return result; - } - - // stop service - if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)) is false) - { - result.UpdateErrors.Add("service control failed / failed to stop service"); - return result; - } - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // start updateds service - if (SetServiceState(serviceName, ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)) is false) - { - result.UpdateErrors.Add("service control failed / failed to start service"); - return result; - } + // get local file binary version + if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) + { + result.UpdateErrors.Add("source binary fileversion not valid"); + return result; + } + // compare local against update version, skip lower or equal update version + var actualVersion = Version.Parse(binVersionString); + if (actualVersion >= response.Version) + { result.Success = true; + return result; } - catch (Exception ex) + else { - result.UpdateErrors.Add(ex.Message); + result.UpdateAvailable = true; } - return result; + // get update file (bytes) to memory + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) + { + result.ApiErrors.Add("update source not available"); + return result; + } + + // stop service + if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)) is false) + { + result.UpdateErrors.Add("service control failed / failed to stop service"); + return result; + } + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // start updateds service + if (SetServiceState(serviceName, ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)) is false) + { + result.UpdateErrors.Add("service control failed / failed to start service"); + return result; + } + + result.Success = true; } + catch (Exception ex) + { + result.UpdateErrors.Add(ex.Message); + } + + return result; + } + } + + public static class Process + { + public static bool IsRunning(FileInfo bin) + { + if (bin.Exists is false) return false; + + var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + + if (matched is null || matched.Any() is false) return false; + + if (matched.Any(p => + p.MainModule is not null && + p.MainModule.FileName is not null && + p.MainModule.FileName.Equals(bin.FullName, + StringComparison.InvariantCultureIgnoreCase))) return true; + + return false; } - public static class Process + public static bool Start(FileInfo binary) { - public static bool IsRunning(FileInfo bin) + try { - if (bin.Exists is false) return false; + if (IsRunning(binary) is false) return false; - var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); - - if (matched is null || matched.Any() is false) return false; - - if (matched.Any(p => - p.MainModule is not null && - p.MainModule.FileName is not null && - p.MainModule.FileName.Equals(bin.FullName, - StringComparison.InvariantCultureIgnoreCase))) return true; - - return false; + using var process = System.Diagnostics.Process.Start(binary.FullName); + return true; } + catch (Exception) { } - public static bool Start(FileInfo binary) - { - try - { - if (IsRunning(binary) is false) return false; + return false; + } - using var process = System.Diagnostics.Process.Start(binary.FullName); - return true; - } - catch (Exception) { } - - return false; - } - - public static bool Stop(FileInfo bin, TimeSpan timeout) - { - try - { - if (IsRunning(bin) is false) return false; - - var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); - - if (matched is null || matched.Any() is false) return true; - - foreach (var procsInfo in matched.Where(p => - p.MainModule is not null && - p.MainModule.FileName is not null && - p.MainModule.FileName.Equals(bin.FullName, StringComparison.InvariantCultureIgnoreCase))) - { - if (procsInfo.CloseMainWindow()) procsInfo.WaitForExit((int)timeout.TotalMilliseconds); - if (procsInfo.HasExited is false) procsInfo.Kill(true); - } - - return true; - } - catch (Exception) { } - - return false; - } - - public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, CancellationToken cancellationToken) + public static bool Stop(FileInfo bin, TimeSpan timeout) + { + try { if (IsRunning(bin) is false) return false; - var response = await httpClient.GetFromJsonAsync(api.AbsoluteUri, new JsonSerializerOptions + var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + + if (matched is null || matched.Any() is false) return true; + + foreach (var procsInfo in matched.Where(p => + p.MainModule is not null && + p.MainModule.FileName is not null && + p.MainModule.FileName.Equals(bin.FullName, StringComparison.InvariantCultureIgnoreCase))) { - IncludeFields = true - }, cancellationToken); - - if (response is null) return false; - - Version actualVersion; - - if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) return false; - - try - { - actualVersion = Version.Parse(binVersionString); - if (actualVersion >= response.Version) return false; - - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) return false; - - Stop(bin, TimeSpan.FromSeconds(60)); - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // rewrite with options to start user session process - //Start(app, directory, TimeSpan.FromSeconds(60)); - - return true; + if (procsInfo.CloseMainWindow()) procsInfo.WaitForExit((int)timeout.TotalMilliseconds); + if (procsInfo.HasExited is false) procsInfo.Kill(true); } - catch (Exception) { } - return false; + return true; } + catch (Exception) { } - public static bool Delete(FileInfo bin, TimeSpan timeout) + return false; + } + + public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, CancellationToken cancellationToken) + { + if (IsRunning(bin) is false) return false; + + var response = await httpClient.GetFromJsonAsync(api.AbsoluteUri, new JsonSerializerOptions { - try - { - Stop(bin, timeout); - bin.Delete(); + IncludeFields = true + }, cancellationToken); - return true; - } - catch (Exception) { } + if (response is null) return false; - return false; + Version actualVersion; + + if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) return false; + + try + { + actualVersion = Version.Parse(binVersionString); + if (actualVersion >= response.Version) return false; + + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) return false; + + Stop(bin, TimeSpan.FromSeconds(60)); + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // rewrite with options to start user session process + //Start(app, directory, TimeSpan.FromSeconds(60)); + + return true; } + catch (Exception) { } + + return false; + } + + public static bool Delete(FileInfo bin, TimeSpan timeout) + { + try + { + Stop(bin, timeout); + bin.Delete(); + + return true; + } + catch (Exception) { } + + return false; } } + } - public class UpdateResult - { - public string? Api { get; set; } - public string? SourceDirectory { get; set; } - public string? App { get; set; } - public string? ServiceName { get; set; } + public class UpdateResult + { + public string? Api { get; set; } + public string? SourceDirectory { get; set; } + public string? App { get; set; } + public string? ServiceName { get; set; } - public bool ApiAvailable { get; set; } = false; - public bool UpdateAvailable { get; set; } = false; - public bool Success { get; set; } = false; - public List ApiErrors { get; } = new(); - public List UpdateErrors { get; } = new(); - } + public bool ApiAvailable { get; set; } = false; + public bool UpdateAvailable { get; set; } = false; + public bool Success { get; set; } = false; + public List ApiErrors { get; } = new(); + public List UpdateErrors { get; } = new(); } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/_Collector/_Os.cs b/src/Agent/Insight.Agent/Services/_Collector/_Os.cs index 7b3eb0e..34db513 100644 --- a/src/Agent/Insight.Agent/Services/_Collector/_Os.cs +++ b/src/Agent/Insight.Agent/Services/_Collector/_Os.cs @@ -1,106 +1,105 @@ using Insight.Agent.Extensions; -using Insight.Agent.Messages; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Logging; using System.Text.RegularExpressions; -namespace Insight.Agent.Services +namespace Insight.Agent.Services; + +public partial class CollectorService { - public partial class CollectorService + public OperationSystem? GetOperatingSystem() { - public OperationSystem? GetOperatingSystem() - { - Logger.LogTrace("GetOperatingSystem"); + Logger.LogTrace("GetOperatingSystem"); - var os = new OperationSystem(); + var os = new OperationSystem(); - // get uptime - var output = string.Empty; + // get uptime + var output = string.Empty; - // read file - using var stream = File.OpenText(@"/proc/uptime"); - output = stream.ReadToEnd(); + // read file + using var stream = File.OpenText(@"/proc/uptime"); + output = stream.ReadToEnd(); - // clean output - var clean = Regex - .Replace(output + // clean output + var clean = Regex + .Replace(output + .Trim() + .Replace("\t", " "), @"[ ]{2,}", " "); + + var elements = clean.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); + + // assign values + //os.Uptime = DateTime.Now - TimeSpan.FromSeconds(double.Parse(elements.ElementAt(0))); + + // request data with process + output = "hostnamectl".Bash(); + + // linebreak list conversion + var lines = new List(output + .Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) + .Select(l => + { + return Regex + .Replace(l .Trim() .Replace("\t", " "), @"[ ]{2,}", " "); + }) + .ToList(); - var elements = clean.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); + // assign values + os.Virtual = lines + .Any(l => l + .StartsWith("Virtualization:")) && !string.IsNullOrEmpty(lines + .Where(l => l + .StartsWith("Virtualization:")) + .First() + .Split("Virtualization:")[1] + .Trim()); - // assign values - //os.Uptime = DateTime.Now - TimeSpan.FromSeconds(double.Parse(elements.ElementAt(0))); + // OS + // request data with process + output = "hostnamectl".Bash(); - // request data with process - output = "hostnamectl".Bash(); + // linebreak list conversion + lines = new List(output + .Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) + .Select(l => + { + return Regex + .Replace(l + .Trim() + .Replace("\t", " "), @"[ ]{2,}", " "); + }) + .ToList(); - // linebreak list conversion - var lines = new List(output - .Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) - .Select(l => - { - return Regex - .Replace(l - .Trim() - .Replace("\t", " "), @"[ ]{2,}", " "); - }) - .ToList(); + // assign values + os.Name = lines + .Any(l => l + .StartsWith("Operating System:")) ? lines + .Where(l => l + .StartsWith("Operating System:")) + .First() + .Split("Operating System:")[1] + .Trim() : string.Empty; - // assign values - os.Virtual = lines - .Any(l => l - .StartsWith("Virtualization:")) && !string.IsNullOrEmpty(lines - .Where(l => l - .StartsWith("Virtualization:")) - .First() - .Split("Virtualization:")[1] - .Trim()); + os.Version = lines + .Any(l => l + .StartsWith("Kernel:")) ? lines + .Where(l => l + .StartsWith("Kernel:")) + .First() + .Split("Kernel:")[1] + .Trim() : string.Empty; - // OS - // request data with process - output = "hostnamectl".Bash(); + var architecture = lines + .Any(l => l + .StartsWith("Architecture:")) ? lines + .Where(l => l + .StartsWith("Architecture:")) + .First() + .Split("Architecture:")[1] + .Trim() : string.Empty; - // linebreak list conversion - lines = new List(output - .Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) - .Select(l => - { - return Regex - .Replace(l - .Trim() - .Replace("\t", " "), @"[ ]{2,}", " "); - }) - .ToList(); - - // assign values - os.Name = lines - .Any(l => l - .StartsWith("Operating System:")) ? lines - .Where(l => l - .StartsWith("Operating System:")) - .First() - .Split("Operating System:")[1] - .Trim() : string.Empty; - - os.Version = lines - .Any(l => l - .StartsWith("Kernel:")) ? lines - .Where(l => l - .StartsWith("Kernel:")) - .First() - .Split("Kernel:")[1] - .Trim() : string.Empty; - - var architecture = lines - .Any(l => l - .StartsWith("Architecture:")) ? lines - .Where(l => l - .StartsWith("Architecture:")) - .First() - .Split("Architecture:")[1] - .Trim() : string.Empty; - - return os; - } + return os; } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Services/_Collector/_Session.cs b/src/Agent/Insight.Agent/Services/_Collector/_Session.cs index 3b34213..fd42981 100644 --- a/src/Agent/Insight.Agent/Services/_Collector/_Session.cs +++ b/src/Agent/Insight.Agent/Services/_Collector/_Session.cs @@ -1,50 +1,49 @@ using Insight.Agent.Extensions; -using Insight.Agent.Messages; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Logging; using System.Text.RegularExpressions; -namespace Insight.Agent.Services +namespace Insight.Agent.Services; + +public partial class CollectorService { - public partial class CollectorService + public List? GetSessions() { - public List? GetSessions() + Logger.LogTrace("GetSessions"); + + var sessions = new List(); + + // request data with process + var output = "w".Bash(); + + // linebreak list conversion + var lines = output.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); + + lines = lines.Select(l => { - Logger.LogTrace("GetSessions"); + return Regex.Replace(l.Trim().Replace("\t", " "), @"[ ]{2,}", " "); + }).ToList(); - var sessions = new List(); + // cleaning + lines.RemoveRange(0, 2); - // request data with process - var output = "w".Bash(); + // process list elements + foreach (var l in lines) + { + // split into elements + var elements = l.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); - // linebreak list conversion - var lines = output.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); - - lines = lines.Select(l => + // add user, assign values + sessions.Add(new Session { - return Regex.Replace(l.Trim().Replace("\t", " "), @"[ ]{2,}", " "); - }).ToList(); - - // cleaning - lines.RemoveRange(0, 2); - - // process list elements - foreach (var l in lines) - { - // split into elements - var elements = l.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); - - // add user, assign values - sessions.Add(new Session - { - User = elements.ElementAt(0), - //Id = elements.ElementAt(1), - Remote = elements.ElementAt(2), - //Login = elements.ElementAt(3), - //Idle = elements.ElementAt(4) - }); - } - - return sessions; + User = elements.ElementAt(0), + //Id = elements.ElementAt(1), + Remote = elements.ElementAt(2), + //Login = elements.ElementAt(3), + //Idle = elements.ElementAt(4) + }); } + + return sessions; } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Constants/Locations.cs b/src/Api/Insight.Api/Constants/Locations.cs index 8db6a8f..94d66e7 100644 --- a/src/Api/Insight.Api/Constants/Locations.cs +++ b/src/Api/Insight.Api/Constants/Locations.cs @@ -1,9 +1,8 @@ using Insight.Domain.Constants; -namespace Insight.Api +namespace Insight.Api; + +public static class Locations { - public static class Locations - { - public static DirectoryInfo UpdatesPath { get; } = new DirectoryInfo($"{Configuration.AppDirectory?.FullName}/files/updates"); - } + public static DirectoryInfo UpdatesPath { get; } = new DirectoryInfo($"{Configuration.AppDirectory?.FullName}/files/updates"); } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/AccountController.cs b/src/Api/Insight.Api/Controllers/AccountController.cs index 21756f3..3510438 100644 --- a/src/Api/Insight.Api/Controllers/AccountController.cs +++ b/src/Api/Insight.Api/Controllers/AccountController.cs @@ -4,73 +4,72 @@ using Insight.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Insight.Api.Controllers +namespace Insight.Api.Controllers; + +[ApiController, Route("api/accounts")] +public class AccountController : ControllerBase { - [ApiController, Route("api/accounts")] - public class AccountController : ControllerBase + private readonly IdentityService _identityService; + private readonly AccountService _accountService; + private readonly ILogger _logger; + + public AccountController(IdentityService identityService, AccountService accountService, ILogger logger) { - private readonly IdentityService _identityService; - private readonly AccountService _accountService; - private readonly ILogger _logger; + _identityService = identityService; + _accountService = accountService; + _logger = logger; + } - public AccountController(IdentityService identityService, AccountService accountService, ILogger logger) + [HttpGet, Authorize] + public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + { + try { - _identityService = identityService; - _accountService = accountService; - _logger = logger; + var result = await _accountService.GetAsync( + offset: request.Offset, + limit: request.Limit, + request: Request, + response: Response, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return Ok(result); } - - [HttpGet, Authorize] - public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + catch (UnauthorizedAccessException ex) { - try - { - var result = await _accountService.GetAsync( - offset: request.Offset, - limit: request.Limit, - request: Request, - response: Response, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.ToString()); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return Unauthorized(ex.ToString()); } - - [HttpPost("register"), Authorize] - public async Task Register([FromBody] RegistrationModel model) + catch (Exception ex) { - if (string.IsNullOrWhiteSpace(model.Email)) return BadRequest("Email Required"); - if (string.IsNullOrWhiteSpace(model.Password)) return BadRequest("Password Required"); - if (string.IsNullOrWhiteSpace(model.ConfirmPassword)) return BadRequest("Password Confirmation Required"); - - if (model.Password != model.ConfirmPassword) return BadRequest("Passwords do not match"); - - try - { - var result = await _identityService.CreateUserAsync(model.Email, model.Password).ConfigureAwait(false); - - if (result.Succeeded is false) return BadRequest(result.Errors); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.Message); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - - //await _userManager.AddToRoleAsync(user, "Visitor"); - - return Ok(model.Email); + return BadRequest(ex.Message); } } + + [HttpPost("register"), Authorize] + public async Task Register([FromBody] RegistrationModel model) + { + if (string.IsNullOrWhiteSpace(model.Email)) return BadRequest("Email Required"); + if (string.IsNullOrWhiteSpace(model.Password)) return BadRequest("Password Required"); + if (string.IsNullOrWhiteSpace(model.ConfirmPassword)) return BadRequest("Password Confirmation Required"); + + if (model.Password != model.ConfirmPassword) return BadRequest("Passwords do not match"); + + try + { + var result = await _identityService.CreateUserAsync(model.Email, model.Password).ConfigureAwait(false); + + if (result.Succeeded is false) return BadRequest(result.Errors); + } + catch (UnauthorizedAccessException ex) + { + return Unauthorized(ex.Message); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + + //await _userManager.AddToRoleAsync(user, "Visitor"); + + return Ok(model.Email); + } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/AgentController.cs b/src/Api/Insight.Api/Controllers/AgentController.cs index cc0f6b1..0a1e5b9 100644 --- a/src/Api/Insight.Api/Controllers/AgentController.cs +++ b/src/Api/Insight.Api/Controllers/AgentController.cs @@ -3,42 +3,41 @@ using Insight.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Insight.Api.Controllers +namespace Insight.Api.Controllers; + +[ApiController, Route("api/agents")] +public class AgentController : ControllerBase { - [ApiController, Route("api/agents")] - public class AgentController : ControllerBase + private readonly AgentService _agentService; + private readonly ILogger _logger; + + public AgentController(AgentService agentService, ILogger logger) { - private readonly AgentService _agentService; - private readonly ILogger _logger; + _agentService = agentService; + _logger = logger; + } - public AgentController(AgentService agentService, ILogger logger) + [HttpGet, Authorize] + public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + { + try { - _agentService = agentService; - _logger = logger; + var result = await _agentService.GetAsync( + offset: request.Offset, + limit: request.Limit, + request: Request, + response: Response, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return Ok(result); } - - [HttpGet, Authorize] - public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + catch (UnauthorizedAccessException ex) { - try - { - var result = await _agentService.GetAsync( - offset: request.Offset, - limit: request.Limit, - request: Request, - response: Response, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.ToString()); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return Unauthorized(ex.ToString()); + } + catch (Exception ex) + { + return BadRequest(ex.Message); } } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/CustomerController.cs b/src/Api/Insight.Api/Controllers/CustomerController.cs index 6733f81..4aedc19 100644 --- a/src/Api/Insight.Api/Controllers/CustomerController.cs +++ b/src/Api/Insight.Api/Controllers/CustomerController.cs @@ -3,42 +3,41 @@ using Insight.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Insight.Api.Controllers +namespace Insight.Api.Controllers; + +[ApiController, Route("api/customers")] +public class CustomerController : ControllerBase { - [ApiController, Route("api/customers")] - public class CustomerController : ControllerBase + private readonly CustomerService _customerService; + private readonly ILogger _logger; + + public CustomerController(CustomerService customerService, ILogger logger) { - private readonly CustomerService _customerService; - private readonly ILogger _logger; + _customerService = customerService; + _logger = logger; + } - public CustomerController(CustomerService customerService, ILogger logger) + [HttpGet, Authorize] + public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + { + try { - _customerService = customerService; - _logger = logger; + var result = await _customerService.GetAsync( + offset: request.Offset, + limit: request.Limit, + request: Request, + response: Response, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return Ok(result); } - - [HttpGet, Authorize] - public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + catch (UnauthorizedAccessException ex) { - try - { - var result = await _customerService.GetAsync( - offset: request.Offset, - limit: request.Limit, - request: Request, - response: Response, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.ToString()); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return Unauthorized(ex.ToString()); + } + catch (Exception ex) + { + return BadRequest(ex.Message); } } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/HostController.cs b/src/Api/Insight.Api/Controllers/HostController.cs index bd42dac..d8eb5c5 100644 --- a/src/Api/Insight.Api/Controllers/HostController.cs +++ b/src/Api/Insight.Api/Controllers/HostController.cs @@ -3,42 +3,41 @@ using Insight.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Insight.Api.Controllers +namespace Insight.Api.Controllers; + +[ApiController, Route("api/hosts")] +public class HostController : ControllerBase { - [ApiController, Route("api/hosts")] - public class HostController : ControllerBase + private readonly HostService _hostService; + private readonly ILogger _logger; + + public HostController(HostService hostService, ILogger logger) { - private readonly HostService _hostService; - private readonly ILogger _logger; + _hostService = hostService; + _logger = logger; + } - public HostController(HostService hostService, ILogger logger) + [HttpGet, Authorize] + public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + { + try { - _hostService = hostService; - _logger = logger; + var result = await _hostService.GetAsync( + offset: request.Offset, + limit: request.Limit, + request: Request, + response: Response, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return Ok(result); } - - [HttpGet, Authorize] - public async Task Get([FromQuery] PagedDataRequest request, CancellationToken cancellationToken) + catch (UnauthorizedAccessException ex) { - try - { - var result = await _hostService.GetAsync( - offset: request.Offset, - limit: request.Limit, - request: Request, - response: Response, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.ToString()); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return Unauthorized(ex.ToString()); + } + catch (Exception ex) + { + return BadRequest(ex.Message); } } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/InventoryController.cs b/src/Api/Insight.Api/Controllers/InventoryController.cs index 2a47dff..4327843 100644 --- a/src/Api/Insight.Api/Controllers/InventoryController.cs +++ b/src/Api/Insight.Api/Controllers/InventoryController.cs @@ -8,66 +8,65 @@ using MongoDB.Driver; using System.Text.Json.Serialization; using System.Text.RegularExpressions; -namespace Insight.Api.Controllers +namespace Insight.Api.Controllers; + +[ApiController, Route("api/inventory")] +public class InventoryController : ControllerBase { - [ApiController, Route("api/inventory")] - public class InventoryController : ControllerBase + private readonly InventoryService _inventoryService; + private readonly ILogger _logger; + + public InventoryController(InventoryService inventoryService, ILogger logger) { - private readonly InventoryService _inventoryService; - private readonly ILogger _logger; + _inventoryService = inventoryService; + _logger = logger; + } - public InventoryController(InventoryService inventoryService, ILogger logger) + [HttpGet, Authorize] + public async Task Get([FromQuery] HostApplicationEntity request, [FromQuery] PagedDataRequest meta, CancellationToken cancellationToken) + { + try { - _inventoryService = inventoryService; - _logger = logger; + var filter = Builders.Filter.Empty; + + if (request.Id is not null) + filter &= Builders.Filter.Eq(p => p.Id, request.Id); + + if (request.Host is not null) + filter &= Builders.Filter.Eq(p => p.Host, request.Host); + + if (request.Name is not null) + filter &= Builders.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(request.Name, RegexOptions.IgnoreCase))); + + var result = await _inventoryService.GetAsync( + filter: filter, + offset: meta.Offset, + limit: meta.Limit, + request: Request, + response: Response, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return Ok(result); } - - [HttpGet, Authorize] - public async Task Get([FromQuery] HostApplicationEntity request, [FromQuery] PagedDataRequest meta, CancellationToken cancellationToken) + catch (UnauthorizedAccessException ex) { - try - { - var filter = Builders.Filter.Empty; - - if (request.Id is not null) - filter &= Builders.Filter.Eq(p => p.Id, request.Id); - - if (request.Host is not null) - filter &= Builders.Filter.Eq(p => p.Host, request.Host); - - if (request.Name is not null) - filter &= Builders.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(request.Name, RegexOptions.IgnoreCase))); - - var result = await _inventoryService.GetAsync( - filter: filter, - offset: meta.Offset, - limit: meta.Limit, - request: Request, - response: Response, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.ToString()); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return Unauthorized(ex.ToString()); } - - public class ApplicationRequest + catch (Exception ex) { - [JsonPropertyName("id")] - public string? Id { get; set; } - - [JsonPropertyName("host")] - public string? Host { get; set; } - - [JsonPropertyName("name")] - public string? Name { get; set; } + return BadRequest(ex.Message); } } + + public class ApplicationRequest + { + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("host")] + public string? Host { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/SetupController.cs b/src/Api/Insight.Api/Controllers/SetupController.cs index 913e294..831a922 100644 --- a/src/Api/Insight.Api/Controllers/SetupController.cs +++ b/src/Api/Insight.Api/Controllers/SetupController.cs @@ -1,30 +1,29 @@ using Microsoft.AspNetCore.Mvc; -namespace Insight.Server.Controllers +namespace Insight.Server.Controllers; + +[ApiController, Route("api/setup")] +public class SetupController : ControllerBase { - [ApiController, Route("api/setup")] - public class SetupController : ControllerBase + private readonly ILogger _logger; + + public SetupController(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public SetupController(ILogger logger) + [HttpGet("windows")] + public async Task GetAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("[{method}] {route} => {ep}", Request.Method, Request.HttpContext.Request.Path, Request.HttpContext.Connection.RemoteIpAddress); + + var files = new DirectoryInfo($"{Domain.Constants.Configuration.AppDirectory?.FullName}/files/setup").GetFiles(); + + if (files.Length == 0) { - _logger = logger; + return NotFound(); } - [HttpGet("windows")] - public async Task GetAsync(CancellationToken cancellationToken) - { - _logger.LogInformation("[{method}] {route} => {ep}", Request.Method, Request.HttpContext.Request.Path, Request.HttpContext.Connection.RemoteIpAddress); - - var files = new DirectoryInfo($"{Domain.Constants.Configuration.AppDirectory?.FullName}/files/setup").GetFiles(); - - if (files.Length == 0) - { - return NotFound(); - } - - return File(await System.IO.File.ReadAllBytesAsync(files.OrderBy(p => p.LastWriteTime).First().FullName, cancellationToken), "application/zip", "setup-win64.zip"); - } + return File(await System.IO.File.ReadAllBytesAsync(files.OrderBy(p => p.LastWriteTime).First().FullName, cancellationToken), "application/zip", "setup-win64.zip"); } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/TokenController.cs b/src/Api/Insight.Api/Controllers/TokenController.cs index 372a066..9c36083 100644 --- a/src/Api/Insight.Api/Controllers/TokenController.cs +++ b/src/Api/Insight.Api/Controllers/TokenController.cs @@ -3,83 +3,82 @@ using Insight.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Insight.Api.Controllers +namespace Insight.Api.Controllers; + +[ApiController, Route("api/token", Order = 0)] +public class TokenController : ControllerBase { - [ApiController, Route("api/token", Order = 0)] - public class TokenController : ControllerBase + private readonly TokenService _tokenService; + + public TokenController(TokenService tokenService) { - private readonly TokenService _tokenService; + _tokenService = tokenService; + } - public TokenController(TokenService tokenService) + /// + /// Access Token Request + /// + [HttpPost, AllowAnonymous] + public async Task Authentication([FromBody] TokenRequest request) + { + try { - _tokenService = tokenService; + var result = await _tokenService.GetAsync(request.Username, request.Password, request.Code, HttpContext.Connection.RemoteIpAddress).ConfigureAwait(false); + return Ok(result); } - - /// - /// Access Token Request - /// - [HttpPost, AllowAnonymous] - public async Task Authentication([FromBody] TokenRequest request) + catch (UnauthorizedAccessException ex) { - try - { - var result = await _tokenService.GetAsync(request.Username, request.Password, request.Code, HttpContext.Connection.RemoteIpAddress).ConfigureAwait(false); - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.Message); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return Unauthorized(ex.Message); } - - /// - /// Refresh Token Request - /// - [HttpPost("refresh"), AllowAnonymous] - public async Task Refresh([FromBody] TokenRefreshRequest request) + catch (Exception ex) { - if (string.IsNullOrWhiteSpace(request.Token)) return BadRequest("Refresh Token Required"); - - try - { - var result = await _tokenService.RefreshAsync(request.Token, HttpContext.Connection.RemoteIpAddress).ConfigureAwait(false); - return Ok(result); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.Message); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + return BadRequest(ex.Message); } + } - /// - /// Revoke Token Request - /// - [HttpPost("revoke"), AllowAnonymous] - public async Task Revoke([FromBody] TokenRevokeRequest request) + /// + /// Refresh Token Request + /// + [HttpPost("refresh"), AllowAnonymous] + public async Task Refresh([FromBody] TokenRefreshRequest request) + { + if (string.IsNullOrWhiteSpace(request.Token)) return BadRequest("Refresh Token Required"); + + try { - if (string.IsNullOrWhiteSpace(request.Token)) return BadRequest("Refresh Token Required"); + var result = await _tokenService.RefreshAsync(request.Token, HttpContext.Connection.RemoteIpAddress).ConfigureAwait(false); + return Ok(result); + } + catch (UnauthorizedAccessException ex) + { + return Unauthorized(ex.Message); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } - try - { - await _tokenService.RevokeAsync(request.Token, request.Reason ?? "revoked by user", HttpContext.Connection.RemoteIpAddress).ConfigureAwait(false); - return Ok(); - } - catch (UnauthorizedAccessException ex) - { - return Unauthorized(ex.Message); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } + /// + /// Revoke Token Request + /// + [HttpPost("revoke"), AllowAnonymous] + public async Task Revoke([FromBody] TokenRevokeRequest request) + { + if (string.IsNullOrWhiteSpace(request.Token)) return BadRequest("Refresh Token Required"); + + try + { + await _tokenService.RevokeAsync(request.Token, request.Reason ?? "revoked by user", HttpContext.Connection.RemoteIpAddress).ConfigureAwait(false); + return Ok(); + } + catch (UnauthorizedAccessException ex) + { + return Unauthorized(ex.Message); + } + catch (Exception ex) + { + return BadRequest(ex.Message); } } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Controllers/UpdateController.cs b/src/Api/Insight.Api/Controllers/UpdateController.cs index 6ff09df..173a636 100644 --- a/src/Api/Insight.Api/Controllers/UpdateController.cs +++ b/src/Api/Insight.Api/Controllers/UpdateController.cs @@ -2,92 +2,91 @@ using Insight.Domain.Models; using Microsoft.AspNetCore.Mvc; -namespace Insight.Server.Controllers +namespace Insight.Server.Controllers; + +[ApiController, Route("api/update")] +public class UpdateController : ControllerBase { - [ApiController, Route("api/update")] - public class UpdateController : ControllerBase + private readonly ILogger _logger; + + public UpdateController(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public UpdateController(ILogger logger) + [HttpGet("updater/windows")] + public IActionResult UpdaterWindows() + { + _logger.LogInformation("[{method}] {route} => {ep}", Request.Method, Request.HttpContext.Request.Path, Request.HttpContext.Connection.RemoteIpAddress); + + var updateDir = new DirectoryInfo($"{Locations.UpdatesPath}/updater/windows"); + + if (updateDir.Exists is false) { - _logger = logger; + return NotFound(); } - [HttpGet("updater/windows")] - public IActionResult UpdaterWindows() + var updateLock = new FileInfo($"{updateDir.FullName}/.lock"); + + if (updateLock.Exists) { - _logger.LogInformation("[{method}] {route} => {ep}", Request.Method, Request.HttpContext.Request.Path, Request.HttpContext.Connection.RemoteIpAddress); - - var updateDir = new DirectoryInfo($"{Locations.UpdatesPath}/updater/windows"); - - if (updateDir.Exists is false) - { - return NotFound(); - } - - var updateLock = new FileInfo($"{updateDir.FullName}/.lock"); - - if (updateLock.Exists) - { - return NotFound("locked"); - } - - var versions = updateDir.GetFiles("*.zip", SearchOption.TopDirectoryOnly); - - if (versions is null || versions.Any() is false) return NotFound(); - - var latest = versions.OrderBy(x => x.Name).FirstOrDefault(); - - if (latest is null) return NotFound(); - - var relPath = $"{Path.GetRelativePath($"{Domain.Constants.Configuration.AppDirectory?.FullName}", latest.FullName)}"; - - if (Version.TryParse(Path.GetFileNameWithoutExtension(latest.Name), out var fileversion) is false) return NotFound(); - - return Ok(new UpdateResponse - { - Version = fileversion, - Uri = new Uri($"{Request.Scheme}://{Request.Host}/api/{relPath}") - }); + return NotFound("locked"); } - [HttpGet("agent/windows")] - public IActionResult AgentWindows() + var versions = updateDir.GetFiles("*.zip", SearchOption.TopDirectoryOnly); + + if (versions is null || versions.Any() is false) return NotFound(); + + var latest = versions.OrderBy(x => x.Name).FirstOrDefault(); + + if (latest is null) return NotFound(); + + var relPath = $"{Path.GetRelativePath($"{Domain.Constants.Configuration.AppDirectory?.FullName}", latest.FullName)}"; + + if (Version.TryParse(Path.GetFileNameWithoutExtension(latest.Name), out var fileversion) is false) return NotFound(); + + return Ok(new UpdateResponse { - _logger.LogInformation("[{method}] {route} => {ep}", Request.Method, Request.HttpContext.Request.Path, Request.HttpContext.Connection.RemoteIpAddress); + Version = fileversion, + Uri = new Uri($"{Request.Scheme}://{Request.Host}/api/{relPath}") + }); + } - var updateDir = new DirectoryInfo($"{Locations.UpdatesPath}/agent/windows"); + [HttpGet("agent/windows")] + public IActionResult AgentWindows() + { + _logger.LogInformation("[{method}] {route} => {ep}", Request.Method, Request.HttpContext.Request.Path, Request.HttpContext.Connection.RemoteIpAddress); - if (updateDir.Exists is false) - { - return NotFound(); - } + var updateDir = new DirectoryInfo($"{Locations.UpdatesPath}/agent/windows"); - var updateLock = new FileInfo($"{updateDir.FullName}/.lock"); - - if (updateLock.Exists) - { - return NotFound("locked"); - } - - var versions = updateDir.GetFiles("*.zip", SearchOption.TopDirectoryOnly); - - if (versions is null || versions.Any() is false) return NotFound(); - - var latest = versions.OrderBy(x => x.Name).FirstOrDefault(); - - if (latest is null) return NotFound(); - - var relPath = $"{Path.GetRelativePath($"{Domain.Constants.Configuration.AppDirectory?.FullName}", latest.FullName)}"; - - if (Version.TryParse(Path.GetFileNameWithoutExtension(latest.Name), out var fileversion) is false) return NotFound(); - - return Ok(new UpdateResponse - { - Version = fileversion, - Uri = new Uri($"{Request.Scheme}://{Request.Host}/api/{relPath}") - }); + if (updateDir.Exists is false) + { + return NotFound(); } + + var updateLock = new FileInfo($"{updateDir.FullName}/.lock"); + + if (updateLock.Exists) + { + return NotFound("locked"); + } + + var versions = updateDir.GetFiles("*.zip", SearchOption.TopDirectoryOnly); + + if (versions is null || versions.Any() is false) return NotFound(); + + var latest = versions.OrderBy(x => x.Name).FirstOrDefault(); + + if (latest is null) return NotFound(); + + var relPath = $"{Path.GetRelativePath($"{Domain.Constants.Configuration.AppDirectory?.FullName}", latest.FullName)}"; + + if (Version.TryParse(Path.GetFileNameWithoutExtension(latest.Name), out var fileversion) is false) return NotFound(); + + return Ok(new UpdateResponse + { + Version = fileversion, + Uri = new Uri($"{Request.Scheme}://{Request.Host}/api/{relPath}") + }); } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Extensions/ServiceExtensions.cs b/src/Api/Insight.Api/Extensions/ServiceExtensions.cs index 46b91a7..afd32de 100644 --- a/src/Api/Insight.Api/Extensions/ServiceExtensions.cs +++ b/src/Api/Insight.Api/Extensions/ServiceExtensions.cs @@ -2,58 +2,57 @@ using Microsoft.OpenApi.Models; using System.Reflection; -namespace Insight.Api.Hosting +namespace Insight.Api.Hosting; + +public static class ServiceExtensions { - public static class ServiceExtensions + internal static IServiceCollection AddSwaggerServices(this IServiceCollection services, IConfiguration configuration) { - internal static IServiceCollection AddSwaggerServices(this IServiceCollection services, IConfiguration configuration) + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(options => { - services.AddEndpointsApiExplorer(); - services.AddSwaggerGen(options => + options.SwaggerDoc("v1", new OpenApiInfo { - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "Insight API", - Version = "v1" - }); - - options.AddSecurityDefinition(name: "Bearer", securityScheme: new OpenApiSecurityScheme - { - Name = "Authorization", - Description = "Enter the Bearer Authorization string as following: `Bearer Generated-JWT-Token`", - In = ParameterLocation.Header, - Type = SecuritySchemeType.ApiKey, - Scheme = "Bearer", - BearerFormat = "JWT", - - Reference = new OpenApiReference - { - Id = JwtBearerDefaults.AuthenticationScheme, - Type = ReferenceType.SecurityScheme - } - }); - - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - In = ParameterLocation.Header, - Reference = new OpenApiReference - { - Id = "Bearer", - Type = ReferenceType.SecurityScheme - } - }, - new List() - } - }); - - var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; - options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + Title = "Insight API", + Version = "v1" }); - return services; - } + options.AddSecurityDefinition(name: "Bearer", securityScheme: new OpenApiSecurityScheme + { + Name = "Authorization", + Description = "Enter the Bearer Authorization string as following: `Bearer Generated-JWT-Token`", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer", + BearerFormat = "JWT", + + Reference = new OpenApiReference + { + Id = JwtBearerDefaults.AuthenticationScheme, + Type = ReferenceType.SecurityScheme + } + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Reference = new OpenApiReference + { + Id = "Bearer", + Type = ReferenceType.SecurityScheme + } + }, + new List() + } + }); + + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + }); + + return services; } } \ No newline at end of file diff --git a/src/Api/Insight.Api/Insight.Api.csproj b/src/Api/Insight.Api/Insight.Api.csproj index 4d54881..2d4f1d9 100644 --- a/src/Api/Insight.Api/Insight.Api.csproj +++ b/src/Api/Insight.Api/Insight.Api.csproj @@ -4,13 +4,10 @@ net7.0 Insight api - 2025.2.24.0 - 2025.2.24.0 + 2023.12.14.0 Insight.Api enable enable - none - true 4ae1d3bf-869e-4963-8a19-35634507d3b3 false @@ -33,17 +30,10 @@ - + - - - - - - - diff --git a/src/Api/Insight.Api/Models/RegistrationModel.cs b/src/Api/Insight.Api/Models/RegistrationModel.cs index 206b2dc..0794854 100644 --- a/src/Api/Insight.Api/Models/RegistrationModel.cs +++ b/src/Api/Insight.Api/Models/RegistrationModel.cs @@ -1,23 +1,22 @@ using System.ComponentModel.DataAnnotations; -namespace Insight.Api.Models +namespace Insight.Api.Models; + +public class RegistrationModel { - public class RegistrationModel - { - public string? FirstName { get; set; } - public string? LastName { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } - [Required(ErrorMessage = "Email is required")] - [EmailAddress] - public string? Email { get; set; } + [Required(ErrorMessage = "Email is required")] + [EmailAddress] + public string? Email { get; set; } - [Required(ErrorMessage = "Password is required")] - [DataType(DataType.Password)] - public string? Password { get; set; } + [Required(ErrorMessage = "Password is required")] + [DataType(DataType.Password)] + public string? Password { get; set; } - [Required(ErrorMessage = "Password is required")] - [DataType(DataType.Password)] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string? ConfirmPassword { get; set; } - } + [Required(ErrorMessage = "Password is required")] + [DataType(DataType.Password)] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string? ConfirmPassword { get; set; } } diff --git a/src/Api/Insight.Api/Program.cs b/src/Api/Insight.Api/Program.cs index 0c9e95f..8e2b778 100644 --- a/src/Api/Insight.Api/Program.cs +++ b/src/Api/Insight.Api/Program.cs @@ -3,97 +3,96 @@ using Insight.Domain.Constants; using Insight.Infrastructure; using Microsoft.Extensions.FileProviders; -namespace Insight.Api +namespace Insight.Api; + +internal class Program { - public class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) + var builder = WebApplication.CreateBuilder(args); + builder.Host.UseWindowsService(); + builder.Host.UseSystemd(); + + // LOGGER + builder.Logging.ClearProviders(); + builder.Logging.SetMinimumLevel(LogLevel.Trace); + builder.Logging.AddFilter("Microsoft.AspNetCore", LogLevel.Warning); + + builder.Logging.AddSimpleConsole(options => { - var builder = WebApplication.CreateBuilder(args); - builder.Host.UseWindowsService(); - builder.Host.UseSystemd(); + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; + }); - // LOGGER - builder.Logging.ClearProviders(); - builder.Logging.SetMinimumLevel(LogLevel.Trace); - builder.Logging.AddFilter("Microsoft.AspNetCore", LogLevel.Warning); + builder.Logging.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/api_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); - builder.Logging.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; - }); + // INFRASTRUCTURE + builder.Services.AddDatabase(builder.Configuration); + builder.Services.AddInfrastructureServices(); - builder.Logging.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/api_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); + // IDENTITY + builder.Services.AddIdentityServices(builder.Configuration); + builder.Services.AddBearerAuthentication(builder.Configuration); + builder.Services.AddTokenServices(builder.Configuration); - // INFRASTRUCTURE - builder.Services.AddDatabase(builder.Configuration); - builder.Services.AddInfrastructureServices(); + // SECURITY + builder.Services.AddAuthorization(); - // IDENTITY - builder.Services.AddIdentityServices(builder.Configuration); - builder.Services.AddBearerAuthentication(builder.Configuration); - builder.Services.AddTokenServices(builder.Configuration); + // WEBSERVICES + builder.Services.AddProxyServices(builder.Configuration); + builder.Services.AddRoutingServices(builder.Configuration); + builder.Services.AddControllers(); - // SECURITY - builder.Services.AddAuthorization(); + // SWAGGER + builder.Services.AddSwaggerServices(builder.Configuration); - // WEBSERVICES - builder.Services.AddProxyServices(builder.Configuration); - builder.Services.AddRoutingServices(builder.Configuration); - builder.Services.AddControllers(); + //builder.Services.AddControllers(); + //builder.Services.AddEndpointsApiExplorer(); + //builder.Services.AddSwaggerGen(); - // SWAGGER - builder.Services.AddSwaggerServices(builder.Configuration); + var app = builder.Build(); - //builder.Services.AddControllers(); - //builder.Services.AddEndpointsApiExplorer(); - //builder.Services.AddSwaggerGen(); + // Configure the HTTP request pipeline. + app.UseForwardedHeaders(); - var app = builder.Build(); + if (app.Environment.IsDevelopment()) + { - // Configure the HTTP request pipeline. - app.UseForwardedHeaders(); - - if (app.Environment.IsDevelopment()) - { - - } - - app.UseSwagger(options => - { - options.RouteTemplate = "api/swagger/{documentName}/swagger.json"; - }); - - app.UseSwaggerUI(options => - { - options.DefaultModelsExpandDepth(-1); - options.SwaggerEndpoint("/api/swagger/v1/swagger.json", "v1"); - options.RoutePrefix = "api/swagger"; - }); - - app.UseCors(x => x - .AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader()); - - app.UseAuthorization(); - - app.MapControllers(); - - // STATIC FILES (UPDATES) - var files = new DirectoryInfo($"{Domain.Constants.Configuration.AppDirectory?.FullName}/files"); - files.Create(); - - app.UseStaticFiles(new StaticFileOptions - { - FileProvider = new PhysicalFileProvider(files.FullName), - RequestPath = "/api/files", - ServeUnknownFileTypes = true - }); - - await app.RunAsync(); } + + app.UseSwagger(options => + { + options.RouteTemplate = "api/swagger/{documentName}/swagger.json"; + }); + + app.UseSwaggerUI(options => + { + options.DefaultModelsExpandDepth(-1); + options.SwaggerEndpoint("/api/swagger/v1/swagger.json", "v1"); + options.RoutePrefix = "api/swagger"; + }); + + app.UseCors(x => x + .AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader()); + + app.UseAuthorization(); + + app.MapControllers(); + + // STATIC FILES (UPDATES) + var files = new DirectoryInfo($"{Domain.Constants.Configuration.AppDirectory?.FullName}/files"); + files.Create(); + + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider(files.FullName), + RequestPath = "/api/files", + ServeUnknownFileTypes = true + }); + + await app.RunAsync(); } } \ No newline at end of file diff --git a/src/Api/Insight.Api/appsettings.Development.json b/src/Api/Insight.Api/appsettings.Development.json index 1ccded8..fff92f2 100644 --- a/src/Api/Insight.Api/appsettings.Development.json +++ b/src/Api/Insight.Api/appsettings.Development.json @@ -1,7 +1,7 @@ { "AllowedHosts": "*", "Urls": "http://127.0.0.1:5000", - "database": "mongodb://10.22.70.40:32768", + "database": "mongodb://db.insight.local:27017", "jwt.key": "x5dcaE8fiBmHfgsNrwIEtSWzZkz6gpouzKOIgEiVjxJnW28V1aUnYXF19IcnF5x", "jwt.exp": 3600, "jwt.audience": "http://127.0.0.1:5000", diff --git a/src/Api/Insight.Api/appsettings.json b/src/Api/Insight.Api/appsettings.json index 84eb581..21706b1 100644 --- a/src/Api/Insight.Api/appsettings.json +++ b/src/Api/Insight.Api/appsettings.json @@ -6,4 +6,4 @@ "jwt.exp": 3600, "jwt.audience": "https://insight.webmatic.de/api", "jwt.issuer": "https://insight.webmatic.de/api" -} +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Constants/Configuration.cs b/src/Core/Insight.Domain/Constants/Configuration.cs index f0643e2..812b469 100644 --- a/src/Core/Insight.Domain/Constants/Configuration.cs +++ b/src/Core/Insight.Domain/Constants/Configuration.cs @@ -1,13 +1,12 @@ using System.Net; using System.Reflection; -namespace Insight.Domain.Constants +namespace Insight.Domain.Constants; + +public static class Configuration { - public static class Configuration - { - public static string Hostname => Dns.GetHostEntry("localhost").HostName; - public static Version Version => Assembly.GetEntryAssembly()?.GetName().Version ?? throw new ArgumentNullException("Version"); - public static DirectoryInfo? AppDirectory => string.IsNullOrWhiteSpace(Environment.ProcessPath) ? null : new DirectoryInfo(Environment.ProcessPath).Parent; - public static string DefaultConfig => Path.Combine(AppDirectory?.FullName ?? string.Empty, "config.json"); - } + public static string Hostname => Dns.GetHostEntry("localhost").HostName; + public static Version Version => Assembly.GetEntryAssembly()?.GetName().Version ?? throw new ArgumentNullException("Version"); + public static DirectoryInfo? AppDirectory => string.IsNullOrWhiteSpace(Environment.ProcessPath) ? null : new DirectoryInfo(Environment.ProcessPath).Parent; + public static string DefaultConfig => Path.Combine(AppDirectory?.FullName ?? string.Empty, "config.json"); } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Enums/CategoryEnum.cs b/src/Core/Insight.Domain/Enums/CategoryEnum.cs new file mode 100644 index 0000000..739b5fd --- /dev/null +++ b/src/Core/Insight.Domain/Enums/CategoryEnum.cs @@ -0,0 +1,13 @@ +namespace Insight.Domain.Enums; + +public enum CategoryEnum +{ + Network = 1, + System = 2, + Application = 3, + Security = 4, + Monitoring = 5, + Task = 6, + Printer = 7, + RDP = 8 +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Enums/DispatchEnum.cs b/src/Core/Insight.Domain/Enums/DispatchEnum.cs new file mode 100644 index 0000000..da38f84 --- /dev/null +++ b/src/Core/Insight.Domain/Enums/DispatchEnum.cs @@ -0,0 +1,8 @@ +namespace Insight.Domain.Enums; + +public enum DispatchEnum +{ + Pending = 1, + Failure = 2, + Success = 3, +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Enums/RemoteControlMode.cs b/src/Core/Insight.Domain/Enums/RemoteControlMode.cs new file mode 100644 index 0000000..2c764e5 --- /dev/null +++ b/src/Core/Insight.Domain/Enums/RemoteControlMode.cs @@ -0,0 +1,8 @@ +namespace Insight.Domain.Enums; + +public enum RemoteControlMode +{ + Unknown, + Unattended, + Attended +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Enums/SessionEndReasons.cs b/src/Core/Insight.Domain/Enums/SessionEndReasons.cs new file mode 100644 index 0000000..bf01052 --- /dev/null +++ b/src/Core/Insight.Domain/Enums/SessionEndReasons.cs @@ -0,0 +1,7 @@ +namespace Insight.Domain.Enums; + +public enum SessionEndReasons +{ + Logoff = 1, + SystemShutdown = 2 +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Enums/SessionSwitchReason.cs b/src/Core/Insight.Domain/Enums/SessionSwitchReason.cs new file mode 100644 index 0000000..3785ee9 --- /dev/null +++ b/src/Core/Insight.Domain/Enums/SessionSwitchReason.cs @@ -0,0 +1,14 @@ +namespace Insight.Domain.Enums; + +public enum SessionSwitchReason +{ + ConsoleConnect = 1, + ConsoleDisconnect = 2, + RemoteConnect = 3, + RemoteDisconnect = 4, + SessionLogon = 5, + SessionLogoff = 6, + SessionLock = 7, + SessionUnlock = 8, + SessionRemoteControl = 9 +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Enums/StatusEnum.cs b/src/Core/Insight.Domain/Enums/StatusEnum.cs new file mode 100644 index 0000000..41abd57 --- /dev/null +++ b/src/Core/Insight.Domain/Enums/StatusEnum.cs @@ -0,0 +1,8 @@ +namespace Insight.Domain.Enums; + +public enum StatusEnum +{ + Information = 1, + Warning = 2, + Error = 3 +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Insight.Domain.csproj b/src/Core/Insight.Domain/Insight.Domain.csproj index d3ad1de..88c6256 100644 --- a/src/Core/Insight.Domain/Insight.Domain.csproj +++ b/src/Core/Insight.Domain/Insight.Domain.csproj @@ -6,12 +6,14 @@ enable Insight.Domain Insight - 2025.2.24.0 - 2025.2.24.0 - none - true + 2023.12.14.0 + + + + + none @@ -20,4 +22,4 @@ none - + \ No newline at end of file diff --git a/src/Core/Insight.Domain/Interfaces/IMessageHandler.cs b/src/Core/Insight.Domain/Interfaces/IMessageHandler.cs new file mode 100644 index 0000000..1c0d3cb --- /dev/null +++ b/src/Core/Insight.Domain/Interfaces/IMessageHandler.cs @@ -0,0 +1,8 @@ +using Insight.Domain.Network; + +namespace Insight.Domain.Interfaces; + +public partial interface IMessageHandler +{ + ValueTask HandleAsync(TSender sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage; +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Models/Result.cs b/src/Core/Insight.Domain/Models/Result.cs new file mode 100644 index 0000000..85f6934 --- /dev/null +++ b/src/Core/Insight.Domain/Models/Result.cs @@ -0,0 +1,148 @@ +using MessagePack; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace RemoteControl.Shared; + +/// +/// Describes the success or failure of any kind of operation. +/// +[DataContract] +public class Result +{ + /// + /// For serialization only. + /// + [SerializationConstructor] + [JsonConstructor] + public Result() { } + + public Result(bool isSuccess, string reason = "", Exception? exception = null) + { + if (!isSuccess && exception is null && string.IsNullOrWhiteSpace(reason)) + { + throw new ArgumentException("A reason or exception must be supplied for an unsuccessful result."); + } + + IsSuccess = isSuccess; + Exception = exception; + Reason = reason; + } + + [IgnoreDataMember] + public Exception? Exception { get; init; } + + [IgnoreDataMember] + [MemberNotNullWhen(true, nameof(Exception))] + public bool HadException => Exception is not null; + + [DataMember] + public bool IsSuccess { get; init; } + + [DataMember] + public string Reason { get; init; } = string.Empty; + + + public static Result Fail(string reason) + { + return new Result(false, reason); + } + + public static Result Fail(Exception ex) + { + return new Result(false, string.Empty, ex); + } + + public static Result Fail(string reason) + { + return new Result(reason); + } + + public static Result Fail(Exception ex) + { + return new Result(ex); + } + + public static Result Ok() + { + return new Result(true); + } + + public static Result Ok(T value) + { + return new Result(value); + } +} + + +/// +/// Describes the success or failure of any kind of operation. +/// +[DataContract] +public class Result +{ + /// + /// Returns a successful result with the given value. + /// + /// + public Result(T value) + { + IsSuccess = true; + Value = value; + } + + /// + /// Returns an unsuccessful result with the given exception. + /// + /// + public Result(Exception exception) + { + IsSuccess = false; + Exception = exception; + Reason = exception.Message; + } + + /// + /// Returns an unsuccessful result with the given reason. + /// + /// + /// + public Result(string reason) + { + IsSuccess = false; + Reason = reason; + } + + /// + /// For serialization only. + /// + [SerializationConstructor] + [JsonConstructor] + public Result() { } + + public Result(Exception? exception, bool isSuccess, string reason, T? value) + { + Exception = exception; + IsSuccess = isSuccess; + Reason = reason; + Value = value; + } + + [IgnoreDataMember] + public Exception? Exception { get; init; } + + [IgnoreDataMember] + [MemberNotNullWhen(true, nameof(Exception))] + public bool HadException => Exception is not null; + + [DataMember] + [MemberNotNullWhen(true, nameof(Value))] + public bool IsSuccess { get; init; } + + [DataMember] + public string Reason { get; init; } = string.Empty; + + [DataMember] + public T? Value { get; init; } +} diff --git a/src/Core/Insight.Domain/Models/Token.cs b/src/Core/Insight.Domain/Models/Token.cs index 66d91bc..44718f3 100644 --- a/src/Core/Insight.Domain/Models/Token.cs +++ b/src/Core/Insight.Domain/Models/Token.cs @@ -1,55 +1,54 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -namespace Insight.Domain.Models +namespace Insight.Domain.Models; + +public class TokenRequest { - public class TokenRequest + [JsonPropertyName("username"), EmailAddress, Required] + public string? Username { get; set; } + + [JsonPropertyName("password"), DataType(DataType.Password), Required] + public string? Password { get; set; } + + [JsonPropertyName("code"), DataType(DataType.Text)] + public string? Code { get; set; } +} + +public class TokenResponse +{ + [JsonPropertyName("access_token")] + public string? AccessToken { get; set; } + + [JsonPropertyName("expires_in")] + public int ExpireInSeconds { get; set; } + + [JsonPropertyName("refresh_token")] + public string? RefreshToken { get; set; } +} + +public class TokenRevokeRequest +{ + [JsonPropertyName("token"), Required] + public string? Token { get; set; } + + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + public TokenRevokeRequest(string token, string? reason) { - [JsonPropertyName("username"), EmailAddress, Required] - public string Username { get; set; } - - [JsonPropertyName("password"), DataType(DataType.Password), Required] - public string Password { get; set; } - - [JsonPropertyName("code"), DataType(DataType.Text)] - public string? Code { get; set; } + Token = token; + Reason = reason; } +} - public class TokenResponse +public class TokenRefreshRequest +{ + [JsonPropertyName("token"), Required] + public string? Token { get; set; } + + public TokenRefreshRequest(string token) { - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } - - [JsonPropertyName("expires_in")] - public int ExpireInSeconds { get; set; } - - [JsonPropertyName("refresh_token")] - public string RefreshToken { get; set; } - } - - public class TokenRevokeRequest - { - [JsonPropertyName("token"), Required] - public string Token { get; set; } - - [JsonPropertyName("reason")] - public string? Reason { get; set; } - - public TokenRevokeRequest(string token, string? reason) - { - Token = token; - Reason = reason; - } - } - - public class TokenRefreshRequest - { - [JsonPropertyName("token"), Required] - public string Token { get; set; } - - public TokenRefreshRequest(string token) - { - Token = token; - } + Token = token; } } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Models/Update.cs b/src/Core/Insight.Domain/Models/Update.cs index 6f0fd0b..16258a3 100644 --- a/src/Core/Insight.Domain/Models/Update.cs +++ b/src/Core/Insight.Domain/Models/Update.cs @@ -1,8 +1,7 @@ -namespace Insight.Domain.Models +namespace Insight.Domain.Models; + +public class UpdateResponse { - public class UpdateResponse - { - public Version? Version { get; set; } - public Uri? Uri { get; set; } - } + public Version? Version { get; set; } + public Uri? Uri { get; set; } } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Application.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Application.cs new file mode 100644 index 0000000..fc2db2f --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Application.cs @@ -0,0 +1,32 @@ +using MemoryPack; +using System.Runtime.InteropServices; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Application : IMessage +{ + [MemoryPackOrder(0)] + public string? Name { get; set; } + + [MemoryPackOrder(1)] + public string? Publisher { get; set; } + + [MemoryPackOrder(2)] + public string? Version { get; set; } + + [MemoryPackOrder(3)] + public string? Location { get; set; } + + [MemoryPackOrder(4)] + public string? Source { get; set; } + + [MemoryPackOrder(5)] + public string? Uninstall { get; set; } + + [MemoryPackOrder(6)] + public DateTime? InstallDate { get; set; } + + [MemoryPackOrder(7)] + public Architecture? Architecture { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Authentication.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Authentication.cs new file mode 100644 index 0000000..566d490 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Authentication.cs @@ -0,0 +1,29 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class AuthenticationRequest : IMessage { } + +[MemoryPackable] +public partial class AuthenticationResponse : IMessage +{ + [MemoryPackOrder(0)] + public PlatformType? Platform { get; set; } + + [MemoryPackOrder(1)] + public Guid Serial { get; set; } + + [MemoryPackOrder(2)] + public Version? Version { get; set; } + + [MemoryPackOrder(3)] + public string? Hostname { get; set; } + + public enum PlatformType + { + Unknown = 0, + Windows = 1, + Unix = 2 + } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Commands.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Commands.cs new file mode 100644 index 0000000..e8b32b6 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Commands.cs @@ -0,0 +1,6 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class InventoryRequest : IMessage { } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Drive.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Drive.cs new file mode 100644 index 0000000..8d8d556 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Drive.cs @@ -0,0 +1,95 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Drive : IMessage +{ + [MemoryPackOrder(0)] + public uint? Index { get; set; } + + [MemoryPackOrder(1)] + public string? Id { get; set; } + + [MemoryPackOrder(2)] + public string? Name { get; set; } + + [MemoryPackOrder(3)] + public string? Manufacturer { get; set; } + + [MemoryPackOrder(4)] + public string? SerialNumber { get; set; } + + [MemoryPackOrder(5)] + public ulong? Size { get; set; } + + [MemoryPackOrder(6)] + public string? Status { get; set; } + + [MemoryPackOrder(7)] + public string? InterfaceType { get; set; } + + [MemoryPackOrder(8)] + public string? FirmwareRevision { get; set; } + + [MemoryPackOrder(9)] + public string? PNPDeviceID { get; set; } + + [MemoryPackOrder(10)] + public List? Volumes { get; set; } +} + +[MemoryPackable] +public partial class Volume : IMessage +{ + [MemoryPackOrder(0)] + public uint? Index { get; set; } + + [MemoryPackOrder(1)] + public string? Id { get; set; } + + [MemoryPackOrder(2)] + public string? Name { get; set; } + + [MemoryPackOrder(3)] + public string? SerialNumber { get; set; } + + [MemoryPackOrder(4)] + public ulong? Size { get; set; } + + [MemoryPackOrder(5)] + public ulong? FreeSpace { get; set; } + + [MemoryPackOrder(6)] + public string? Type { get; set; } + + [MemoryPackOrder(7)] + public string? FileSystem { get; set; } + + [MemoryPackOrder(8)] + public bool? Compressed { get; set; } + + [MemoryPackOrder(9)] + public bool? Bootable { get; set; } + + [MemoryPackOrder(10)] + public bool? PrimaryPartition { get; set; } + + [MemoryPackOrder(11)] + public bool? BootPartition { get; set; } + + [MemoryPackOrder(12)] + public ulong? BlockSize { get; set; } + + [MemoryPackOrder(13)] + public ulong? NumberOfBlocks { get; set; } + + [MemoryPackOrder(14)] + public ulong? StartingOffset { get; set; } + + [MemoryPackOrder(15)] + public DriveType? DriveType { get; set; } + + [MemoryPackOrder(16)] + public string? ProviderName { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Event.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Event.cs new file mode 100644 index 0000000..1afb844 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Event.cs @@ -0,0 +1,37 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Event : IMessage +{ + [MemoryPackOrder(0)] + public DateTime? Timestamp { get; set; } + + [MemoryPackOrder(1)] + public StatusType? Status { get; set; } + + [MemoryPackOrder(2)] + public string? Source { get; set; } + + [MemoryPackOrder(3)] + public string? Category { get; set; } + + [MemoryPackOrder(4)] + public int? EventId { get; set; } + + [MemoryPackOrder(5)] + public string? Task { get; set; } + + [MemoryPackOrder(6)] + public string? Message { get; set; } + + public enum StatusType + { + Unknown = 0, + Information = 1, + Warning = 2, + Error = 3, + Critical = 4 + } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Interface.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Interface.cs new file mode 100644 index 0000000..3c985b6 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Interface.cs @@ -0,0 +1,187 @@ +using MemoryPack; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Interface : IMessage +{ + [MemoryPackOrder(0)] + public uint? Index { get; set; } + + [MemoryPackOrder(1)] + public Guid? Guid { get; set; } + + [MemoryPackOrder(2)] + public string? Mac { get; set; } + + [MemoryPackOrder(3)] + public string? Name { get; set; } + + [MemoryPackOrder(4)] + public string? Description { get; set; } + + [MemoryPackOrder(5)] + public string? Manufacturer { get; set; } + + [MemoryPackOrder(6)] + public string? Suffix { get; set; } + + [MemoryPackOrder(7)] + public bool? Physical { get; set; } + + [MemoryPackOrder(8)] + public NetworkInterfaceType? Type { get; set; } + + [MemoryPackOrder(9)] + public OperationalStatus? Status { get; set; } + + [MemoryPackOrder(10)] + public long? Speed { get; set; } + + [MemoryPackOrder(11)] + public long? Ipv4Mtu { get; set; } + + [MemoryPackOrder(12)] + public bool? Ipv4Dhcp { get; set; } + + [MemoryPackOrder(13)] + public bool? Ipv4Forwarding { get; set; } + + [MemoryPackOrder(14)] + public long? Ipv6Mtu { get; set; } + + [MemoryPackOrder(15)] + public long? Sent { get; set; } + + [MemoryPackOrder(16)] + public long? Received { get; set; } + + [MemoryPackOrder(17)] + public long? IncomingPacketsDiscarded { get; set; } + + [MemoryPackOrder(18)] + public long? IncomingPacketsWithErrors { get; set; } + + [MemoryPackOrder(19)] + public long? IncomingUnknownProtocolPackets { get; set; } + + [MemoryPackOrder(20)] + public long? OutgoingPacketsDiscarded { get; set; } + + [MemoryPackOrder(21)] + public long? OutgoingPacketsWithErrors { get; set; } + + [MemoryPackOrder(22)] + public List? Addresses { get; set; } + + [MemoryPackOrder(23)] + public List? Gateways { get; set; } + + [MemoryPackOrder(24)] + public List? Dns { get; set; } + + [MemoryPackOrder(25)] + public List? Dhcp { get; set; } + + [MemoryPackOrder(26)] + public List? Routes { get; set; } +} + +[MemoryPackable] +public partial class Unicast : IMessage +{ + [MemoryPackOrder(0)] + public IPAddress2? IpAddress { get; set; } + + [MemoryPackOrder(1)] + public IPAddress2? Ipv4Mask { get; set; } + + [MemoryPackOrder(2)] + public long? AddressPreferredLifetime { get; set; } + + [MemoryPackOrder(3)] + public long? AddressValidLifetime { get; set; } + + [MemoryPackOrder(4)] + public long? DhcpLeaseLifetime { get; set; } + + [MemoryPackOrder(5)] + public DuplicateAddressDetectionState? DuplicateAddressDetectionState { get; set; } + + [MemoryPackOrder(6)] + public int? PrefixLength { get; set; } + + [MemoryPackOrder(7)] + public PrefixOrigin? PrefixOrigin { get; set; } + + [MemoryPackOrder(8)] + public SuffixOrigin? SuffixOrigin { get; set; } +} + +[MemoryPackable] +public partial class Route : IMessage +{ + [MemoryPackOrder(0)] + public uint? InterfaceIndex { get; set; } + + [MemoryPackOrder(1)] + public IPAddress2? Destination { get; set; } + + [MemoryPackOrder(2)] + public IPAddress2? Gateway { get; set; } + + [MemoryPackOrder(3)] + public string? Mask { get; set; } + + [MemoryPackOrder(4)] + public int? Metric { get; set; } +} + +[MemoryPackable] +public partial class IPAddress2 : IMessage +{ + [MemoryPackOrder(0)] + public AddressFamily? AddressFamily { get; set; } + + [MemoryPackOrder(1)] + public string? Address { get; set; } + + [MemoryPackOrder(2)] + public bool? IsIPv6Teredo { get; set; } + + [MemoryPackOrder(3)] + public bool? IsIPv6SiteLocal { get; set; } + + [MemoryPackOrder(4)] + public bool? IsIPv6Multicast { get; set; } + + [MemoryPackOrder(5)] + public bool? IsIPv6LinkLocal { get; set; } + + [MemoryPackOrder(6)] + public bool? IsIPv4MappedToIPv6 { get; set; } + + [MemoryPackOrder(7)] + public bool? IsIPv6UniqueLocal { get; set; } + + [MemoryPackConstructor] + public IPAddress2() + { + + } + + public IPAddress2(IPAddress address) + { + AddressFamily = address.AddressFamily; + Address = address.ToString(); + IsIPv4MappedToIPv6 = address.IsIPv4MappedToIPv6; + IsIPv6LinkLocal = address.IsIPv6LinkLocal; + IsIPv6Multicast = address.IsIPv6Multicast; + IsIPv6SiteLocal = address.IsIPv6SiteLocal; + IsIPv6Teredo = address.IsIPv6Teredo; + IsIPv6UniqueLocal = address.IsIPv6UniqueLocal; + } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Mainboard.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Mainboard.cs new file mode 100644 index 0000000..463c12b --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Mainboard.cs @@ -0,0 +1,25 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Mainboard : IMessage +{ + [MemoryPackOrder(0)] + public string? Manufacturer { get; set; } + + [MemoryPackOrder(1)] + public string? Model { get; set; } + + [MemoryPackOrder(2)] + public string? Serial { get; set; } + + [MemoryPackOrder(3)] + public string? BiosManufacturer { get; set; } + + [MemoryPackOrder(4)] + public string? BiosVersion { get; set; } + + [MemoryPackOrder(5)] + public DateTime? BiosDate { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Memory.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Memory.cs new file mode 100644 index 0000000..d11ddaf --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Memory.cs @@ -0,0 +1,59 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Memory : IMessage +{ + [MemoryPackOrder(0)] + public uint? Index { get; set; } + + [MemoryPackOrder(1)] + public string? Tag { get; set; } + + [MemoryPackOrder(2)] + public string? Location { get; set; } + + [MemoryPackOrder(3)] + public string? Manufacturer { get; set; } + + [MemoryPackOrder(4)] + public string? Model { get; set; } + + [MemoryPackOrder(5)] + public string? Serial { get; set; } + + [MemoryPackOrder(6)] + public ulong? Capacity { get; set; } + + [MemoryPackOrder(7)] + public uint? Speed { get; set; } + + [MemoryPackOrder(8)] + public uint? Voltage { get; set; } + + [MemoryPackOrder(9)] + public uint? ConfiguredSpeed { get; set; } + + [MemoryPackOrder(10)] + public uint? ConfiguredVoltage { get; set; } +} + +[MemoryPackable] +public partial class MemoryMetric : IMessage +{ + [MemoryPackOrder(0)] + public DateTime? Timestamp { get; set; } + + [MemoryPackOrder(1)] + public float? MemoryAvailable { get; set; } + + [MemoryPackOrder(2)] + public float? MemoryAvailablePercentage { get; set; } + + [MemoryPackOrder(3)] + public float? MemoryUsed { get; set; } + + [MemoryPackOrder(4)] + public float? MemoryUsagePercentage { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/OperationSystem.cs b/src/Core/Insight.Domain/Network/Agent/Messages/OperationSystem.cs new file mode 100644 index 0000000..3eb9602 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/OperationSystem.cs @@ -0,0 +1,26 @@ +using MemoryPack; +using System.Runtime.InteropServices; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class OperationSystem : IMessage +{ + [MemoryPackOrder(0)] + public string? Name { get; set; } + + [MemoryPackOrder(1)] + public string? Version { get; set; } + + [MemoryPackOrder(2)] + public string? SerialNumber { get; set; } + + [MemoryPackOrder(3)] + public Architecture? Architecture { get; set; } + + [MemoryPackOrder(4)] + public bool? Virtual { get; set; } + + [MemoryPackOrder(5)] + public DateTime? InstallDate { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Printer.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Printer.cs new file mode 100644 index 0000000..743fc2c --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Printer.cs @@ -0,0 +1,22 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Printer : IMessage +{ + [MemoryPackOrder(0)] + public string? Name { get; set; } + + [MemoryPackOrder(1)] + public string? Driver { get; set; } + + [MemoryPackOrder(2)] + public string? Port { get; set; } + + [MemoryPackOrder(3)] + public string? Location { get; set; } + + [MemoryPackOrder(4)] + public string? Comment { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Processor.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Processor.cs new file mode 100644 index 0000000..94ce8be --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Processor.cs @@ -0,0 +1,62 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Processor : IMessage +{ + [MemoryPackOrder(0)] + public uint? Index { get; set; } + + [MemoryPackOrder(1)] + public string? Name { get; set; } + + [MemoryPackOrder(2)] + public string? Manufacturer { get; set; } + + [MemoryPackOrder(3)] + public string? SerialNumber { get; set; } + + [MemoryPackOrder(4)] + public string? Socket { get; set; } + + [MemoryPackOrder(5)] + public string? Version { get; set; } + + [MemoryPackOrder(6)] + public string? DeviceId { get; set; } + + [MemoryPackOrder(7)] + public uint? Cores { get; set; } + + [MemoryPackOrder(8)] + public uint? LogicalCores { get; set; } + + [MemoryPackOrder(9)] + public uint? CurrentSpeed { get; set; } + + [MemoryPackOrder(10)] + public uint? MaxSpeed { get; set; } + + [MemoryPackOrder(11)] + public uint? L1Size { get; set; } + + [MemoryPackOrder(12)] + public uint? L2Size { get; set; } + + [MemoryPackOrder(13)] + public uint? L3Size { get; set; } + + [MemoryPackOrder(14)] + public bool? Virtualization { get; set; } +} + +[MemoryPackable] +public partial class ProcessorMetric : IMessage +{ + [MemoryPackOrder(0)] + public DateTime? Timestamp { get; set; } + + [MemoryPackOrder(1)] + public float? ProcessorUsagePercentage { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Query.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Query.cs new file mode 100644 index 0000000..9bd2363 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Query.cs @@ -0,0 +1,61 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Request : IMessage +{ + [MemoryPackOrder(0)] + public string? RequestId { get; set; } + + [MemoryPackOrder(1)] + public string? RequestData { get; set; } +} + +[MemoryPackable] +public partial class Response : IMessage +{ + [MemoryPackOrder(0)] + public string? RequestId { get; set; } + + [MemoryPackOrder(1)] + public string? RequestData { get; set; } + + [MemoryPackOrder(2)] + public string? ResponseId { get; set; } + + [MemoryPackOrder(3)] + public string? ResponseData { get; set; } + + [MemoryPackOrder(4)] + public bool? ResponseError { get; set; } + + [MemoryPackConstructor] + public Response() { } + + public Response(Request request) + { + RequestId = request.RequestId; + RequestData = request.RequestData; + } +} + +//[MemoryPackable] +//public partial class ConsoleQueryRequest : IMessage +//{ +// [MemoryPackOrder(0)] +// public string? Query { get; set; } +//} + +//[MemoryPackable] +//public partial class ConsoleQueryResponse : IMessage +//{ +// [MemoryPackOrder(0)] +// public string? Data { get; set; } + +// [MemoryPackOrder(1)] +// public string? Errors { get; set; } + +// [MemoryPackOrder(2)] +// public bool HadErrors { get; set; } +//} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Service.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Service.cs new file mode 100644 index 0000000..4d7a51b --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Service.cs @@ -0,0 +1,56 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Service : IMessage +{ + [MemoryPackOrder(0)] + public uint? ProcessId { get; set; } + + [MemoryPackOrder(1)] + public string? Name { get; set; } + + [MemoryPackOrder(2)] + public string? Display { get; set; } + + [MemoryPackOrder(3)] + public string? Description { get; set; } + + [MemoryPackOrder(4)] + public string? PathName { get; set; } + + [MemoryPackOrder(5)] + public string? Account { get; set; } + + [MemoryPackOrder(6)] + public bool? Delay { get; set; } + + [MemoryPackOrder(7)] + public ServiceStatus? Status { get; set; } + + [MemoryPackOrder(8)] + public ServiceMode? StartMode { get; set; } + + public enum ServiceStatus + { + Unknown = -1, + Stopped = 1, + StartPending = 2, + StopPending = 3, + Running = 4, + ContinuePending = 5, + PausePending = 6, + Paused = 7 + } + + public enum ServiceMode + { + Unknown = -1, + Boot = 0, + System = 1, + Automatic = 2, + Manual = 3, + Disabled = 4 + } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Session.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Session.cs new file mode 100644 index 0000000..024db30 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Session.cs @@ -0,0 +1,22 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Session : IMessage +{ + [MemoryPackOrder(0)] + public string? Sid { get; set; } + + [MemoryPackOrder(1)] + public string? User { get; set; } + + [MemoryPackOrder(2)] + public string? Type { get; set; } + + [MemoryPackOrder(3)] + public string? Status { get; set; } + + [MemoryPackOrder(4)] + public string? Remote { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Status.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Status.cs new file mode 100644 index 0000000..ec59e1f --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Status.cs @@ -0,0 +1,13 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Status : IMessage +{ + [MemoryPackOrder(0)] + public DateTime Timestamp { get; } = DateTime.Now; + + [MemoryPackOrder(1)] + public TimeSpan Uptime { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/StoragePool.cs b/src/Core/Insight.Domain/Network/Agent/Messages/StoragePool.cs new file mode 100644 index 0000000..d2d22da --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/StoragePool.cs @@ -0,0 +1,294 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class StoragePool : IMessage +{ + [MemoryPackOrder(0)] + public string? UniqueId { get; set; } + + [MemoryPackOrder(1)] + public string? Name { get; set; } + + [MemoryPackOrder(2)] + public string? FriendlyName { get; set; } + + [MemoryPackOrder(3)] + public List? States { get; set; } + + [MemoryPackOrder(4)] + public HealthState? Health { get; set; } + + [MemoryPackOrder(5)] + public RetireMissingPhysicalDisksEnum? RetireMissingPhysicalDisks { get; set; } + + [MemoryPackOrder(6)] + public string? Resiliency { get; set; } + + [MemoryPackOrder(7)] + public bool? IsPrimordial { get; set; } + + [MemoryPackOrder(8)] + public bool? IsReadOnly { get; set; } + + [MemoryPackOrder(9)] + public bool? IsClustered { get; set; } + + [MemoryPackOrder(10)] + public ulong? Size { get; set; } + + [MemoryPackOrder(11)] + public ulong? AllocatedSize { get; set; } + + [MemoryPackOrder(12)] + public ulong? SectorSize { get; set; } + + [MemoryPackOrder(13)] + public List? PhysicalDisks { get; set; } + + [MemoryPackOrder(14)] + public List? VirtualDisks { get; set; } + + public enum OperationalState + { + Unknown = 0, + Other = 1, + OK = 2, + Degraded = 3, + Stressed = 4, + Predictive_Failure = 5, + Error = 6, + Non_Recoverable_Error = 7, + Starting = 8, + Stopping = 9, + Stopped = 10, + In_Service = 11, + No_Contact = 12, + Lost_Communication = 13, + Aborted = 14, + Dormant = 15, + Supporting_Entity_In_Error = 16, + Completed = 17, + Power_Mode = 18, + Relocating = 19 + } + + public enum HealthState + { + Healthy = 0, + Warning = 1, + Unhealthy = 2, + Unknown = 3 + } + + public enum RetireMissingPhysicalDisksEnum + { + Auto = 1, + Always = 2, + Never = 3 + } +} + +[MemoryPackable] +public partial class PhysicalDisk : IMessage +{ + [MemoryPackOrder(0)] + public string? UniqueId { get; set; } + + [MemoryPackOrder(1)] + public string? DeviceId { get; set; } + + [MemoryPackOrder(2)] + public string? FriendlyName { get; set; } + + [MemoryPackOrder(3)] + public string? Manufacturer { get; set; } + + [MemoryPackOrder(4)] + public string? Model { get; set; } + + [MemoryPackOrder(5)] + public ushort? MediaType { get; set; } + + [MemoryPackOrder(6)] + public ushort? BusType { get; set; } + + [MemoryPackOrder(7)] + public List? States { get; set; } + + [MemoryPackOrder(8)] + public HealthState? Health { get; set; } + + [MemoryPackOrder(9)] + public List? SupportedUsages { get; set; } + + [MemoryPackOrder(10)] + public ushort? Usage { get; set; } + + [MemoryPackOrder(11)] + public string? PhysicalLocation { get; set; } + + [MemoryPackOrder(12)] + public string? SerialNumber { get; set; } + + [MemoryPackOrder(13)] + public string? FirmwareVersion { get; set; } + + [MemoryPackOrder(14)] + public ulong? Size { get; set; } + + [MemoryPackOrder(15)] + public ulong? AllocatedSize { get; set; } + + [MemoryPackOrder(16)] + public ulong? LogicalSectorSize { get; set; } + + [MemoryPackOrder(17)] + public ulong? PhysicalSectorSize { get; set; } + + [MemoryPackOrder(18)] + public ulong? VirtualDiskFootprint { get; set; } + + public enum OperationalState + { + Unknown = 0, + Other = 1, + OK = 2, + Degraded = 3, + Stressed = 4, + Predictive_Failure = 5, + Error = 6, + Non_Recoverable_Error = 7, + Starting = 8, + Stopping = 9, + Stopped = 10, + In_Service = 11, + No_Contact = 12, + Lost_Communication = 13, + Aborted = 14, + Dormant = 15, + Supporting_Entity_In_Error = 16, + Completed = 17, + Power_Mode = 18, + Relocating = 19 + } + + public enum HealthState + { + Healthy = 0, + Warning = 1, + Unhealthy = 2, + Unknown = 3 + } + + public enum SupportedUsagesEnum + { + Unknown = 0, + Auto_Select = 1, + Manual_Select = 2, + Hot_Spare = 3, + Retired = 4, + Journal = 5 + } +} + +[MemoryPackable] +public partial class VirtualDisk : IMessage +{ + [MemoryPackOrder(0)] + public string? UniqueId { get; set; } + + [MemoryPackOrder(1)] + public string? Name { get; set; } + + [MemoryPackOrder(2)] + public string? FriendlyName { get; set; } + + [MemoryPackOrder(3)] + public List? States { get; set; } + + [MemoryPackOrder(4)] + public HealthState? Health { get; set; } + + [MemoryPackOrder(5)] + public AccessTypeEnum? AccessType { get; set; } + + [MemoryPackOrder(6)] + public ProvisioningTypeEnum? ProvisioningType { get; set; } + + [MemoryPackOrder(7)] + public ushort? PhysicalDiskRedundancy { get; set; } + + [MemoryPackOrder(8)] + public string? ResiliencySettingName { get; set; } + + [MemoryPackOrder(9)] + public bool? Deduplication { get; set; } + + [MemoryPackOrder(10)] + public bool? IsSnapshot { get; set; } + + [MemoryPackOrder(11)] + public ulong? Size { get; set; } + + [MemoryPackOrder(12)] + public ulong? AllocatedSize { get; set; } + + [MemoryPackOrder(13)] + public ulong? FootprintOnPool { get; set; } + + [MemoryPackOrder(14)] + public ulong? ReadCacheSize { get; set; } + + [MemoryPackOrder(15)] + public ulong? WriteCacheSize { get; set; } + + public enum OperationalState + { + Unknown = 0, + Other = 1, + OK = 2, + Degraded = 3, + Stressed = 4, + Predictive_Failure = 5, + Error = 6, + Non_Recoverable_Error = 7, + Starting = 8, + Stopping = 9, + Stopped = 10, + In_Service = 11, + No_Contact = 12, + Lost_Communication = 13, + Aborted = 14, + Dormant = 15, + Supporting_Entity_In_Error = 16, + Completed = 17, + Power_Mode = 18, + Relocating = 19 + } + + public enum HealthState + { + Healthy = 0, + Warning = 1, + Unhealthy = 2, + Unknown = 3 + } + + public enum AccessTypeEnum + { + Unknown = 0, + Readable = 1, + Writeable = 2, + Read_Write = 3, + Write_Once = 4 + } + + public enum ProvisioningTypeEnum + { + Unknown = 0, + Thin = 1, + Fixed = 2 + } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/SystemInfo.cs b/src/Core/Insight.Domain/Network/Agent/Messages/SystemInfo.cs new file mode 100644 index 0000000..187abfd --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/SystemInfo.cs @@ -0,0 +1,19 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class SystemInfo : IMessage +{ + [MemoryPackOrder(0)] + public DateTime? LastBootUpTime { get; set; } + + [MemoryPackOrder(1)] + public DateTime? LocalDateTime { get; set; } + + [MemoryPackOrder(2)] + public uint? Processes { get; set; } + + [MemoryPackOrder(3)] + public string? License { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Trap.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Trap.cs new file mode 100644 index 0000000..e2199ae --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Trap.cs @@ -0,0 +1,25 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Trap : IMessage +{ + [MemoryPackOrder(0)] + public DateTime? Timestamp { get; set; } + + [MemoryPackOrder(1)] + public string? Endpoint { get; set; } + + [MemoryPackOrder(2)] + public string? Hostname { get; set; } + + [MemoryPackOrder(3)] + public string? Version { get; set; } + + [MemoryPackOrder(4)] + public string? Community { get; set; } + + [MemoryPackOrder(5)] + public List>? Data { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Update.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Update.cs new file mode 100644 index 0000000..5a039cb --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Update.cs @@ -0,0 +1,78 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Update : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public DateTime? Date { get; set; } + + [MemoryPackOrder(2)] + public string? Name { get; set; } + + [MemoryPackOrder(3)] + public string? Description { get; set; } + + [MemoryPackOrder(4)] + public string? SupportUrl { get; set; } + + [MemoryPackOrder(5)] + public string? Hotfix { get; set; } + + // if installed + [MemoryPackOrder(6)] + public OsUpdateResultCodeEnum? Result { get; set; } + + // if pending + [MemoryPackOrder(7)] + public OsUpdateTypeEnum? Type { get; set; } + + [MemoryPackOrder(8)] + public decimal? Size { get; set; } + + [MemoryPackOrder(9)] + public bool? IsDownloaded { get; set; } + + [MemoryPackOrder(10)] + public bool? CanRequestUserInput { get; set; } + + [MemoryPackOrder(11)] + public OsUpdateRebootBehaviorEnum? RebootBehavior { get; set; } + + public enum OsUpdateRebootBehaviorEnum + { + NeverReboots = 1, + AlwaysRequiresReboot = 2, + CanRequestReboot = 3 + } + + public enum OsUpdateResultCodeEnum + { + NotStarted = 1, + InProgress = 2, + Succeeded = 3, + SucceededWithErrors = 4, + Failed = 5, + Aborted = 6 + } + + public enum OsUpdateTypeEnum + { + Software = 1, + Driver = 2 + } +} + +[MemoryPackable] +public partial class UpdateCollection : IMessage +{ + [MemoryPackOrder(0)] + public List? Installed { get; set; } + + [MemoryPackOrder(1)] + public List? Pending { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/User.cs b/src/Core/Insight.Domain/Network/Agent/Messages/User.cs new file mode 100644 index 0000000..5417641 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/User.cs @@ -0,0 +1,65 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class User : IMessage +{ + [MemoryPackOrder(0)] + public string? Sid { get; set; } + + [MemoryPackOrder(1)] + public string? Domain { get; set; } + + [MemoryPackOrder(2)] + public string? Name { get; set; } + + [MemoryPackOrder(3)] + public string? FullName { get; set; } + + [MemoryPackOrder(4)] + public string? Description { get; set; } + + [MemoryPackOrder(5)] + public string? Status { get; set; } + + [MemoryPackOrder(6)] + public bool? LocalAccount { get; set; } + + [MemoryPackOrder(7)] + public bool? Disabled { get; set; } + + [MemoryPackOrder(8)] + public bool? Lockout { get; set; } + + [MemoryPackOrder(9)] + public bool? PasswordChangeable { get; set; } + + [MemoryPackOrder(10)] + public bool? PasswordExpires { get; set; } + + [MemoryPackOrder(11)] + public bool? PasswordRequired { get; set; } + + [MemoryPackOrder(12)] + public List? Groups { get; set; } +} + +[MemoryPackable] +public partial class Group : IMessage +{ + [MemoryPackOrder(0)] + public string? Sid { get; set; } + + [MemoryPackOrder(1)] + public string? Domain { get; set; } + + [MemoryPackOrder(2)] + public string? Name { get; set; } + + [MemoryPackOrder(3)] + public string? Description { get; set; } + + [MemoryPackOrder(4)] + public bool? LocalAccount { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/Videocard.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Videocard.cs new file mode 100644 index 0000000..f651294 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Videocard.cs @@ -0,0 +1,22 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class Videocard : IMessage +{ + [MemoryPackOrder(0)] + public string? DeviceId { get; set; } + + [MemoryPackOrder(1)] + public string? Model { get; set; } + + [MemoryPackOrder(2)] + public ulong Memory { get; set; } + + [MemoryPackOrder(3)] + public DateTime DriverDate { get; set; } + + [MemoryPackOrder(4)] + public string? DriverVersion { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Agent/Messages/VirtualMaschine.cs b/src/Core/Insight.Domain/Network/Agent/Messages/VirtualMaschine.cs new file mode 100644 index 0000000..7e2eef5 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Agent/Messages/VirtualMaschine.cs @@ -0,0 +1,257 @@ +using MemoryPack; + +namespace Insight.Domain.Network.Agent.Messages; + +[MemoryPackable] +public partial class VirtualMaschine : IMessage +{ + [MemoryPackOrder(0)] + public Guid? Id { get; set; } + + [MemoryPackOrder(1)] + public uint? ProcessId { get; set; } + + [MemoryPackOrder(2)] + public string? Caption { get; set; } + + [MemoryPackOrder(3)] + public string? Name { get; set; } + + [MemoryPackOrder(4)] + public string? Notes { get; set; } + + [MemoryPackOrder(5)] + public EnabledEnum? Enabled { get; set; } + + [MemoryPackOrder(6)] + public EnabledDefaultEnum? EnabledDefault { get; set; } + + [MemoryPackOrder(7)] + public HealthStatusEnum? HealthState { get; set; } + + [MemoryPackOrder(8)] + public string? Status { get; set; } + + [MemoryPackOrder(9)] + public ulong? OnTime { get; set; } + + [MemoryPackOrder(10)] + public uint? ReplicationMode { get; set; } + + [MemoryPackOrder(11)] + public ReplicationStateEnum? ReplicationState { get; set; } + + [MemoryPackOrder(12)] + public ReplicationHealthEnum? ReplicationHealth { get; set; } + + [MemoryPackOrder(13)] + public string? ConfigurationVersion { get; set; } + + [MemoryPackOrder(14)] + public IntegrationServicesVersionStateEnum? IntegrationServicesVersionState { get; set; } + + [MemoryPackOrder(15)] + public uint? NumberOfProcessors { get; set; } + + [MemoryPackOrder(16)] + public uint? ProcessorLoad { get; set; } + + [MemoryPackOrder(17)] + public int? MemoryAvailable { get; set; } + + [MemoryPackOrder(18)] + public ulong? MemoryUsage { get; set; } + + [MemoryPackOrder(19)] + public DateTime? InstallDate { get; set; } + + [MemoryPackOrder(20)] + public DateTime? TimeOfLastConfigurationChange { get; set; } + + [MemoryPackOrder(21)] + public DateTime? TimeOfLastStateChange { get; set; } + + [MemoryPackOrder(22)] + public DateTime? LastReplicationTime { get; set; } + + [MemoryPackOrder(23)] + public string? GuestOperatingSystem { get; set; } + + [MemoryPackOrder(24)] + public List? Configurations { get; set; } + + public enum EnabledEnum + { + Unbekannt = 0, + Andere = 1, + Aktiviert = 2, + Deaktiviert = 3, + Herunterfahren = 4, + Nicht_Verfügbar = 5, + Aktiviert_Offline = 6, + In_Test = 7, + Latent = 8, + Eingeschränkt = 9, + Wird_gestartet = 10 + } + + public enum EnabledDefaultEnum + { + Aktiviert = 2, + Deaktiviert = 3, + Aktiviert_Offline = 6 + } + + public enum HealthStatusEnum + { + OK = 5, + Hauptfehler = 20, + Kritischer_Fehler = 25 + } + + public enum ReplicationStateEnum + { + Deaktiviert = 0, + Bereit = 1, + Warten_auf_Erstreplikation = 2, + Replikat = 3, + Synchronisierte_Replication_abgeschlossen = 4, + Wiederhergestellt = 5, + Commit = 6, + Angehalten = 7, + Kritisch = 8, + Warten_auf_die_Neusynchronisierung = 9, + Resynchronisierung = 10, + Resynchronisierung_angehalten = 11, + Failover_in_Bearbeitung = 12, + Failback_in_Fortschritt = 13, + Failback_abgeschlossen = 14, + Datenträgerupdate_in_Bearbeitung = 15, + Datenträgeraktualisierung_kritisch = 16, + Unbekannt = 17, + Repurpose_Replikation_in_Bearbeitung = 18, + Vorbereitet_für_die_Synchronisierungsreplikation = 19, + Vorbereitet_für_die_Umgekehrte_Replikation_der_Gruppe = 20, + Failover_in_Fortschritt = 21 + } + + public enum ReplicationHealthEnum + { + OK = 1, + Warnung = 2, + Kritisch = 3 + } + + public enum IntegrationServicesVersionStateEnum + { + Unknown = 0, + UpToDate = 1, + Mismatch = 2 + } +} + +[MemoryPackable] +public partial class VirtualMaschineConfiguration : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public string? ParentId { get; set; } + + [MemoryPackOrder(2)] + public string? Type { get; set; } + + [MemoryPackOrder(3)] + public string? Name { get; set; } + + [MemoryPackOrder(4)] + public DateTime? CreationTime { get; set; } + + [MemoryPackOrder(5)] + public string? Generation { get; set; } + + [MemoryPackOrder(6)] + public string? Architecture { get; set; } + + [MemoryPackOrder(7)] + public AutomaticStartupActionEnum? AutomaticStartupAction { get; set; } + //public DateTime? AutomaticStartupActionDelay { get; set; } + + [MemoryPackOrder(8)] + public AutomaticShutdownActionEnum? AutomaticShutdownAction { get; set; } + + [MemoryPackOrder(9)] + public AutomaticRecoveryActionEnum? AutomaticRecoveryAction { get; set; } + + [MemoryPackOrder(10)] + public bool? AutomaticSnapshotsEnabled { get; set; } + + [MemoryPackOrder(11)] + public string? BaseBoardSerialNumber { get; set; } + + [MemoryPackOrder(12)] + public string? BIOSGUID { get; set; } + + [MemoryPackOrder(13)] + public string? BIOSSerialNumber { get; set; } + + [MemoryPackOrder(14)] + public ushort[]? BootOrder { get; set; } + + [MemoryPackOrder(15)] + public string? ConfigurationDataRoot { get; set; } + + [MemoryPackOrder(16)] + public string? ConfigurationFile { get; set; } + + [MemoryPackOrder(17)] + public string? GuestStateDataRoot { get; set; } + + [MemoryPackOrder(18)] + public string? GuestStateFile { get; set; } + + [MemoryPackOrder(19)] + public string? SnapshotDataRoot { get; set; } + + [MemoryPackOrder(20)] + public string? SuspendDataRoot { get; set; } + + [MemoryPackOrder(21)] + public string? SwapFileDataRoot { get; set; } + + [MemoryPackOrder(22)] + public bool? SecureBootEnabled { get; set; } + + [MemoryPackOrder(23)] + public bool? IsAutomaticSnapshot { get; set; } + + [MemoryPackOrder(24)] + public string[]? Notes { get; set; } + + [MemoryPackOrder(25)] + public List? Childs { get; set; } + + //public string[]? HostResource { get; set; } + + public enum AutomaticStartupActionEnum + { + Nothing = 2, + RestartIfLastStateActive = 3, + Alway = 4 + } + + public enum AutomaticShutdownActionEnum + { + Ausschalten = 2, + Speichern = 3, + Herunterfahren = 4 + } + + public enum AutomaticRecoveryActionEnum + { + Keine = 2, + Neustart = 3, + Rollback = 4 + } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Collection.cs b/src/Core/Insight.Domain/Network/Collection.cs new file mode 100644 index 0000000..7466c26 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Collection.cs @@ -0,0 +1,7 @@ +using MemoryPack; + +namespace Insight.Domain.Network; + +[MemoryPackable(GenerateType.Collection)] +public partial class Collection : List, IMessage where TMessage : IMessage +{ } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/IMessage.cs b/src/Core/Insight.Domain/Network/IMessage.cs new file mode 100644 index 0000000..39da489 --- /dev/null +++ b/src/Core/Insight.Domain/Network/IMessage.cs @@ -0,0 +1,54 @@ +using MemoryPack; + +namespace Insight.Domain.Network; + +// AGENT [0 - 99] +[MemoryPackUnion(0, typeof(Agent.Messages.AuthenticationRequest))] +[MemoryPackUnion(1, typeof(Agent.Messages.AuthenticationResponse))] +[MemoryPackUnion(2, typeof(Agent.Messages.InventoryRequest))] +//[MemoryPackUnion(3, typeof(Agent.Messages.ConsoleQueryRequest))] +//[MemoryPackUnion(4, typeof(Agent.Messages.ConsoleQueryResponse))] +//[MemoryPackUnion(5, typeof(Proxy))] +//[MemoryPackUnion(6, typeof(Proxy))] +[MemoryPackUnion(7, typeof(Agent.Messages.Event))] +[MemoryPackUnion(8, typeof(Agent.Messages.Trap))] +[MemoryPackUnion(9, typeof(Agent.Messages.Mainboard))] +[MemoryPackUnion(10, typeof(Agent.Messages.OperationSystem))] +[MemoryPackUnion(11, typeof(Agent.Messages.Status))] +[MemoryPackUnion(12, typeof(Agent.Messages.SystemInfo))] +[MemoryPackUnion(13, typeof(Collection))] +[MemoryPackUnion(14, typeof(Collection))] +[MemoryPackUnion(15, typeof(Collection))] +[MemoryPackUnion(16, typeof(Collection))] +[MemoryPackUnion(17, typeof(Collection))] +[MemoryPackUnion(18, typeof(Collection))] +[MemoryPackUnion(19, typeof(Collection))] +[MemoryPackUnion(20, typeof(Collection))] +[MemoryPackUnion(21, typeof(Collection))] +[MemoryPackUnion(22, typeof(Agent.Messages.UpdateCollection))] +[MemoryPackUnion(23, typeof(Collection))] +[MemoryPackUnion(24, typeof(Collection))] +[MemoryPackUnion(25, typeof(Collection))] + +[MemoryPackUnion(50, typeof(Agent.Messages.Request))] +[MemoryPackUnion(51, typeof(Agent.Messages.Response))] +[MemoryPackUnion(52, typeof(Proxy))] +[MemoryPackUnion(53, typeof(Proxy))] + +// REMOTE [100 - 199] +[MemoryPackUnion(102, typeof(Remote.Messages.RemoteSessionRequest))] +[MemoryPackUnion(103, typeof(Remote.Messages.RemoteSessionResponse))] +[MemoryPackUnion(104, typeof(Remote.Messages.CastRequest))] +[MemoryPackUnion(105, typeof(Remote.Messages.CastRequestResponse))] +[MemoryPackUnion(107, typeof(Remote.Messages.CastAbort))] +[MemoryPackUnion(108, typeof(Remote.Messages.CastMetric))] +[MemoryPackUnion(109, typeof(Remote.Messages.CastDisplay))] +[MemoryPackUnion(110, typeof(Remote.Messages.CastScreen))] +[MemoryPackUnion(111, typeof(Remote.Messages.CastScreenReceived))] +[MemoryPackUnion(112, typeof(Remote.Messages.CastCursor))] +[MemoryPackUnion(113, typeof(Remote.Messages.CastCursorReceived))] +[MemoryPackUnion(114, typeof(Remote.Messages.CastClipboardReceived))] +[MemoryPackUnion(115, typeof(Remote.Messages.CastAudio))] + +[MemoryPackable] +public partial interface IMessage { } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Proxy.cs b/src/Core/Insight.Domain/Network/Proxy.cs new file mode 100644 index 0000000..a16fb78 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Proxy.cs @@ -0,0 +1,14 @@ +using MemoryPack; + +namespace Insight.Domain.Network; + +[MemoryPackable] +public partial class Proxy : IMessage + where TMessage : IMessage +{ + [MemoryPackOrder(0)] + public string? ProxyId { get; set; } + + [MemoryPackOrder(1)] + public TMessage? Message { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Remote/Messages/Session.cs b/src/Core/Insight.Domain/Network/Remote/Messages/Session.cs new file mode 100644 index 0000000..1a1ecca --- /dev/null +++ b/src/Core/Insight.Domain/Network/Remote/Messages/Session.cs @@ -0,0 +1,24 @@ +using Insight.Domain.Enums; +using MemoryPack; + +namespace Insight.Domain.Network.Remote.Messages; + +[MemoryPackable] +public partial class RemoteSessionRequest : IMessage +{ + [MemoryPackOrder(0)] + public RemoteControlMode Mode { get; set; } + + [MemoryPackOrder(1)] + public string? AccessKey { get; set; } + + [MemoryPackOrder(2)] + public string? Hostname { get; set; } +} + +[MemoryPackable] +public partial class RemoteSessionResponse : IMessage +{ + [MemoryPackOrder(0)] + public string? SessionId { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Network/Remote/Messages/Stream.cs b/src/Core/Insight.Domain/Network/Remote/Messages/Stream.cs new file mode 100644 index 0000000..e1a9ba0 --- /dev/null +++ b/src/Core/Insight.Domain/Network/Remote/Messages/Stream.cs @@ -0,0 +1,185 @@ +using Insight.Domain.Enums; +using MemoryPack; + +namespace Insight.Domain.Network.Remote.Messages; + +[MemoryPackable] +public partial class CastRequest : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public RemoteControlMode Mode { get; set; } + + [MemoryPackOrder(2)] + public string? RequesterName { get; set; } + + [MemoryPackOrder(3)] + public string? AccessKey { get; set; } +} + +[MemoryPackable] +public partial class CastRequestResponse : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(0)] + public bool Accepted { get; set; } + + [MemoryPackConstructor] + public CastRequestResponse() { } + + public CastRequestResponse(CastRequest request) + { + Id = request.Id; + } +} + +[MemoryPackable] +public partial class CastAbort : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } +} + +[MemoryPackable] +public partial class CastMetric : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public DateTimeOffset? Timestamp { get; set; } + + [MemoryPackOrder(2)] + public double? Mbps { get; set; } + + [MemoryPackOrder(3)] + public double? Fps { get; set; } + + [MemoryPackOrder(4)] + public double? RTT { get; set; } +} + +[MemoryPackable] +public partial class CastDisplay : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public IEnumerable? DisplayNames { get; set; } + + [MemoryPackOrder(2)] + public string? SelectedDisplay { get; set; } + + [MemoryPackOrder(3)] + public int ScreenWidth { get; set; } + + [MemoryPackOrder(4)] + public int ScreenHeight { get; set; } + + [MemoryPackOrder(5)] + public string? MachineName { get; set; } +} + +[MemoryPackable] +public partial class CastScreen : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public DateTimeOffset? Timestamp { get; set; } + + [MemoryPackOrder(2)] + public float? ViewWidth { get; set; } + + [MemoryPackOrder(3)] + public float? ViewHeight { get; set; } + + [MemoryPackOrder(4)] + public float? Left { get; set; } + + [MemoryPackOrder(5)] + public float? Top { get; set; } + + [MemoryPackOrder(6)] + public float? Width { get; set; } + + [MemoryPackOrder(7)] + public float? Height { get; set; } + + [MemoryPackOrder(8)] + public byte[]? Image { get; set; } +} + +[MemoryPackable] +public partial class CastScreenReceived : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public DateTimeOffset? Timestamp { get; set; } + + [MemoryPackConstructor] + public CastScreenReceived() { } + + public CastScreenReceived(CastScreen screenData) + { + Id = screenData.Id; + Timestamp = screenData.Timestamp; + } +} + +[MemoryPackable] +public partial class CastCursor : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public int X { get; set; } + + [MemoryPackOrder(2)] + public int Y { get; set; } + + [MemoryPackOrder(3)] + public byte[]? Icon { get; set; } +} + +[MemoryPackable] +public partial class CastCursorReceived : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public int X { get; set; } + + [MemoryPackOrder(2)] + public int Y { get; set; } +} + +[MemoryPackable] +public partial class CastClipboardReceived : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public string? Text { get; set; } +} + +[MemoryPackable] +public partial class CastAudio : IMessage +{ + [MemoryPackOrder(0)] + public string? Id { get; set; } + + [MemoryPackOrder(1)] + public byte[]? Buffer { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Constants/Appsettings.cs b/src/Core/Insight.Infrastructure/Constants/Appsettings.cs index d85fd32..e1adf44 100644 --- a/src/Core/Insight.Infrastructure/Constants/Appsettings.cs +++ b/src/Core/Insight.Infrastructure/Constants/Appsettings.cs @@ -1,14 +1,17 @@ -namespace Insight.Infrastructure -{ - public class Appsettings - { - public const string Database = "database"; - public const string JwtKey = "jwt.key"; - public const string JwtAudience = "jwt.audience"; - public const string JwtIssuer = "jwt.issuer"; - public const string JwtExp = "jwt.exp"; +namespace Insight.Infrastructure; - public const string ServerHost = "server.host"; - public const string ServerPort = "server.port"; - } +public class Appsettings +{ + public const string Database = "database"; + public const string JwtKey = "jwt.key"; + public const string JwtAudience = "jwt.audience"; + public const string JwtIssuer = "jwt.issuer"; + public const string JwtExp = "jwt.exp"; + + public const string ServerHost = "server.host"; + public const string ServerPort = "server.port"; + + public const string RemoteServerPort = "remote.port"; + public const string RemoteServerCertificate = "remote.certificate"; + public const string RemoteServerCertificatePassword = "remote.certificate.password"; } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Constants/Monitoring.cs b/src/Core/Insight.Infrastructure/Constants/Monitoring.cs index c8bcf72..f88284b 100644 --- a/src/Core/Insight.Infrastructure/Constants/Monitoring.cs +++ b/src/Core/Insight.Infrastructure/Constants/Monitoring.cs @@ -1,8 +1,7 @@ -namespace Insight.Infrastructure +namespace Insight.Infrastructure; + +public static class Monitoring { - public static class Monitoring - { - public static readonly Uri StatusUri = new("https://nexus.webmatic.de/rest/monitoring/send/status"); - public static readonly Uri LogUri = new("https://nexus.webmatic.de/rest/monitoring/send/log"); - } + public static readonly Uri StatusUri = new("https://admin.webmatic.de/monitoring/computer/send/status"); + public static readonly Uri LogUri = new("https://admin.webmatic.de/monitoring/computer/send/log"); } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Constants/Settings.cs b/src/Core/Insight.Infrastructure/Constants/Settings.cs index b667086..ec9bec6 100644 --- a/src/Core/Insight.Infrastructure/Constants/Settings.cs +++ b/src/Core/Insight.Infrastructure/Constants/Settings.cs @@ -1,7 +1,6 @@ -namespace Insight.Infrastructure +namespace Insight.Infrastructure; + +public class Settings { - public class Settings - { - public const string Database = "insight"; - } + public const string Database = "insight"; } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Agent.cs b/src/Core/Insight.Infrastructure/Entities/Agent.cs deleted file mode 100644 index d538498..0000000 --- a/src/Core/Insight.Infrastructure/Entities/Agent.cs +++ /dev/null @@ -1,62 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class AgentEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("serial"), Required] - public string? Serial { get; set; } - - [BsonElement("hostname")] - public string? Hostname { get; set; } - - [BsonElement("version"), BsonRepresentation(BsonType.String)] - public Version? Version { get; set; } - - [BsonElement("endpoint"), BsonRepresentation(BsonType.String)] - public string? Endpoint { get; set; } - - [BsonElement("connected")] - public DateTime? Connected { get; set; } - - [BsonElement("activity")] - public DateTime? Activity { get; set; } - - [BsonElement("bytes_sent")] - public long SentBytes { get; set; } - - [BsonElement("bytes_received")] - public long ReceivedBytes { get; set; } - - [BsonElement("packets_sent")] - public long SentPackets { get; set; } - - [BsonElement("packets_received")] - public long ReceivedPackets { get; set; } - - //[BsonElement("latency")] - //public TimeSpan? Latency { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Hosts { get; set; } - - public bool GetOnlineState() - { - if (Activity is null) return false; - return Activity.Value.ToLocalTime().Add(TimeSpan.FromSeconds(60)).Subtract(DateTime.Now).Seconds > 0; - } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Agent/Agent.cs b/src/Core/Insight.Infrastructure/Entities/Agent/Agent.cs new file mode 100644 index 0000000..e673e8e --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Agent/Agent.cs @@ -0,0 +1,67 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection Agent(this IMongoDatabase database) => database.GetCollection("agent"); +} + +[BsonIgnoreExtraElements] +public class AgentEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("serial"), Required] + public string? Serial { get; set; } + + [BsonElement("hostname")] + public string? Hostname { get; set; } + + [BsonElement("version"), BsonRepresentation(BsonType.String)] + public Version? Version { get; set; } + + [BsonElement("endpoint"), BsonRepresentation(BsonType.String)] + public string? Endpoint { get; set; } + + [BsonElement("connected")] + public DateTime? Connected { get; set; } + + [BsonElement("activity")] + public DateTime? Activity { get; set; } + + [BsonElement("bytes_sent")] + public long SentBytes { get; set; } + + [BsonElement("bytes_received")] + public long ReceivedBytes { get; set; } + + [BsonElement("packets_sent")] + public long SentPackets { get; set; } + + [BsonElement("packets_received")] + public long ReceivedPackets { get; set; } + + //[BsonElement("latency")] + //public TimeSpan? Latency { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Hosts { get; set; } + + public bool GetOnlineState() + { + if (Activity is null) return false; + return Activity.Value.ToLocalTime().Add(TimeSpan.FromSeconds(60)).Subtract(DateTime.Now).Seconds > 0; + } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Agent/AgentLog.cs b/src/Core/Insight.Infrastructure/Entities/Agent/AgentLog.cs new file mode 100644 index 0000000..b8fc06e --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Agent/AgentLog.cs @@ -0,0 +1,42 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection AgentLog(this IMongoDatabase database) => database.GetCollection("agent_log"); +} + +[BsonIgnoreExtraElements] +public class AgentLogEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_agent"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("agent")] + public string? Agent { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("eventid")] + public string? EventId { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("source")] + public string? Source { get; set; } + + [BsonElement("category")] + public string? Category { get; set; } + + [BsonElement("message")] + public string? Message { get; set; } + + [BsonElement("timestamp")] + public DateTime? Timestamp { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/AgentLog.cs b/src/Core/Insight.Infrastructure/Entities/AgentLog.cs deleted file mode 100644 index 8c32884..0000000 --- a/src/Core/Insight.Infrastructure/Entities/AgentLog.cs +++ /dev/null @@ -1,37 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class AgentLogEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_agent"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("agent")] - public string? Agent { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("eventid")] - public string? EventId { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("source")] - public string? Source { get; set; } - - [BsonElement("category")] - public string? Category { get; set; } - - [BsonElement("message")] - public string? Message { get; set; } - - [BsonElement("timestamp")] - public DateTime? Timestamp { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Customer.cs b/src/Core/Insight.Infrastructure/Entities/Customer.cs deleted file mode 100644 index 983715e..0000000 --- a/src/Core/Insight.Infrastructure/Entities/Customer.cs +++ /dev/null @@ -1,29 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class CustomerEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("name"), Required] - public string? Name { get; set; } - - [BsonElement("tag")] - public string? Tag { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Hosts { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Customer/Customer.cs b/src/Core/Insight.Infrastructure/Entities/Customer/Customer.cs new file mode 100644 index 0000000..8a1b737 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Customer/Customer.cs @@ -0,0 +1,34 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection Customer(this IMongoDatabase database) => database.GetCollection("customer"); +} + +[BsonIgnoreExtraElements] +public class CustomerEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name"), Required] + public string? Name { get; set; } + + [BsonElement("tag")] + public string? Tag { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Hosts { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host.cs b/src/Core/Insight.Infrastructure/Entities/Host.cs deleted file mode 100644 index 9297e78..0000000 --- a/src/Core/Insight.Infrastructure/Entities/Host.cs +++ /dev/null @@ -1,38 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_customer"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("customer")] - public string? Customer { get; set; } - - [BsonElement("_agent"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("agent")] - public string? Agent { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("name"), Required] - public string? Name { get; set; } - - [BsonElement("description")] - public string? Description { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Customers { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Agents { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/Host.cs b/src/Core/Insight.Infrastructure/Entities/Host/Host.cs new file mode 100644 index 0000000..2e065c8 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/Host.cs @@ -0,0 +1,43 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection Host(this IMongoDatabase database) => database.GetCollection("host"); +} + +[BsonIgnoreExtraElements] +public class HostEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_customer"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("customer")] + public string? Customer { get; set; } + + [BsonElement("_agent"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("agent")] + public string? Agent { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name"), Required] + public string? Name { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Customers { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Agents { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostApplication.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostApplication.cs new file mode 100644 index 0000000..388b522 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostApplication.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostApplication(this IMongoDatabase database) => database.GetCollection("host_app"); +} + +[BsonIgnoreExtraElements] +public class HostApplicationEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("architecture")] + public string? Architecture { get; set; } + + [BsonElement("version")] + public string? Version { get; set; } + + [BsonElement("installdate")] + public DateTime? InstallDate { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostDrive.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostDrive.cs new file mode 100644 index 0000000..72d46e7 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostDrive.cs @@ -0,0 +1,57 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostDrive(this IMongoDatabase database) => database.GetCollection("host_drv"); +} + +[BsonIgnoreExtraElements] +public class HostDriveEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("index")] + public uint? Index { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("size")] + public ulong? Size { get; set; } + + [BsonElement("type")] + public string? Type { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } + + [BsonElement("firmware")] + public string? Firmware { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("pnp")] + public string? Pnp { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostHypervisor.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostHypervisor.cs new file mode 100644 index 0000000..0bfdcb5 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostHypervisor.cs @@ -0,0 +1,194 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostHypervisorVirtualMaschine(this IMongoDatabase database) => database.GetCollection("host_hv_vm"); + public static IMongoCollection HostVirtualMaschineConfig(this IMongoDatabase database) => database.GetCollection("host_hv_vm_cfg"); +} + +[BsonIgnoreExtraElements] +public class HostHypervisorVirtualMaschineEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("uniqueid")] + public string? UniqueId { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("notes")] + public string? Notes { get; set; } + + [BsonElement("enabled")] + public string? Enabled { get; set; } + + [BsonElement("enabled_default")] + public string? EnabledDefault { get; set; } + + [BsonElement("health")] + public string? Health { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("ontime")] + public ulong? OnTime { get; set; } + + [BsonElement("replication_state")] + public string? ReplicationState { get; set; } + + [BsonElement("replication_health")] + public string? ReplicationHealth { get; set; } + + [BsonElement("version_configuration")] + public string? ConfigurationVersion { get; set; } + + [BsonElement("version_integrated_services")] + public string? IntegrationServicesVersionState { get; set; } + + [BsonElement("processid")] + public uint? ProcessId { get; set; } + + [BsonElement("processor_count")] + public uint? NumberOfProcessors { get; set; } + + [BsonElement("processor_load")] + public uint? ProcessorLoad { get; set; } + + [BsonElement("memory_available")] + public int? MemoryAvailable { get; set; } + + [BsonElement("memory_usage")] + public ulong? MemoryUsage { get; set; } + + [BsonElement("installdate")] + public DateTime? InstallDate { get; set; } + + [BsonElement("configuration_changed")] + public DateTime? TimeOfLastConfigurationChange { get; set; } + + [BsonElement("state_changed")] + public DateTime? TimeOfLastStateChange { get; set; } + + [BsonElement("replication_last")] + public DateTime? LastReplicationTime { get; set; } + + [BsonElement("guest_os")] + public string? Os { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Configs { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostHypervisorVirtualMaschineConfigEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("virtualmaschine"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("virtualmaschine")] + public string? VirtualMaschine { get; set; } + + [BsonElement("batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("uniqueid")] + public string? UniqueId { get; set; } + + [BsonElement("parentid")] + public string? ParentId { get; set; } + + [BsonElement("type")] + public string? Type { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("notes")] + public string? Notes { get; set; } + + [BsonElement("creationtime")] + public DateTime? CreationTime { get; set; } + + [BsonElement("generation")] + public string? Generation { get; set; } + + [BsonElement("architecture")] + public string? Architecture { get; set; } + + [BsonElement("secureboot")] + public bool? SecureBootEnabled { get; set; } + + [BsonElement("automatic_snapshot")] + public bool? IsAutomaticSnapshot { get; set; } + + [BsonElement("action_start")] + public string? AutomaticStartupAction { get; set; } + + [BsonElement("action_shutdown")] + public string? AutomaticShutdownAction { get; set; } + + [BsonElement("action_recovery")] + public string? AutomaticRecoveryAction { get; set; } + + [BsonElement("auto_snapshots")] + public bool? AutomaticSnapshotsEnabled { get; set; } + + [BsonElement("serial_mainboard")] + public string? BaseBoardSerialNumber { get; set; } + + [BsonElement("serial_bios")] + public string? BIOSSerialNumber { get; set; } + + [BsonElement("bios_guid")] + public string? BIOSGUID { get; set; } + + [BsonElement("data_root")] + public string? ConfigurationDataRoot { get; set; } + + [BsonElement("file")] + public string? ConfigurationFile { get; set; } + + [BsonElement("guest_data_root")] + public string? GuestStateDataRoot { get; set; } + + [BsonElement("guest_state_file")] + public string? GuestStateFile { get; set; } + + [BsonElement("snapshot_data_root")] + public string? SnapshotDataRoot { get; set; } + + [BsonElement("suspend_data_root")] + public string? SuspendDataRoot { get; set; } + + [BsonElement("swap_data_root")] + public string? SwapFileDataRoot { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostInterface.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostInterface.cs new file mode 100644 index 0000000..7756a47 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostInterface.cs @@ -0,0 +1,227 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostInterface(this IMongoDatabase database) => database.GetCollection("host_if"); + public static IMongoCollection HostInterfaceAddress(this IMongoDatabase database) => database.GetCollection("host_if_addr"); + public static IMongoCollection HostInterfaceGateway(this IMongoDatabase database) => database.GetCollection("host_if_gw"); + public static IMongoCollection HostInterfaceNameserver(this IMongoDatabase database) => database.GetCollection("host_if_ns"); + public static IMongoCollection HostInterfaceRoute(this IMongoDatabase database) => database.GetCollection("host_if_rt"); +} + +[BsonIgnoreExtraElements] +public class HostInterfaceEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("index")] + public uint? Index { get; set; } + + [BsonElement("mac")] + public string? Mac { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("physical")] + public bool? Physical { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("suffix")] + public string? Suffix { get; set; } + + [BsonElement("speed")] + public long? Speed { get; set; } + + [BsonElement("ipv4_mtu")] + public long? Ipv4Mtu { get; set; } + + [BsonElement("ipv4_dhcp")] + public bool? Ipv4Dhcp { get; set; } + + [BsonElement("ipv4_forwarding")] + public bool? Ipv4Forwarding { get; set; } + + [BsonElement("ipv6_mtu")] + public long? Ipv6Mtu { get; set; } + + [BsonElement("sent")] + public long? Sent { get; set; } + + [BsonElement("received")] + public long? Received { get; set; } + + [BsonElement("packets_incoming_discarded")] + public long? IncomingPacketsDiscarded { get; set; } + + [BsonElement("packets_incoming_errors")] + public long? IncomingPacketsWithErrors { get; set; } + + [BsonElement("packets_incoming_unknown")] + public long? IncomingUnknownProtocolPackets { get; set; } + + [BsonElement("packets_outgoing_discarded")] + public long? OutgoingPacketsDiscarded { get; set; } + + [BsonElement("packets_outgoing_errors")] + public long? OutgoingPacketsWithErrors { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostInterfaceAddressEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] + public string? Interface { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("address")] + public string? Address { get; set; } + + [BsonElement("mask")] + public string? Mask { get; set; } + + //public string? State { get; set; } + //public long? PreferredLifetime { get; set; } + //public long? ValidLifetime { get; set; } + //public long? LeaseLifetime { get; set; } + //public int? PrefixLength { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Interfaces { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostInterfaceGatewayEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] + public string? Interface { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("address")] + public string? Address { get; set; } + + [BsonElement("mask")] + public string? Mask { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Interfaces { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostInterfaceNameserverEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] + public string? Interface { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("address")] + public string? Address { get; set; } + + [BsonElement("mask")] + public string? Mask { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Interfaces { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostInterfaceRouteEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] + public string? Interface { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("destination")] + public string? Destination { get; set; } + + [BsonElement("mask")] + public string? Mask { get; set; } + + [BsonElement("gateway")] + public string? Gateway { get; set; } + + [BsonElement("metric")] + public int? Metric { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? Interfaces { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostLog.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostLog.cs new file mode 100644 index 0000000..ee34fa6 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostLog.cs @@ -0,0 +1,42 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostLog(this IMongoDatabase database) => database.GetCollection("host_log"); +} + +[BsonIgnoreExtraElements] +public class HostLogEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("eventid")] + public string? EventId { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("source")] + public string? Source { get; set; } + + [BsonElement("category")] + public string? Category { get; set; } + + [BsonElement("message")] + public string? Message { get; set; } + + [BsonElement("timestamp")] + public DateTime? Timestamp { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostLogMonitoring.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostLogMonitoring.cs new file mode 100644 index 0000000..6579981 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostLogMonitoring.cs @@ -0,0 +1,48 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostLogMonitoring(this IMongoDatabase database) => database.GetCollection("host_log_mon"); +} + +[BsonIgnoreExtraElements] +public class HostLogMonitoringEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("hostname")] + public string? Hostname { get; set; } + + [BsonElement("category")] + public string? Category { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("task")] + public string? Task { get; set; } + + [BsonElement("message")] + public string? Message { get; set; } + + [BsonElement("dispatch")] + public string? Dispatch { get; set; } + + [BsonElement("timestamp")] + public DateTime? Timestamp { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostMainboard.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostMainboard.cs new file mode 100644 index 0000000..5339e78 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostMainboard.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostMainboard(this IMongoDatabase database) => database.GetCollection("host_board"); +} + +[BsonIgnoreExtraElements] +public class HostMainboardEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } + + [BsonElement("bios")] + public string? Bios { get; set; } + + [BsonElement("version")] + public string? Version { get; set; } + + [BsonElement("Date")] + public DateTime? Date { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostMemory.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostMemory.cs new file mode 100644 index 0000000..0f0dc70 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostMemory.cs @@ -0,0 +1,63 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostMemory(this IMongoDatabase database) => database.GetCollection("host_mem"); +} + +[BsonIgnoreExtraElements] +public class HostMemoryEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("index")] + public uint? Index { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("tag")] + public string? Tag { get; set; } + + [BsonElement("location")] + public string? Location { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } + + [BsonElement("capacity")] + public ulong? Capacity { get; set; } + + [BsonElement("clock")] + public uint? Clock { get; set; } + + [BsonElement("clock_current")] + public uint? CurrentClock { get; set; } + + [BsonElement("voltage")] + public uint? Voltage { get; set; } + + [BsonElement("voltage_current")] + public uint? CurrentVoltage { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostOs.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostOs.cs new file mode 100644 index 0000000..06a65df --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostOs.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostOs(this IMongoDatabase database) => database.GetCollection("host_os"); +} + +[BsonIgnoreExtraElements] +public class HostOsEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("version")] + public string? Version { get; set; } + + [BsonElement("architecture")] + public string? Architecture { get; set; } + + [BsonElement("serialnumber")] + public string? SerialNumber { get; set; } + + [BsonElement("virtual")] + public bool? Virtual { get; set; } + + [BsonElement("installed")] + public DateTime? Installed { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostPrinter.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostPrinter.cs new file mode 100644 index 0000000..afa266f --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostPrinter.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostPrinter(this IMongoDatabase database) => database.GetCollection("host_prn"); +} + +[BsonIgnoreExtraElements] +public class HostPrinterEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("port")] + public string? Port { get; set; } + + [BsonElement("location")] + public string? Location { get; set; } + + [BsonElement("comment")] + public string? Comment { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostProcessor.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostProcessor.cs new file mode 100644 index 0000000..a31fba5 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostProcessor.cs @@ -0,0 +1,75 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostProcessor(this IMongoDatabase database) => database.GetCollection("host_cpu"); +} + +[BsonIgnoreExtraElements] +public class HostProcessorEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("index")] + public uint? Index { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("socket")] + public string? Socket { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } + + [BsonElement("version")] + public string? Version { get; set; } + + [BsonElement("cores")] + public uint? Cores { get; set; } + + [BsonElement("logicalcores")] + public uint? LogicalCores { get; set; } + + [BsonElement("clock")] + public uint? Clock { get; set; } + + [BsonElement("clock_current")] + public uint? CurrentClock { get; set; } + + [BsonElement("l1")] + public uint? L1Size { get; set; } + + [BsonElement("l2")] + public uint? L2Size { get; set; } + + [BsonElement("l3")] + public uint? L3Size { get; set; } + + [BsonElement("virtualization")] + public bool? Virtualization { get; set; } + + [BsonElement("pnp")] + public string? PNP { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostService.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostService.cs new file mode 100644 index 0000000..c55ab70 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostService.cs @@ -0,0 +1,60 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostService(this IMongoDatabase database) => database.GetCollection("host_svc"); +} + +[BsonIgnoreExtraElements] +public class HostServiceEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("displayname")] + public string? DisplayName { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("startmode")] + public string? StartMode { get; set; } + + [BsonElement("state")] + public string? State { get; set; } + + [BsonElement("processid")] + public uint? ProcessId { get; set; } + + [BsonElement("delay")] + public bool? Delay { get; set; } + + [BsonElement("path")] + public string? Path { get; set; } + + [BsonElement("account")] + public string? Account { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostSession.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSession.cs new file mode 100644 index 0000000..f2b15d6 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSession.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostSession(this IMongoDatabase database) => database.GetCollection("host_session"); +} + +[BsonIgnoreExtraElements] +public class HostSessionEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("sid")] + public string? Sid { get; set; } + + [BsonElement("user")] + public string? User { get; set; } + + [BsonElement("remote")] + public string? Remote { get; set; } + + [BsonElement("type")] + public string? Type { get; set; } + + [BsonElement("state")] + public string? State { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostStoragePool.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostStoragePool.cs new file mode 100644 index 0000000..4d8ce19 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostStoragePool.cs @@ -0,0 +1,214 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostStoragePool(this IMongoDatabase database) => database.GetCollection("host_sp"); + public static IMongoCollection HostStoragePoolPhysicalDisk(this IMongoDatabase database) => database.GetCollection("host_sp.pd"); + public static IMongoCollection HostStoragePoolVirtualDisk(this IMongoDatabase database) => database.GetCollection("host_sp.vd"); +} + +[BsonIgnoreExtraElements] +public class HostStoragePoolEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("uniqueid")] + public string? UniqueId { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("health")] + public string? Health { get; set; } + + [BsonElement("resiliency")] + public string? Resiliency { get; set; } + + [BsonElement("primordial")] + public bool? Primordial { get; set; } + + [BsonElement("readonly")] + public bool? ReadOnly { get; set; } + + [BsonElement("clustered")] + public bool? Clustered { get; set; } + + [BsonElement("size")] + public ulong? Size { get; set; } + + [BsonElement("size_allocated")] + public ulong? AllocatedSize { get; set; } + + [BsonElement("sectorsize")] + public ulong? SectorSize { get; set; } + + [BsonElement("states")] + public List? States { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? PhysicalDisks { get; set; } + + [BsonIgnoreIfNull, JsonIgnore] + public List? VirtualDisks { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostStoragePoolVirtualDiskEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_storagepool"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("storagepool")] + public string? StoragePool { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("uniqueid")] + public string? UniqueId { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("health")] + public string? Health { get; set; } + + [BsonElement("access")] + public string? Access { get; set; } + + [BsonElement("provisioning")] + public string? Provisioning { get; set; } + + [BsonElement("redundancy")] + public uint? PhysicalRedundancy { get; set; } + + [BsonElement("resiliency")] + public string? Resiliency { get; set; } + + [BsonElement("deduplication")] + public bool? Deduplication { get; set; } + + [BsonElement("snapshot")] + public bool? Snapshot { get; set; } + + [BsonElement("size")] + public ulong? Size { get; set; } + + [BsonElement("size_allocated")] + public ulong? AllocatedSize { get; set; } + + [BsonElement("footprint")] + public ulong? Footprint { get; set; } + + [BsonElement("cache_read_size")] + public ulong? ReadCacheSize { get; set; } + + [BsonElement("cache_write_size")] + public ulong? WriteCacheSize { get; set; } + + [BsonElement("states")] + public List? States { get; set; } +} + +[BsonIgnoreExtraElements] +public class HostStoragePoolPhysicalDiskEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_storagepool"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("storagepool")] + public string? StoragePool { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("uniqueid")] + public string? UniqueId { get; set; } + + [BsonElement("deviceid")] + public string? DeviceId { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("manufacturer")] + public string? Manufacturer { get; set; } + + [BsonElement("Model")] + public string? Model { get; set; } + + [BsonElement("media")] + public string? Media { get; set; } + + [BsonElement("bus")] + public string? Bus { get; set; } + + [BsonElement("health")] + public string? Health { get; set; } + + [BsonElement("usage")] + public ushort? Usage { get; set; } + + [BsonElement("location")] + public string? Location { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } + + [BsonElement("firmware")] + public string? Firmware { get; set; } + + [BsonElement("size")] + public ulong? Size { get; set; } + + [BsonElement("size_allocated")] + public ulong? AllocatedSize { get; set; } + + [BsonElement("footprint")] + public ulong? Footprint { get; set; } + + [BsonElement("sector_size_physical")] + public ulong? PhysicalSectorSize { get; set; } + + [BsonElement("sector_size_logical")] + public ulong? LogicalSectorSize { get; set; } + + [BsonElement("states")] + public List? States { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostSysGroup.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSysGroup.cs new file mode 100644 index 0000000..0cfe399 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSysGroup.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostSystemGroup(this IMongoDatabase database) => database.GetCollection("host_sysgrp"); +} + +[BsonIgnoreExtraElements] +public class HostSysGroupEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("sid")] + public string? Sid { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("domain")] + public string? Domain { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("localaccount")] + public bool? LocalAccount { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostSysUser.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSysUser.cs new file mode 100644 index 0000000..2814066 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSysUser.cs @@ -0,0 +1,66 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostSystemUser(this IMongoDatabase database) => database.GetCollection("host_sysusr"); +} + +[BsonIgnoreExtraElements] +public class HostSysUserEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("sid")] + public string? Sid { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("domain")] + public string? Domain { get; set; } + + [BsonElement("fullname")] + public string? FullName { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("status")] + public string? Status { get; set; } + + [BsonElement("localaccount")] + public bool? LocalAccount { get; set; } + + [BsonElement("disabled")] + public bool? Disabled { get; set; } + + [BsonElement("lockout")] + public bool? Lockout { get; set; } + + [BsonElement("password_changeable")] + public bool? PasswordChangeable { get; set; } + + [BsonElement("password_expires")] + public bool? PasswordExpires { get; set; } + + [BsonElement("password_required")] + public bool? PasswordRequired { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostSysUserSysGroup.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSysUserSysGroup.cs new file mode 100644 index 0000000..b5681f5 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSysUserSysGroup.cs @@ -0,0 +1,36 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostSystemUserSystemGroup(this IMongoDatabase database) => database.GetCollection("host_sysusr_sysgrp"); +} + +[BsonIgnoreExtraElements] +public class HostSysUserSysGroupEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_user"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("user")] + public string? User { get; set; } + + [BsonElement("_group"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("group")] + public string? Group { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostSystem.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSystem.cs new file mode 100644 index 0000000..610544f --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSystem.cs @@ -0,0 +1,39 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostSystem(this IMongoDatabase database) => database.GetCollection("host_sys"); +} + +[BsonIgnoreExtraElements] +public class HostSystemEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("localtime")] + public DateTime? LocalTime { get; set; } + + [BsonElement("bootuptime")] + public DateTime? BootUpTime { get; set; } + + [BsonElement("processes")] + public uint? Processes { get; set; } + + [BsonElement("license")] + public string? License { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostUpdate.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostUpdate.cs new file mode 100644 index 0000000..7f497ea --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostUpdate.cs @@ -0,0 +1,66 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostUpdate(this IMongoDatabase database) => database.GetCollection("host_upd"); +} + +[BsonIgnoreExtraElements] +public class HostUpdateEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } // os update id + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("supporturl")] + public string? SupportUrl { get; set; } + + [BsonElement("date")] + public DateTime? Date { get; set; } + + [BsonElement("pending")] + public bool? Pending { get; set; } + + [BsonElement("result"), BsonIgnoreIfNull] // installed only + public string? Result { get; set; } + + [BsonElement("type"), BsonIgnoreIfNull] // pending only + public string? Type { get; set; } + + [BsonElement("size"), BsonIgnoreIfNull] // pending only + public decimal? Size { get; set; } + + [BsonElement("downloaded"), BsonIgnoreIfNull] // pending only + public bool? IsDownloaded { get; set; } + + [BsonElement("inputrequest"), BsonIgnoreIfNull] // pending only + public bool? CanRequestUserInput { get; set; } + + [BsonElement("reboot"), BsonIgnoreIfNull] // pending only + public string? RebootBehavior { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostVideocard.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostVideocard.cs new file mode 100644 index 0000000..1ce11d8 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostVideocard.cs @@ -0,0 +1,45 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostVideocard(this IMongoDatabase database) => database.GetCollection("host_gpu"); +} + +[BsonIgnoreExtraElements] +public class HostVideocardEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("company")] + public string? Company { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("memory")] + public ulong? Memory { get; set; } + + [BsonElement("driver")] + public string? Driver { get; set; } + + [BsonElement("date")] + public DateTime? Date { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Host/HostVolume.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostVolume.cs new file mode 100644 index 0000000..901c644 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostVolume.cs @@ -0,0 +1,81 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostVolume(this IMongoDatabase database) => database.GetCollection("host_vol"); +} + +[BsonIgnoreExtraElements] +public class HostVolumeEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("_drive"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("drive")] + public string? Drive { get; set; } + + [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] + public string? Batch { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("index")] + public uint? Index { get; set; } + + [BsonElement("label")] + public string? Label { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("serial")] + public string? Serial { get; set; } + + [BsonElement("size")] + public ulong? Size { get; set; } + + [BsonElement("freespace")] + public ulong? FreeSpace { get; set; } + + [BsonElement("type")] + public string? Type { get; set; } + + [BsonElement("filesystem")] + public string? FileSystem { get; set; } + + [BsonElement("compressed")] + public bool? Compressed { get; set; } + + [BsonElement("bootable")] + public bool? Bootable { get; set; } + + [BsonElement("primary")] + public bool? Primary { get; set; } + + [BsonElement("boot")] + public bool? Boot { get; set; } + + [BsonElement("blocksize")] + public ulong? BlockSize { get; set; } + + [BsonElement("blocks")] + public ulong? Blocks { get; set; } + + [BsonElement("startoffset")] + public ulong? StartingOffset { get; set; } + + [BsonElement("provider")] + public string? Provider { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostApplication.cs b/src/Core/Insight.Infrastructure/Entities/HostApplication.cs deleted file mode 100644 index 473f383..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostApplication.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostApplicationEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("architecture")] - public string? Architecture { get; set; } - - [BsonElement("version")] - public string? Version { get; set; } - - [BsonElement("installdate")] - public DateTime? InstallDate { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostDrive.cs b/src/Core/Insight.Infrastructure/Entities/HostDrive.cs deleted file mode 100644 index f8cde99..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostDrive.cs +++ /dev/null @@ -1,52 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostDriveEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("index")] - public uint? Index { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("size")] - public ulong? Size { get; set; } - - [BsonElement("type")] - public string? Type { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } - - [BsonElement("firmware")] - public string? Firmware { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("pnp")] - public string? Pnp { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroup.cs b/src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroup.cs new file mode 100644 index 0000000..2cdaa8c --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroup.cs @@ -0,0 +1,31 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostGroup(this IMongoDatabase database) => database.GetCollection("hostgroup"); +} + +[BsonIgnoreExtraElements] +public class HostGroupEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("name"), Required] + public string? Name { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroupHost.cs b/src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroupHost.cs new file mode 100644 index 0000000..4428915 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroupHost.cs @@ -0,0 +1,30 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection HostGroupHost(this IMongoDatabase database) => database.GetCollection("hostgroup_host"); +} + +[BsonIgnoreExtraElements] +public class HostGroupHostEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_hostgroup"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("hostgroup")] + public string? HostGroup { get; set; } + + [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] + public string? Host { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostHypervisor.cs b/src/Core/Insight.Infrastructure/Entities/HostHypervisor.cs deleted file mode 100644 index a51b202..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostHypervisor.cs +++ /dev/null @@ -1,188 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostHypervisorVirtualMaschineEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("uniqueid")] - public string? UniqueId { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("notes")] - public string? Notes { get; set; } - - [BsonElement("enabled")] - public string? Enabled { get; set; } - - [BsonElement("enabled_default")] - public string? EnabledDefault { get; set; } - - [BsonElement("health")] - public string? Health { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("ontime")] - public ulong? OnTime { get; set; } - - [BsonElement("replication_state")] - public string? ReplicationState { get; set; } - - [BsonElement("replication_health")] - public string? ReplicationHealth { get; set; } - - [BsonElement("version_configuration")] - public string? ConfigurationVersion { get; set; } - - [BsonElement("version_integrated_services")] - public string? IntegrationServicesVersionState { get; set; } - - [BsonElement("processid")] - public uint? ProcessId { get; set; } - - [BsonElement("processor_count")] - public uint? NumberOfProcessors { get; set; } - - [BsonElement("processor_load")] - public uint? ProcessorLoad { get; set; } - - [BsonElement("memory_available")] - public int? MemoryAvailable { get; set; } - - [BsonElement("memory_usage")] - public ulong? MemoryUsage { get; set; } - - [BsonElement("installdate")] - public DateTime? InstallDate { get; set; } - - [BsonElement("configuration_changed")] - public DateTime? TimeOfLastConfigurationChange { get; set; } - - [BsonElement("state_changed")] - public DateTime? TimeOfLastStateChange { get; set; } - - [BsonElement("replication_last")] - public DateTime? LastReplicationTime { get; set; } - - [BsonElement("guest_os")] - public string? Os { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Configs { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostHypervisorVirtualMaschineConfigEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("virtualmaschine"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("virtualmaschine")] - public string? VirtualMaschine { get; set; } - - [BsonElement("batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("uniqueid")] - public string? UniqueId { get; set; } - - [BsonElement("parentid")] - public string? ParentId { get; set; } - - [BsonElement("type")] - public string? Type { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("notes")] - public string? Notes { get; set; } - - [BsonElement("creationtime")] - public DateTime? CreationTime { get; set; } - - [BsonElement("generation")] - public string? Generation { get; set; } - - [BsonElement("architecture")] - public string? Architecture { get; set; } - - [BsonElement("secureboot")] - public bool? SecureBootEnabled { get; set; } - - [BsonElement("automatic_snapshot")] - public bool? IsAutomaticSnapshot { get; set; } - - [BsonElement("action_start")] - public string? AutomaticStartupAction { get; set; } - - [BsonElement("action_shutdown")] - public string? AutomaticShutdownAction { get; set; } - - [BsonElement("action_recovery")] - public string? AutomaticRecoveryAction { get; set; } - - [BsonElement("auto_snapshots")] - public bool? AutomaticSnapshotsEnabled { get; set; } - - [BsonElement("serial_mainboard")] - public string? BaseBoardSerialNumber { get; set; } - - [BsonElement("serial_bios")] - public string? BIOSSerialNumber { get; set; } - - [BsonElement("bios_guid")] - public string? BIOSGUID { get; set; } - - [BsonElement("data_root")] - public string? ConfigurationDataRoot { get; set; } - - [BsonElement("file")] - public string? ConfigurationFile { get; set; } - - [BsonElement("guest_data_root")] - public string? GuestStateDataRoot { get; set; } - - [BsonElement("guest_state_file")] - public string? GuestStateFile { get; set; } - - [BsonElement("snapshot_data_root")] - public string? SnapshotDataRoot { get; set; } - - [BsonElement("suspend_data_root")] - public string? SuspendDataRoot { get; set; } - - [BsonElement("swap_data_root")] - public string? SwapFileDataRoot { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostInterface.cs b/src/Core/Insight.Infrastructure/Entities/HostInterface.cs deleted file mode 100644 index 2b18c18..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostInterface.cs +++ /dev/null @@ -1,218 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostInterfaceEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("index")] - public uint? Index { get; set; } - - [BsonElement("mac")] - public string? Mac { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("description")] - public string? Description { get; set; } - - [BsonElement("physical")] - public bool? Physical { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("suffix")] - public string? Suffix { get; set; } - - [BsonElement("speed")] - public long? Speed { get; set; } - - [BsonElement("ipv4_mtu")] - public long? Ipv4Mtu { get; set; } - - [BsonElement("ipv4_dhcp")] - public bool? Ipv4Dhcp { get; set; } - - [BsonElement("ipv4_forwarding")] - public bool? Ipv4Forwarding { get; set; } - - [BsonElement("ipv6_mtu")] - public long? Ipv6Mtu { get; set; } - - [BsonElement("sent")] - public long? Sent { get; set; } - - [BsonElement("received")] - public long? Received { get; set; } - - [BsonElement("packets_incoming_discarded")] - public long? IncomingPacketsDiscarded { get; set; } - - [BsonElement("packets_incoming_errors")] - public long? IncomingPacketsWithErrors { get; set; } - - [BsonElement("packets_incoming_unknown")] - public long? IncomingUnknownProtocolPackets { get; set; } - - [BsonElement("packets_outgoing_discarded")] - public long? OutgoingPacketsDiscarded { get; set; } - - [BsonElement("packets_outgoing_errors")] - public long? OutgoingPacketsWithErrors { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostInterfaceAddressEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] - public string? Interface { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("address")] - public string? Address { get; set; } - - [BsonElement("mask")] - public string? Mask { get; set; } - - //public string? State { get; set; } - //public long? PreferredLifetime { get; set; } - //public long? ValidLifetime { get; set; } - //public long? LeaseLifetime { get; set; } - //public int? PrefixLength { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Interfaces { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostInterfaceGatewayEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] - public string? Interface { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("address")] - public string? Address { get; set; } - - [BsonElement("mask")] - public string? Mask { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Interfaces { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostInterfaceNameserverEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] - public string? Interface { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("address")] - public string? Address { get; set; } - - [BsonElement("mask")] - public string? Mask { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Interfaces { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostInterfaceRouteEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_interface"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("interface")] - public string? Interface { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("destination")] - public string? Destination { get; set; } - - [BsonElement("mask")] - public string? Mask { get; set; } - - [BsonElement("gateway")] - public string? Gateway { get; set; } - - [BsonElement("metric")] - public int? Metric { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? Interfaces { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostLog.cs b/src/Core/Insight.Infrastructure/Entities/HostLog.cs deleted file mode 100644 index c6d4ae4..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostLog.cs +++ /dev/null @@ -1,37 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostLogEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("eventid")] - public string? EventId { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("source")] - public string? Source { get; set; } - - [BsonElement("category")] - public string? Category { get; set; } - - [BsonElement("message")] - public string? Message { get; set; } - - [BsonElement("timestamp")] - public DateTime? Timestamp { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostLogMonitoring.cs b/src/Core/Insight.Infrastructure/Entities/HostLogMonitoring.cs deleted file mode 100644 index 67bf429..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostLogMonitoring.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostLogMonitoringEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("hostname")] - public string? Hostname { get; set; } - - [BsonElement("category")] - public string? Category { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("task")] - public string? Task { get; set; } - - [BsonElement("message")] - public string? Message { get; set; } - - [BsonElement("dispatch")] - public string? Dispatch { get; set; } - - [BsonElement("timestamp")] - public DateTime? Timestamp { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostMainboard.cs b/src/Core/Insight.Infrastructure/Entities/HostMainboard.cs deleted file mode 100644 index f4fd655..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostMainboard.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostMainboardEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } - - [BsonElement("bios")] - public string? Bios { get; set; } - - [BsonElement("version")] - public string? Version { get; set; } - - [BsonElement("Date")] - public DateTime? Date { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostMemory.cs b/src/Core/Insight.Infrastructure/Entities/HostMemory.cs deleted file mode 100644 index 1442941..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostMemory.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostMemoryEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("index")] - public uint? Index { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("tag")] - public string? Tag { get; set; } - - [BsonElement("location")] - public string? Location { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } - - [BsonElement("capacity")] - public ulong? Capacity { get; set; } - - [BsonElement("clock")] - public uint? Clock { get; set; } - - [BsonElement("clock_current")] - public uint? CurrentClock { get; set; } - - [BsonElement("voltage")] - public uint? Voltage { get; set; } - - [BsonElement("voltage_current")] - public uint? CurrentVoltage { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostOs.cs b/src/Core/Insight.Infrastructure/Entities/HostOs.cs deleted file mode 100644 index b0e0d8f..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostOs.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostOsEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("version")] - public string? Version { get; set; } - - [BsonElement("architecture")] - public string? Architecture { get; set; } - - [BsonElement("serialnumber")] - public string? SerialNumber { get; set; } - - [BsonElement("virtual")] - public bool? Virtual { get; set; } - - [BsonElement("installed")] - public DateTime? Installed { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostPrinter.cs b/src/Core/Insight.Infrastructure/Entities/HostPrinter.cs deleted file mode 100644 index 7131499..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostPrinter.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostPrinterEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("port")] - public string? Port { get; set; } - - [BsonElement("location")] - public string? Location { get; set; } - - [BsonElement("comment")] - public string? Comment { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostProcessor.cs b/src/Core/Insight.Infrastructure/Entities/HostProcessor.cs deleted file mode 100644 index 1a81ceb..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostProcessor.cs +++ /dev/null @@ -1,70 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostProcessorEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("index")] - public uint? Index { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("socket")] - public string? Socket { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } - - [BsonElement("version")] - public string? Version { get; set; } - - [BsonElement("cores")] - public uint? Cores { get; set; } - - [BsonElement("logicalcores")] - public uint? LogicalCores { get; set; } - - [BsonElement("clock")] - public uint? Clock { get; set; } - - [BsonElement("clock_current")] - public uint? CurrentClock { get; set; } - - [BsonElement("l1")] - public uint? L1Size { get; set; } - - [BsonElement("l2")] - public uint? L2Size { get; set; } - - [BsonElement("l3")] - public uint? L3Size { get; set; } - - [BsonElement("virtualization")] - public bool? Virtualization { get; set; } - - [BsonElement("pnp")] - public string? PNP { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostService.cs b/src/Core/Insight.Infrastructure/Entities/HostService.cs deleted file mode 100644 index c2e71d7..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostService.cs +++ /dev/null @@ -1,55 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostServiceEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("displayname")] - public string? DisplayName { get; set; } - - [BsonElement("description")] - public string? Description { get; set; } - - [BsonElement("startmode")] - public string? StartMode { get; set; } - - [BsonElement("state")] - public string? State { get; set; } - - [BsonElement("processid")] - public uint? ProcessId { get; set; } - - [BsonElement("delay")] - public bool? Delay { get; set; } - - [BsonElement("path")] - public string? Path { get; set; } - - [BsonElement("account")] - public string? Account { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostSession.cs b/src/Core/Insight.Infrastructure/Entities/HostSession.cs deleted file mode 100644 index 7c58585..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostSession.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostSessionEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("sid")] - public string? Sid { get; set; } - - [BsonElement("user")] - public string? User { get; set; } - - [BsonElement("remote")] - public string? Remote { get; set; } - - [BsonElement("type")] - public string? Type { get; set; } - - [BsonElement("state")] - public string? State { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostStoragePool.cs b/src/Core/Insight.Infrastructure/Entities/HostStoragePool.cs deleted file mode 100644 index 67dd652..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostStoragePool.cs +++ /dev/null @@ -1,207 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostStoragePoolEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("uniqueid")] - public string? UniqueId { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("health")] - public string? Health { get; set; } - - [BsonElement("resiliency")] - public string? Resiliency { get; set; } - - [BsonElement("primordial")] - public bool? Primordial { get; set; } - - [BsonElement("readonly")] - public bool? ReadOnly { get; set; } - - [BsonElement("clustered")] - public bool? Clustered { get; set; } - - [BsonElement("size")] - public ulong? Size { get; set; } - - [BsonElement("size_allocated")] - public ulong? AllocatedSize { get; set; } - - [BsonElement("sectorsize")] - public ulong? SectorSize { get; set; } - - [BsonElement("states")] - public List? States { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? PhysicalDisks { get; set; } - - [BsonIgnoreIfNull, JsonIgnore] - public List? VirtualDisks { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostStoragePoolVirtualDiskEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_storagepool"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("storagepool")] - public string? StoragePool { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("uniqueid")] - public string? UniqueId { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("health")] - public string? Health { get; set; } - - [BsonElement("access")] - public string? Access { get; set; } - - [BsonElement("provisioning")] - public string? Provisioning { get; set; } - - [BsonElement("redundancy")] - public uint? PhysicalRedundancy { get; set; } - - [BsonElement("resiliency")] - public string? Resiliency { get; set; } - - [BsonElement("deduplication")] - public bool? Deduplication { get; set; } - - [BsonElement("snapshot")] - public bool? Snapshot { get; set; } - - [BsonElement("size")] - public ulong? Size { get; set; } - - [BsonElement("size_allocated")] - public ulong? AllocatedSize { get; set; } - - [BsonElement("footprint")] - public ulong? Footprint { get; set; } - - [BsonElement("cache_read_size")] - public ulong? ReadCacheSize { get; set; } - - [BsonElement("cache_write_size")] - public ulong? WriteCacheSize { get; set; } - - [BsonElement("states")] - public List? States { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostStoragePoolPhysicalDiskEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_storagepool"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("storagepool")] - public string? StoragePool { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("uniqueid")] - public string? UniqueId { get; set; } - - [BsonElement("deviceid")] - public string? DeviceId { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("manufacturer")] - public string? Manufacturer { get; set; } - - [BsonElement("Model")] - public string? Model { get; set; } - - [BsonElement("media")] - public string? Media { get; set; } - - [BsonElement("bus")] - public string? Bus { get; set; } - - [BsonElement("health")] - public string? Health { get; set; } - - [BsonElement("usage")] - public ushort? Usage { get; set; } - - [BsonElement("location")] - public string? Location { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } - - [BsonElement("firmware")] - public string? Firmware { get; set; } - - [BsonElement("size")] - public ulong? Size { get; set; } - - [BsonElement("size_allocated")] - public ulong? AllocatedSize { get; set; } - - [BsonElement("footprint")] - public ulong? Footprint { get; set; } - - [BsonElement("sector_size_physical")] - public ulong? PhysicalSectorSize { get; set; } - - [BsonElement("sector_size_logical")] - public ulong? LogicalSectorSize { get; set; } - - [BsonElement("states")] - public List? States { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostSystem.cs b/src/Core/Insight.Infrastructure/Entities/HostSystem.cs deleted file mode 100644 index 7cea81e..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostSystem.cs +++ /dev/null @@ -1,34 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostSystemEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("localtime")] - public DateTime? LocalTime { get; set; } - - [BsonElement("bootuptime")] - public DateTime? BootUpTime { get; set; } - - [BsonElement("processes")] - public uint? Processes { get; set; } - - [BsonElement("license")] - public string? License { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostUpdate.cs b/src/Core/Insight.Infrastructure/Entities/HostUpdate.cs deleted file mode 100644 index 2e2e545..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostUpdate.cs +++ /dev/null @@ -1,61 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostUpdateEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } // os update id - - [BsonElement("description")] - public string? Description { get; set; } - - [BsonElement("supporturl")] - public string? SupportUrl { get; set; } - - [BsonElement("date")] - public DateTime? Date { get; set; } - - [BsonElement("pending")] - public bool? Pending { get; set; } - - [BsonElement("result"), BsonIgnoreIfNull] // installed only - public string? Result { get; set; } - - [BsonElement("type"), BsonIgnoreIfNull] // pending only - public string? Type { get; set; } - - [BsonElement("size"), BsonIgnoreIfNull] // pending only - public decimal? Size { get; set; } - - [BsonElement("downloaded"), BsonIgnoreIfNull] // pending only - public bool? IsDownloaded { get; set; } - - [BsonElement("inputrequest"), BsonIgnoreIfNull] // pending only - public bool? CanRequestUserInput { get; set; } - - [BsonElement("reboot"), BsonIgnoreIfNull] // pending only - public string? RebootBehavior { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostUser.cs b/src/Core/Insight.Infrastructure/Entities/HostUser.cs deleted file mode 100644 index 7ef4201..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostUser.cs +++ /dev/null @@ -1,120 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostUserEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("sid")] - public string? Sid { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("domain")] - public string? Domain { get; set; } - - [BsonElement("fullname")] - public string? FullName { get; set; } - - [BsonElement("description")] - public string? Description { get; set; } - - [BsonElement("status")] - public string? Status { get; set; } - - [BsonElement("localaccount")] - public bool? LocalAccount { get; set; } - - [BsonElement("disabled")] - public bool? Disabled { get; set; } - - [BsonElement("lockout")] - public bool? Lockout { get; set; } - - [BsonElement("password_changeable")] - public bool? PasswordChangeable { get; set; } - - [BsonElement("password_expires")] - public bool? PasswordExpires { get; set; } - - [BsonElement("password_required")] - public bool? PasswordRequired { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostGroupEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("sid")] - public string? Sid { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("domain")] - public string? Domain { get; set; } - - [BsonElement("description")] - public string? Description { get; set; } - - [BsonElement("localaccount")] - public bool? LocalAccount { get; set; } - } - - [BsonIgnoreExtraElements] - public class HostUserGroupEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_user"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("user")] - public string? User { get; set; } - - [BsonElement("_group"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("group")] - public string? Group { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostVideocard.cs b/src/Core/Insight.Infrastructure/Entities/HostVideocard.cs deleted file mode 100644 index a2552a9..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostVideocard.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostVideocardEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("company")] - public string? Company { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("memory")] - public ulong? Memory { get; set; } - - [BsonElement("driver")] - public string? Driver { get; set; } - - [BsonElement("date")] - public DateTime? Date { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/HostVolume.cs b/src/Core/Insight.Infrastructure/Entities/HostVolume.cs deleted file mode 100644 index 00c19df..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostVolume.cs +++ /dev/null @@ -1,76 +0,0 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [BsonIgnoreExtraElements] - public class HostVolumeEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_host"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("host")] - public string? Host { get; set; } - - [BsonElement("_drive"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("drive")] - public string? Drive { get; set; } - - [BsonElement("_batch"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("batch")] - public string? Batch { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("index")] - public uint? Index { get; set; } - - [BsonElement("label")] - public string? Label { get; set; } - - [BsonElement("name")] - public string? Name { get; set; } - - [BsonElement("serial")] - public string? Serial { get; set; } - - [BsonElement("size")] - public ulong? Size { get; set; } - - [BsonElement("freespace")] - public ulong? FreeSpace { get; set; } - - [BsonElement("type")] - public string? Type { get; set; } - - [BsonElement("filesystem")] - public string? FileSystem { get; set; } - - [BsonElement("compressed")] - public bool? Compressed { get; set; } - - [BsonElement("bootable")] - public bool? Bootable { get; set; } - - [BsonElement("primary")] - public bool? Primary { get; set; } - - [BsonElement("boot")] - public bool? Boot { get; set; } - - [BsonElement("blocksize")] - public ulong? BlockSize { get; set; } - - [BsonElement("blocks")] - public ulong? Blocks { get; set; } - - [BsonElement("startoffset")] - public ulong? StartingOffset { get; set; } - - [BsonElement("provider")] - public string? Provider { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Identity.cs b/src/Core/Insight.Infrastructure/Entities/Identity.cs deleted file mode 100644 index 9827a9b..0000000 --- a/src/Core/Insight.Infrastructure/Entities/Identity.cs +++ /dev/null @@ -1,99 +0,0 @@ -using AspNetCore.Identity.MongoDbCore.Models; -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using MongoDbGenericRepository.Attributes; -using System.Text.Json.Serialization; - -namespace Insight.Infrastructure.Entities -{ - [CollectionName("user"), BsonIgnoreExtraElements] - public class InsightUser : MongoIdentityUser - { - public InsightUser() : base() { } - - public InsightUser(string userName, string email) : base(userName, email) { } - - [JsonPropertyName("refresh_tokens")] - public List? RefreshTokens { get; set; } - } - - [BsonIgnoreExtraElements] - public class InsightUserLogEntity - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_user"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("user")] - public string? User { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("timestamp")] - public DateTime? Timestamp { get; set; } - - [BsonElement("message")] - public string? Message { get; set; } - } - - [CollectionName("user_pref"), BsonIgnoreExtraElements] - public class InsightUserPreferences - { - [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] - public string? Id { get; set; } - - [BsonElement("_user"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("user")] - public string? User { get; set; } - - [BsonElement("insert")] - public DateTime? Insert { get; set; } - - [BsonElement("update")] - public DateTime? Update { get; set; } - - [BsonElement("darkmode")] - public bool DarkMode { get; set; } - } - - [CollectionName("role")] - public class InsightRole : MongoIdentityRole - { - public InsightRole() : base() { } - - public InsightRole(string roleName) : base(roleName) { } - } - - [BsonIgnoreExtraElements] - public class RefreshToken - { - [BsonElement("token")] - public string? Token { get; set; } - - [BsonElement("created")] - public DateTime Created { get; set; } - - [BsonElement("created_ip")] - public string? CreatedByIp { get; set; } - - [BsonElement("expires")] - public DateTime Expires { get; set; } - - [BsonElement("revoked")] - public DateTime? Revoked { get; set; } - - [BsonElement("revoked_ip")] - public string? RevokedByIp { get; set; } - - [BsonElement("revoked_reason")] - public string? ReasonRevoked { get; set; } - - [BsonIgnore, JsonIgnore] - public bool IsExpired => DateTime.Now >= Expires.ToLocalTime(); - - [BsonIgnore, JsonIgnore] - public bool IsRevoked => Revoked != null; - - [BsonIgnore, JsonIgnore] - public bool IsActive => !IsRevoked && !IsExpired; - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Identity/Identity.cs b/src/Core/Insight.Infrastructure/Entities/Identity/Identity.cs new file mode 100644 index 0000000..4f01e89 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Identity/Identity.cs @@ -0,0 +1,104 @@ +using AspNetCore.Identity.MongoDbCore.Models; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using MongoDbGenericRepository.Attributes; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection User(this IMongoDatabase database) => database.GetCollection("user"); + public static IMongoCollection UserLog(this IMongoDatabase database) => database.GetCollection("user_log"); + public static IMongoCollection UserPreference(this IMongoDatabase database) => database.GetCollection("user_pref"); + public static IMongoCollection Role(this IMongoDatabase database) => database.GetCollection("role"); +} + +[CollectionName("user"), BsonIgnoreExtraElements] +public class InsightUser : MongoIdentityUser +{ + public InsightUser() : base() { } + + public InsightUser(string userName, string email) : base(userName, email) { } + + [JsonPropertyName("refresh_tokens")] + public List? RefreshTokens { get; set; } +} + +[BsonIgnoreExtraElements] +public class InsightUserLogEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_user"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("user")] + public string? User { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("timestamp")] + public DateTime? Timestamp { get; set; } + + [BsonElement("message")] + public string? Message { get; set; } +} + +[CollectionName("user_pref"), BsonIgnoreExtraElements] +public class InsightUserPreferences +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_user"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("user")] + public string? User { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + [BsonElement("darkmode")] + public bool DarkMode { get; set; } +} + +[CollectionName("role")] +public class InsightRole : MongoIdentityRole +{ + +} + +[BsonIgnoreExtraElements] +public class RefreshToken +{ + [BsonElement("token")] + public string? Token { get; set; } + + [BsonElement("created")] + public DateTime Created { get; set; } + + [BsonElement("created_ip")] + public string? CreatedByIp { get; set; } + + [BsonElement("expires")] + public DateTime Expires { get; set; } + + [BsonElement("revoked")] + public DateTime? Revoked { get; set; } + + [BsonElement("revoked_ip")] + public string? RevokedByIp { get; set; } + + [BsonElement("revoked_reason")] + public string? ReasonRevoked { get; set; } + + [BsonIgnore, JsonIgnore] + public bool IsExpired => DateTime.Now >= Expires.ToLocalTime(); + + [BsonIgnore, JsonIgnore] + public bool IsRevoked => Revoked != null; + + [BsonIgnore, JsonIgnore] + public bool IsActive => !IsRevoked && !IsExpired; +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Scheduler/Job.cs b/src/Core/Insight.Infrastructure/Entities/Scheduler/Job.cs new file mode 100644 index 0000000..e0f0c30 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Scheduler/Job.cs @@ -0,0 +1,43 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; +using Vaitr.Scheduler; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection Job(this IMongoDatabase database) => database.GetCollection("job"); +} + +[BsonIgnoreExtraElements] +public class JobEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("state"), BsonRepresentation(BsonType.String)] + public TaskState State { get; set; } + + [BsonElement("timeout")] + public TimeSpan Timeout { get; set; } + + [BsonElement("executed")] + public DateTime Executed { get; set; } + + [BsonElement("disabled")] + public bool Disabled { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Scheduler/JobLog.cs b/src/Core/Insight.Infrastructure/Entities/Scheduler/JobLog.cs new file mode 100644 index 0000000..5ae9db1 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Scheduler/JobLog.cs @@ -0,0 +1,38 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; +using Vaitr.Scheduler; +using static Insight.Infrastructure.Entities.TriggerEntity; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection JobLog(this IMongoDatabase database) => database.GetCollection("job_log"); +} + +[BsonIgnoreExtraElements] +public class JobLogEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_job"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("job")] + public string? Job { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("timestamp")] + public DateTime? Timestamp { get; set; } + + [BsonElement("status"), BsonRepresentation(BsonType.String)] + public TaskState? Status { get; set; } + + [BsonElement("mode"), BsonRepresentation(BsonType.String)] + public ScheduleMode? Mode { get; set; } + + [BsonElement("message")] + public string? Message { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Scheduler/JobTask.cs b/src/Core/Insight.Infrastructure/Entities/Scheduler/JobTask.cs new file mode 100644 index 0000000..a6c7fa1 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Scheduler/JobTask.cs @@ -0,0 +1,30 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection JobTask(this IMongoDatabase database) => database.GetCollection("job_task"); +} + +[BsonIgnoreExtraElements] +public class JobTaskEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_job"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("job")] + public string? Job { get; set; } + + [BsonElement("_task"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("task")] + public string? Task { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Scheduler/JobTrigger.cs b/src/Core/Insight.Infrastructure/Entities/Scheduler/JobTrigger.cs new file mode 100644 index 0000000..6c99dc6 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Scheduler/JobTrigger.cs @@ -0,0 +1,30 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection JobTrigger(this IMongoDatabase database) => database.GetCollection("job_trigger"); +} + +[BsonIgnoreExtraElements] +public class JobTriggerEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("_job"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("job")] + public string? Job { get; set; } + + [BsonElement("_trigger"), BsonRepresentation(BsonType.ObjectId), JsonPropertyName("trigger")] + public string? Trigger { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Scheduler/TaskEntity.cs b/src/Core/Insight.Infrastructure/Entities/Scheduler/TaskEntity.cs new file mode 100644 index 0000000..8811d08 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Scheduler/TaskEntity.cs @@ -0,0 +1,33 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection Task(this IMongoDatabase database) => database.GetCollection("task"); +} + +[BsonIgnoreExtraElements] +public class TaskEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonIgnore] + public Func? Action { get; set; } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Entities/Scheduler/TriggerEntity.cs b/src/Core/Insight.Infrastructure/Entities/Scheduler/TriggerEntity.cs new file mode 100644 index 0000000..95e5381 --- /dev/null +++ b/src/Core/Insight.Infrastructure/Entities/Scheduler/TriggerEntity.cs @@ -0,0 +1,46 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System.Text.Json.Serialization; + +namespace Insight.Infrastructure.Entities; + +public static partial class MongoDatabaseExtensions +{ + public static IMongoCollection Trigger(this IMongoDatabase database) => database.GetCollection("trigger"); +} + +public class TriggerEntity +{ + [BsonId, BsonRepresentation(BsonType.ObjectId), JsonPropertyName("id")] + public string? Id { get; set; } + + [BsonElement("insert")] + public DateTime? Insert { get; set; } + + [BsonElement("update")] + public DateTime? Update { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("description")] + public string? Description { get; set; } + + [BsonElement("mode")] + public ScheduleMode Mode { get; set; } + + [BsonElement("date_start")] + public DateTime StartTime { get; set; } + + [BsonElement("date_end")] + public DateTime? EndTime { get; set; } + + [BsonElement("interval")] + public TimeSpan? Interval { get; set; } + + public enum ScheduleMode + { + Single, Loop + } +} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Extensions/HttpRequestExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/HttpRequestExtensions.cs index 27d0cb9..7d6ca72 100644 --- a/src/Core/Insight.Infrastructure/Extensions/HttpRequestExtensions.cs +++ b/src/Core/Insight.Infrastructure/Extensions/HttpRequestExtensions.cs @@ -2,46 +2,45 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; -namespace Insight.Infrastructure +namespace Insight.Infrastructure; + +public static class HttpRequestExtensions { - public static class HttpRequestExtensions + public static void AddPagination(this HttpRequest request, PagedList pagelist) { - public static void AddPagination(this HttpRequest request, PagedList pagelist) + var builder = new QueryBuilder(); + + foreach (var item in request.Query.Where(p => p.Key.ToLower() != "limit" || p.Key.ToLower() != "offset")) { - var builder = new QueryBuilder(); + builder.Add(item.Key.ToLower(), item.Value.ToString()); + } - foreach (var item in request.Query.Where(p => p.Key.ToLower() != "limit" || p.Key.ToLower() != "offset")) + builder.Add("limit", pagelist.Meta.Limit.ToString()); + + if (pagelist.Meta.Offset > 0) + { + var qb = new QueryBuilder(builder); + + if (pagelist.Meta.Offset > pagelist.Meta.Limit) { - builder.Add(item.Key.ToLower(), item.Value.ToString()); + qb.Add("offset", (pagelist.Meta.Offset - pagelist.Meta.Limit).ToString()); + } + else + { + qb.Add("offset", 0.ToString()); } - builder.Add("limit", pagelist.Meta.Limit.ToString()); + pagelist.Meta.Previous = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}{qb}"; + } - if (pagelist.Meta.Offset > 0) + if ((pagelist.Meta.Offset + pagelist.Meta.Count) < pagelist.Meta.Total) + { + var qb = new QueryBuilder(builder) { - var qb = new QueryBuilder(builder); + { "offset", (pagelist.Meta.Offset + pagelist.Meta.Count).ToString() } + }; - if (pagelist.Meta.Offset > pagelist.Meta.Limit) - { - qb.Add("offset", (pagelist.Meta.Offset - pagelist.Meta.Limit).ToString()); - } - else - { - qb.Add("offset", 0.ToString()); - } - - pagelist.Meta.Previous = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}{qb}"; - } - - if ((pagelist.Meta.Offset + pagelist.Meta.Count) < pagelist.Meta.Total) - { - var qb = new QueryBuilder(builder) - { - { "offset", (pagelist.Meta.Offset + pagelist.Meta.Count).ToString() } - }; - - pagelist.Meta.Next = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}{qb}"; - } + pagelist.Meta.Next = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}{qb}"; } } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Extensions/HttpResponseExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/HttpResponseExtensions.cs index 6bb788d..cf4d828 100644 --- a/src/Core/Insight.Infrastructure/Extensions/HttpResponseExtensions.cs +++ b/src/Core/Insight.Infrastructure/Extensions/HttpResponseExtensions.cs @@ -2,16 +2,15 @@ using Microsoft.AspNetCore.Http; using System.Text.Json; -namespace Insight.Infrastructure +namespace Insight.Infrastructure; + +public static class HttpResponseExtensions { - public static class HttpResponseExtensions + public static void AddPagination(this HttpResponse response, PagedList pagelist) { - public static void AddPagination(this HttpResponse response, PagedList pagelist) + response.Headers.Add("X-Pagination", JsonSerializer.Serialize(pagelist.Meta as PagedHeaderData, new JsonSerializerOptions { - response.Headers.Add("X-Pagination", JsonSerializer.Serialize(pagelist.Meta as PagedHeaderData, new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - })); - } + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + })); } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Extensions/MongoCollectionExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/MongoCollectionExtensions.cs index bc18ab8..3fa382b 100644 --- a/src/Core/Insight.Infrastructure/Extensions/MongoCollectionExtensions.cs +++ b/src/Core/Insight.Infrastructure/Extensions/MongoCollectionExtensions.cs @@ -4,80 +4,79 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -namespace Insight.Infrastructure +namespace Insight.Infrastructure; + +public static class MongoCollectionExtensions { - public static class MongoCollectionExtensions + private const int _maximumLimit = 100; + + public static async Task> GetPagedAsync( + this IMongoCollection collection, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) { - private const int _maximumLimit = 100; + if (limit > _maximumLimit) throw new InvalidOperationException("invalid limit value > 100"); - public static async Task> GetPagedAsync( - this IMongoCollection collection, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) - { - if (limit > _maximumLimit) throw new InvalidOperationException("invalid limit value > 100"); + var query = collection.Find(filter ?? Builders.Filter.Empty); - var query = collection.Find(filter ?? Builders.Filter.Empty); + if (sort is not null) query = query.Sort(sort); - if (sort is not null) query = query.Sort(sort); + var data = await query.Skip(offset).Limit(limit).ToListAsync(cancellationToken).ConfigureAwait(false); + var total = await collection.EstimatedDocumentCountAsync(null, cancellationToken).ConfigureAwait(false); - var data = await query.Skip(offset).Limit(limit).ToListAsync(cancellationToken).ConfigureAwait(false); - var total = await collection.EstimatedDocumentCountAsync(null, cancellationToken).ConfigureAwait(false); + return new PagedList(data, offset, limit, total); + } - return new PagedList(data, offset, limit, total); - } + public static async Task> GetPagedAsync( + this IMongoCollection collection, + HttpRequest request, + HttpResponse response, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) + { + var result = await GetPagedAsync(collection, filter, sort, offset, limit, cancellationToken).ConfigureAwait(false); - public static async Task> GetPagedAsync( - this IMongoCollection collection, - HttpRequest request, - HttpResponse response, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) - { - var result = await GetPagedAsync(collection, filter, sort, offset, limit, cancellationToken).ConfigureAwait(false); + request?.AddPagination(result); + response?.AddPagination(result); - request?.AddPagination(result); - response?.AddPagination(result); + return result; + } - return result; - } + public static async Task> GetPagedAsync( + this IMongoCollection collection, + IAggregateFluent query, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) + { + if (limit > _maximumLimit) throw new InvalidOperationException("invalid limit value"); - public static async Task> GetPagedAsync( - this IMongoCollection collection, - IAggregateFluent query, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) - { - if (limit > _maximumLimit) throw new InvalidOperationException("invalid limit value"); + var data = await query.Skip(offset).Limit(limit).ToListAsync(cancellationToken).ConfigureAwait(false); + var total = await collection.EstimatedDocumentCountAsync(null, cancellationToken).ConfigureAwait(false); - var data = await query.Skip(offset).Limit(limit).ToListAsync(cancellationToken).ConfigureAwait(false); - var total = await collection.EstimatedDocumentCountAsync(null, cancellationToken).ConfigureAwait(false); + return new PagedList(data.Select(x => BsonSerializer.Deserialize(x)), offset, limit, total); + } - return new PagedList(data.Select(x => BsonSerializer.Deserialize(x)), offset, limit, total); - } + public static async Task> GetPagedAsync( + this IMongoCollection collection, + HttpRequest request, + HttpResponse response, + IAggregateFluent query, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) + { + var result = await GetPagedAsync(collection, query, offset, limit, cancellationToken).ConfigureAwait(false); - public static async Task> GetPagedAsync( - this IMongoCollection collection, - HttpRequest request, - HttpResponse response, - IAggregateFluent query, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) - { - var result = await GetPagedAsync(collection, query, offset, limit, cancellationToken).ConfigureAwait(false); + request?.AddPagination(result); + response?.AddPagination(result); - request?.AddPagination(result); - response?.AddPagination(result); - - return result; - } + return result; } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs deleted file mode 100644 index 3769e1d..0000000 --- a/src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Insight.Infrastructure.Entities; -using MongoDB.Driver; - -namespace Insight.Infrastructure -{ - public static class MongoDatabaseExtensions - { - // internal users (roles), groups... - public static IMongoCollection User(this IMongoDatabase database) => database.GetCollection("user"); - public static IMongoCollection UserLog(this IMongoDatabase database) => database.GetCollection("user_log"); - public static IMongoCollection UserPreference(this IMongoDatabase database) => database.GetCollection("user_pref"); - public static IMongoCollection Role(this IMongoDatabase database) => database.GetCollection("role"); - - // customers - public static IMongoCollection Customer(this IMongoDatabase database) => database.GetCollection("customer"); - - // agents - public static IMongoCollection Agent(this IMongoDatabase database) => database.GetCollection("agent"); - public static IMongoCollection AgentLog(this IMongoDatabase database) => database.GetCollection("agent_log"); - - // host groups - public static IMongoCollection HostGroup(this IMongoDatabase database) => database.GetCollection("host"); - - // hosts - public static IMongoCollection Host(this IMongoDatabase database) => database.GetCollection("host"); - public static IMongoCollection HostLog(this IMongoDatabase database) => database.GetCollection("host_log"); - - // hosts extensions - public static IMongoCollection HostLogMonitoring(this IMongoDatabase database) => database.GetCollection("host_log_mon"); - public static IMongoCollection HostApplication(this IMongoDatabase database) => database.GetCollection("host_app"); - public static IMongoCollection HostDrive(this IMongoDatabase database) => database.GetCollection("host_drv"); - public static IMongoCollection HostVolume(this IMongoDatabase database) => database.GetCollection("host_vol"); - public static IMongoCollection HostOs(this IMongoDatabase database) => database.GetCollection("host_os"); - public static IMongoCollection HostUpdate(this IMongoDatabase database) => database.GetCollection("host_upd"); - public static IMongoCollection HostSession(this IMongoDatabase database) => database.GetCollection("host_session"); - public static IMongoCollection HostService(this IMongoDatabase database) => database.GetCollection("host_svc"); - public static IMongoCollection HostPrinter(this IMongoDatabase database) => database.GetCollection("host_prn"); - public static IMongoCollection HostMainboard(this IMongoDatabase database) => database.GetCollection("host_board"); - public static IMongoCollection HostProcessor(this IMongoDatabase database) => database.GetCollection("host_cpu"); - public static IMongoCollection HostMemory(this IMongoDatabase database) => database.GetCollection("host_mem"); - public static IMongoCollection HostVideocard(this IMongoDatabase database) => database.GetCollection("host_gpu"); - public static IMongoCollection HostSystemUser(this IMongoDatabase database) => database.GetCollection("host_sysusr"); - public static IMongoCollection HostSystemGroup(this IMongoDatabase database) => database.GetCollection("host_sysgrp"); - public static IMongoCollection HostSystemUserSystemGroup(this IMongoDatabase database) => database.GetCollection("host_sysusr_sysgrp"); - public static IMongoCollection HostSystem(this IMongoDatabase database) => database.GetCollection("host_sys"); - public static IMongoCollection HostStoragePool(this IMongoDatabase database) => database.GetCollection("host_sp"); - public static IMongoCollection HostStoragePoolPhysicalDisk(this IMongoDatabase database) => database.GetCollection("host_sp.pd"); - public static IMongoCollection HostStoragePoolVirtualDisk(this IMongoDatabase database) => database.GetCollection("host_sp.vd"); - public static IMongoCollection HostHypervisorVirtualMaschine(this IMongoDatabase database) => database.GetCollection("host_hv_vm"); - public static IMongoCollection HostVirtualMaschineConfig(this IMongoDatabase database) => database.GetCollection("host_hv_vm_cfg"); - public static IMongoCollection HostInterface(this IMongoDatabase database) => database.GetCollection("host_if"); - public static IMongoCollection HostInterfaceAddress(this IMongoDatabase database) => database.GetCollection("host_if_addr"); - public static IMongoCollection HostInterfaceGateway(this IMongoDatabase database) => database.GetCollection("host_if_gw"); - public static IMongoCollection HostInterfaceNameserver(this IMongoDatabase database) => database.GetCollection("host_if_ns"); - public static IMongoCollection HostInterfaceRoute(this IMongoDatabase database) => database.GetCollection("host_if_rt"); - } -} \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs index e8e49d6..f46cf99 100644 --- a/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs +++ b/src/Core/Insight.Infrastructure/Extensions/ServiceExtensions.cs @@ -16,248 +16,247 @@ using MongoDB.Driver; using MongoDB.Driver.Core.Configuration; using System.Text; -namespace Insight.Infrastructure +namespace Insight.Infrastructure; + +public static class ServiceExtensions { - public static 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(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)"); + + var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString)); + settings.ConnectTimeout = TimeSpan.FromSeconds(3); + settings.IPv6 = false; + + if (loggerFactory is not null) { - var connectionString = configuration.GetValue(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)"); - - var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString)); - settings.ConnectTimeout = TimeSpan.FromSeconds(3); - settings.IPv6 = false; - - if (loggerFactory is not null) - { - settings.LoggingSettings = new LoggingSettings(loggerFactory); - } - - services.AddSingleton(new MongoClient(settings)); - services.AddSingleton(provider => provider.GetRequiredService()); - return services.AddSingleton(provider => provider.GetRequiredService().GetDatabase(Settings.Database)); + settings.LoggingSettings = new LoggingSettings(loggerFactory); } - public static IServiceCollection AddInfrastructureServices(this IServiceCollection services) + services.AddSingleton(new MongoClient(settings)); + services.AddSingleton(provider => provider.GetRequiredService()); + return services.AddSingleton(provider => provider.GetRequiredService().GetDatabase(Settings.Database)); + } + + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + return services; + } + + public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration) + { + var connectionString = configuration.GetValue(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)"); + + services.AddIdentity(options => { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - return services; - } - public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration) + }) + .AddMongoDbStores(connectionString, Settings.Database) + .AddDefaultTokenProviders() + .AddSignInManager(); + + return services; + } + + public static IServiceCollection AddTokenServices(this IServiceCollection services, IConfiguration configuration) + { + var options = new Models.TokenOptions( + key: configuration.GetValue(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)"), + expires: configuration.GetValue(Appsettings.JwtExp) ?? throw new Exception($"{Appsettings.JwtExp} value not set (appsettings)"), + audience: configuration.GetValue(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)"), + issuer: configuration.GetValue(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)")); + + services.AddSingleton(options); + services.AddTransient(); + + return services; + } + + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + { + // REWRITE TO COOKIE ONLY FOR WEB + + services.AddAuthentication(options => { - var connectionString = configuration.GetValue(Appsettings.Database) ?? throw new Exception($"{Appsettings.Database} value not set (appsettings)"); - - services.AddIdentity(options => - { - - }) - .AddMongoDbStores(connectionString, Settings.Database) - .AddDefaultTokenProviders() - .AddSignInManager(); - - return services; - } - - public static IServiceCollection AddTokenServices(this IServiceCollection services, IConfiguration configuration) + options.DefaultScheme = "Custom"; + options.DefaultChallengeScheme = "Custom"; + }) + .AddCookie("Cookies", options => { - var options = new Models.TokenOptions( - key: configuration.GetValue(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)"), - expires: configuration.GetValue(Appsettings.JwtExp) ?? throw new Exception($"{Appsettings.JwtExp} value not set (appsettings)"), - audience: configuration.GetValue(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)"), - issuer: configuration.GetValue(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)")); + //options.Cookie.Domain = "insight.webmatic.de"; + options.Cookie.Name = "insight"; + options.LoginPath = "/account/login"; + options.LogoutPath = "/account/logout"; + options.ExpireTimeSpan = TimeSpan.FromHours(1); + options.SlidingExpiration = true; - services.AddSingleton(options); - services.AddTransient(); - - return services; - } - - public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) - { - // REWRITE TO COOKIE ONLY FOR WEB - - services.AddAuthentication(options => + options.Events.OnRedirectToLogin = options => { - options.DefaultScheme = "Custom"; - options.DefaultChallengeScheme = "Custom"; - }) - .AddCookie("Cookies", options => - { - //options.Cookie.Domain = "insight.webmatic.de"; - options.Cookie.Name = "insight"; - options.LoginPath = "/account/login"; - options.LogoutPath = "/account/logout"; - options.ExpireTimeSpan = TimeSpan.FromHours(1); - options.SlidingExpiration = true; + if (options.Request.Path.StartsWithSegments("/api") && options.Response.StatusCode == 200) + options.Response.StatusCode = 401; + else + options.Response.Redirect(options.RedirectUri); - options.Events.OnRedirectToLogin = options => - { - if (options.Request.Path.StartsWithSegments("/api") && options.Response.StatusCode == 200) - options.Response.StatusCode = 401; - else - options.Response.Redirect(options.RedirectUri); - - return Task.CompletedTask; - }; - }) - .AddJwtBearer("Bearer", options => - { - options.RequireHttpsMetadata = false; - options.SaveToken = true; - - options.TokenValidationParameters.ValidateActor = false; - - options.TokenValidationParameters.ValidAudience = configuration.GetSection("Jwt:Audience").Value; - options.TokenValidationParameters.ValidateAudience = true; - - options.TokenValidationParameters.ValidIssuer = configuration.GetSection("Jwt:Issuer").Value; - options.TokenValidationParameters.ValidateIssuer = true; - - options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey( - Encoding.UTF8.GetBytes(configuration.GetSection("Jwt:Key").Value ?? throw new ArgumentNullException(nameof(TokenValidationParameters), "Jwt:Key")) - ); - - options.TokenValidationParameters.ValidateIssuerSigningKey = true; - options.TokenValidationParameters.ValidateLifetime = true; - }) - .AddPolicyScheme("Custom", "Custom", options => - { - options.ForwardDefaultSelector = context => - { - string authorization = context.Request.Headers[HeaderNames.Authorization]; - - if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ")) return "Bearer"; - - return "Cookies"; - }; - }); - - return services; - } - - public static IServiceCollection AddBearerAuthentication(this IServiceCollection services, IConfiguration configuration) - { - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(options => - { - options.RequireHttpsMetadata = false; - options.SaveToken = true; - - options.TokenValidationParameters.ValidateActor = false; - - options.TokenValidationParameters.ValidAudience = configuration.GetValue(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)"); - options.TokenValidationParameters.ValidateAudience = true; - - options.TokenValidationParameters.ValidIssuer = configuration.GetValue(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)"); - options.TokenValidationParameters.ValidateIssuer = true; - - options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey( - Encoding.UTF8.GetBytes(configuration.GetValue(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)")) - ); - - options.TokenValidationParameters.ValidateIssuerSigningKey = true; - options.TokenValidationParameters.ValidateLifetime = true; - }); - - return services; - } - - public static IServiceCollection AddProxyServices(this IServiceCollection services, IConfiguration configuration) - { - // add before routing - services.Configure(options => - { - options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; - }); - - return services; - } - - public static IServiceCollection AddRoutingServices(this IServiceCollection services, IConfiguration configuration) - { - // add after proxy - services.AddRouting(options => - { - options.LowercaseUrls = true; - }); - - return services; - } - - private static IServiceCollection AddIdentityServices2(this IServiceCollection services, IConfiguration configuration) - { - var identityOptions = new MongoDbIdentityConfiguration - { - MongoDbSettings = new MongoDbSettings - { - ConnectionString = configuration.GetSection("ConnectionStrings:Mongo").Value, - DatabaseName = "insight" - }, - IdentityOptionsAction = options => - { - options.User.RequireUniqueEmail = true; - options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@.-_"; - - options.Password.RequireDigit = false; - options.Password.RequiredLength = 8; - options.Password.RequireNonAlphanumeric = false; - options.Password.RequireUppercase = false; - options.Password.RequireLowercase = false; - - options.SignIn.RequireConfirmedAccount = false; - options.SignIn.RequireConfirmedEmail = false; - options.SignIn.RequireConfirmedPhoneNumber = false; - - options.Lockout.MaxFailedAccessAttempts = 5; - options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); - } + return Task.CompletedTask; }; - - services.ConfigureMongoDbIdentity(identityOptions) - .AddDefaultTokenProviders() - .AddSignInManager(); - - return services; - } - - private static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, IConfiguration configuration) + }) + .AddJwtBearer("Bearer", options => { - services.AddAuthentication(options => + options.RequireHttpsMetadata = false; + options.SaveToken = true; + + options.TokenValidationParameters.ValidateActor = false; + + options.TokenValidationParameters.ValidAudience = configuration.GetSection("Jwt:Audience").Value; + options.TokenValidationParameters.ValidateAudience = true; + + options.TokenValidationParameters.ValidIssuer = configuration.GetSection("Jwt:Issuer").Value; + options.TokenValidationParameters.ValidateIssuer = true; + + options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey( + Encoding.UTF8.GetBytes(configuration.GetSection("Jwt:Key").Value ?? throw new ArgumentNullException(nameof(TokenValidationParameters), "Jwt:Key")) + ); + + options.TokenValidationParameters.ValidateIssuerSigningKey = true; + options.TokenValidationParameters.ValidateLifetime = true; + }) + .AddPolicyScheme("Custom", "Custom", options => + { + options.ForwardDefaultSelector = context => { - //options.DefaultAuthenticateScheme = - }); - //cookieBuilder.ApplicationCookie = builder.AddApplicationCookie(); - //cookieBuilder.ExternalCookie = builder.AddExternalCookie(); - //cookieBuilder.TwoFactorRememberMeCookie = builder.AddTwoFactorRememberMeCookie(); - //cookieBuilder.TwoFactorUserIdCookie = builder.AddTwoFactorUserIdCookie(); - //.AddCookie(options => - //{ - // options. - //}; - //.AddIdentityCookies(); - //.AddCookie(options => - //{ - // // Specify where to redirect un-authenticated users - // options.LoginPath = "/account/login"; + string authorization = context.Request.Headers[HeaderNames.Authorization]; - // // Specify the name of the auth cookie. - // // ASP.NET picks a dumb name by default. - // options.Cookie.Name = "insight"; - //}); + if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ")) return "Bearer"; - return services; - } + return "Cookies"; + }; + }); + + return services; + } + + public static IServiceCollection AddBearerAuthentication(this IServiceCollection services, IConfiguration configuration) + { + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.RequireHttpsMetadata = false; + options.SaveToken = true; + + options.TokenValidationParameters.ValidateActor = false; + + options.TokenValidationParameters.ValidAudience = configuration.GetValue(Appsettings.JwtAudience) ?? throw new Exception($"{Appsettings.JwtAudience} value not set (appsettings)"); + options.TokenValidationParameters.ValidateAudience = true; + + options.TokenValidationParameters.ValidIssuer = configuration.GetValue(Appsettings.JwtIssuer) ?? throw new Exception($"{Appsettings.JwtIssuer} value not set (appsettings)"); + options.TokenValidationParameters.ValidateIssuer = true; + + options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey( + Encoding.UTF8.GetBytes(configuration.GetValue(Appsettings.JwtKey) ?? throw new Exception($"{Appsettings.JwtKey} value not set (appsettings)")) + ); + + options.TokenValidationParameters.ValidateIssuerSigningKey = true; + options.TokenValidationParameters.ValidateLifetime = true; + }); + + return services; + } + + public static IServiceCollection AddProxyServices(this IServiceCollection services, IConfiguration configuration) + { + // add before routing + services.Configure(options => + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + }); + + return services; + } + + public static IServiceCollection AddRoutingServices(this IServiceCollection services, IConfiguration configuration) + { + // add after proxy + services.AddRouting(options => + { + options.LowercaseUrls = true; + }); + + return services; + } + + private static IServiceCollection AddIdentityServices2(this IServiceCollection services, IConfiguration configuration) + { + var identityOptions = new MongoDbIdentityConfiguration + { + MongoDbSettings = new MongoDbSettings + { + ConnectionString = configuration.GetSection("ConnectionStrings:Mongo").Value, + DatabaseName = "insight" + }, + IdentityOptionsAction = options => + { + options.User.RequireUniqueEmail = true; + options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@.-_"; + + options.Password.RequireDigit = false; + options.Password.RequiredLength = 8; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + options.Password.RequireLowercase = false; + + options.SignIn.RequireConfirmedAccount = false; + options.SignIn.RequireConfirmedEmail = false; + options.SignIn.RequireConfirmedPhoneNumber = false; + + options.Lockout.MaxFailedAccessAttempts = 5; + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); + } + }; + + services.ConfigureMongoDbIdentity(identityOptions) + .AddDefaultTokenProviders() + .AddSignInManager(); + + return services; + } + + private static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, IConfiguration configuration) + { + services.AddAuthentication(options => + { + //options.DefaultAuthenticateScheme = + }); + //cookieBuilder.ApplicationCookie = builder.AddApplicationCookie(); + //cookieBuilder.ExternalCookie = builder.AddExternalCookie(); + //cookieBuilder.TwoFactorRememberMeCookie = builder.AddTwoFactorRememberMeCookie(); + //cookieBuilder.TwoFactorUserIdCookie = builder.AddTwoFactorUserIdCookie(); + //.AddCookie(options => + //{ + // options. + //}; + //.AddIdentityCookies(); + //.AddCookie(options => + //{ + // // Specify where to redirect un-authenticated users + // options.LoginPath = "/account/login"; + + // // Specify the name of the auth cookie. + // // ASP.NET picks a dumb name by default. + // options.Cookie.Name = "insight"; + //}); + + return services; } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj b/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj index 363fd02..0b326b6 100644 --- a/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj +++ b/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj @@ -4,12 +4,9 @@ net7.0 Insight.Infrastructure Insight - 2025.2.24.0 - 2025.2.24.0 + 2023.12.14.0 true enable - none - true @@ -21,10 +18,11 @@ - - + + - + + diff --git a/src/Core/Insight.Infrastructure/Models/Pagination.cs b/src/Core/Insight.Infrastructure/Models/Pagination.cs index 1ddc63a..ea27d93 100644 --- a/src/Core/Insight.Infrastructure/Models/Pagination.cs +++ b/src/Core/Insight.Infrastructure/Models/Pagination.cs @@ -1,49 +1,48 @@ using System.Text.Json.Serialization; -namespace Insight.Infrastructure.Models +namespace Insight.Infrastructure.Models; + +public class PagedList { - public class PagedList - { - public PagedMetaData Meta { get; } = new(); - public IEnumerable Data { get; } + public PagedMetaData Meta { get; } = new(); + public IEnumerable Data { get; } - public PagedList(IEnumerable data, int offset, int limit, long total) + public PagedList(IEnumerable data, int offset, int limit, long total) + { + Data = data; + Meta = new() { - Data = data; - Meta = new() - { - Offset = offset, - Limit = limit, - Count = data?.Count() ?? 0, - Total = total, - }; - } + Offset = offset, + Limit = limit, + Count = data?.Count() ?? 0, + Total = total, + }; } +} - public class PagedDataRequest - { - [JsonPropertyName("offset")] - public int Offset { get; set; } = 0; +public class PagedDataRequest +{ + [JsonPropertyName("offset")] + public int Offset { get; set; } = 0; - [JsonPropertyName("limit")] - public int Limit { get; set; } = 10; - } + [JsonPropertyName("limit")] + public int Limit { get; set; } = 10; +} - public class PagedHeaderData : PagedDataRequest - { - [JsonPropertyName("count")] - public int Count { get; set; } = 0; +public class PagedHeaderData : PagedDataRequest +{ + [JsonPropertyName("count")] + public int Count { get; set; } = 0; - [JsonPropertyName("total")] - public long Total { get; set; } = 0; - } + [JsonPropertyName("total")] + public long Total { get; set; } = 0; +} - public class PagedMetaData : PagedHeaderData - { - [JsonPropertyName("next")] - public string? Next { get; set; } +public class PagedMetaData : PagedHeaderData +{ + [JsonPropertyName("next")] + public string? Next { get; set; } - [JsonPropertyName("previous")] - public string? Previous { get; set; } - } + [JsonPropertyName("previous")] + public string? Previous { get; set; } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Models/TokenOptions.cs b/src/Core/Insight.Infrastructure/Models/TokenOptions.cs index d993465..fb4f6c7 100644 --- a/src/Core/Insight.Infrastructure/Models/TokenOptions.cs +++ b/src/Core/Insight.Infrastructure/Models/TokenOptions.cs @@ -1,18 +1,17 @@ -namespace Insight.Infrastructure.Models -{ - public class TokenOptions - { - public string Key { get; set; } - public int Expires { get; set; } - public Uri? Audience { get; set; } - public Uri? Issuer { get; set; } +namespace Insight.Infrastructure.Models; - public TokenOptions(string key, int expires, Uri? audience = null, Uri? issuer = null) - { - Key = key; - Expires = expires; - Audience = audience; - Issuer = issuer; - } +public class TokenOptions +{ + public string Key { get; set; } + public int Expires { get; set; } + public Uri? Audience { get; set; } + public Uri? Issuer { get; set; } + + public TokenOptions(string key, int expires, Uri? audience = null, Uri? issuer = null) + { + Key = key; + Expires = expires; + Audience = audience; + Issuer = issuer; } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/AccountService.cs b/src/Core/Insight.Infrastructure/Services/AccountService.cs index ece4505..e70a503 100644 --- a/src/Core/Insight.Infrastructure/Services/AccountService.cs +++ b/src/Core/Insight.Infrastructure/Services/AccountService.cs @@ -4,33 +4,32 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class AccountService { - public class AccountService + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public AccountService(IMongoDatabase database, ILogger logger) { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public AccountService(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public Task> GetAsync( - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.User().GetPagedAsync(filter, sort, offset, limit, cancellationToken); - - public Task> GetAsync( - HttpRequest request, - HttpResponse response, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.User().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); + _database = database; + _logger = logger; } + + public Task> GetAsync( + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.User().GetPagedAsync(filter, sort, offset, limit, cancellationToken); + + public Task> GetAsync( + HttpRequest request, + HttpResponse response, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.User().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/AgentService.cs b/src/Core/Insight.Infrastructure/Services/AgentService.cs index 580336e..f5704e3 100644 --- a/src/Core/Insight.Infrastructure/Services/AgentService.cs +++ b/src/Core/Insight.Infrastructure/Services/AgentService.cs @@ -4,33 +4,32 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class AgentService { - public class AgentService + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public AgentService(IMongoDatabase database, ILogger logger) { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public AgentService(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public Task> GetAsync( - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.Agent().GetPagedAsync(filter, sort, offset, limit, cancellationToken); - - public Task> GetAsync( - HttpRequest request, - HttpResponse response, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.Agent().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); + _database = database; + _logger = logger; } + + public Task> GetAsync( + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.Agent().GetPagedAsync(filter, sort, offset, limit, cancellationToken); + + public Task> GetAsync( + HttpRequest request, + HttpResponse response, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.Agent().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs b/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs index 65f29df..cd08c32 100644 --- a/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs +++ b/src/Core/Insight.Infrastructure/Services/AuthenticatorService.cs @@ -5,108 +5,107 @@ using System.Globalization; using System.Text; using System.Text.Encodings.Web; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class AuthenticatorService { - public class AuthenticatorService + private readonly IMongoDatabase _database; + private readonly UserManager _userManager; + + public AuthenticatorService(IMongoDatabase database, UserManager userManager) { - private readonly IMongoDatabase _database; - private readonly UserManager _userManager; + _database = database; + _userManager = userManager; + } - public AuthenticatorService(IMongoDatabase database, UserManager userManager) + public async Task GetStatusAsync(InsightUser user) + { + return await _userManager.GetTwoFactorEnabledAsync(user).ConfigureAwait(false); + } + + public async Task GetKeyAsync(InsightUser user) + { + return await _userManager.GetAuthenticatorKeyAsync(user).ConfigureAwait(false); + } + + public async Task ResetKeyAsync(InsightUser user) + { + var result = await _userManager.ResetAuthenticatorKeyAsync(user).ConfigureAwait(false); + return result.Succeeded; + } + + public async Task VerifyAsync(InsightUser user, string code) + { + code = code.Replace(" ", string.Empty).Replace("-", string.Empty); + return await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, code).ConfigureAwait(false); + } + + public async Task EnableAsync(InsightUser user) + { + var result = await _userManager.SetTwoFactorEnabledAsync(user, true).ConfigureAwait(false); + return result.Succeeded; + } + + public async Task DisableAsync(InsightUser user) + { + var result = await _userManager.SetTwoFactorEnabledAsync(user, false).ConfigureAwait(false); + return result.Succeeded; + } + + public async Task DeleteAsync(InsightUser user) + { + var result = await _userManager.SetTwoFactorEnabledAsync(user, false).ConfigureAwait(false); + if (result.Succeeded is false) return false; + + result = await _userManager.RemoveAuthenticationTokenAsync(user, "[AspNetUserStore]", "AuthenticatorKey").ConfigureAwait(false); + if (result.Succeeded is false) return false; + + return true; + } + + public async Task CountRecoveryCodesAsync(InsightUser user) + { + return await _userManager.CountRecoveryCodesAsync(user).ConfigureAwait(false); + } + + public async Task UseRecoveryCodeAsync(InsightUser user, string recoveryCode) + { + var result = await _userManager.RedeemTwoFactorRecoveryCodeAsync(user, recoveryCode).ConfigureAwait(false); + return result.Succeeded; + } + + public async Task?> ResetRecoveryCodesAsync(InsightUser user, int count = 3) + { + return await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, count).ConfigureAwait(false); + } + + public string GenerateQrCode(string email, string unformattedKey) + { + var encoder = UrlEncoder.Default; + + return string.Format(CultureInfo.InvariantCulture, + @"otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6", + encoder.Encode("Insight"), + encoder.Encode(email), + unformattedKey); + } + + public static string HumanizeKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + + while (currentPosition + 4 < unformattedKey.Length) { - _database = database; - _userManager = userManager; + result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' '); + currentPosition += 4; } - public async Task GetStatusAsync(InsightUser user) + if (currentPosition < unformattedKey.Length) { - return await _userManager.GetTwoFactorEnabledAsync(user).ConfigureAwait(false); + result.Append(unformattedKey.AsSpan(currentPosition)); } - public async Task GetKeyAsync(InsightUser user) - { - return await _userManager.GetAuthenticatorKeyAsync(user).ConfigureAwait(false); - } - - public async Task ResetKeyAsync(InsightUser user) - { - var result = await _userManager.ResetAuthenticatorKeyAsync(user).ConfigureAwait(false); - return result.Succeeded; - } - - public async Task VerifyAsync(InsightUser user, string code) - { - code = code.Replace(" ", string.Empty).Replace("-", string.Empty); - return await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, code).ConfigureAwait(false); - } - - public async Task EnableAsync(InsightUser user) - { - var result = await _userManager.SetTwoFactorEnabledAsync(user, true).ConfigureAwait(false); - return result.Succeeded; - } - - public async Task DisableAsync(InsightUser user) - { - var result = await _userManager.SetTwoFactorEnabledAsync(user, false).ConfigureAwait(false); - return result.Succeeded; - } - - public async Task DeleteAsync(InsightUser user) - { - var result = await _userManager.SetTwoFactorEnabledAsync(user, false).ConfigureAwait(false); - if (result.Succeeded is false) return false; - - result = await _userManager.RemoveAuthenticationTokenAsync(user, "[AspNetUserStore]", "AuthenticatorKey").ConfigureAwait(false); - if (result.Succeeded is false) return false; - - return true; - } - - public async Task CountRecoveryCodesAsync(InsightUser user) - { - return await _userManager.CountRecoveryCodesAsync(user).ConfigureAwait(false); - } - - public async Task UseRecoveryCodeAsync(InsightUser user, string recoveryCode) - { - var result = await _userManager.RedeemTwoFactorRecoveryCodeAsync(user, recoveryCode).ConfigureAwait(false); - return result.Succeeded; - } - - public async Task?> ResetRecoveryCodesAsync(InsightUser user, int count = 3) - { - return await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, count).ConfigureAwait(false); - } - - public string GenerateQrCode(string email, string unformattedKey) - { - var encoder = UrlEncoder.Default; - - return string.Format(CultureInfo.InvariantCulture, - @"otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6", - encoder.Encode("Insight"), - encoder.Encode(email), - unformattedKey); - } - - public static string HumanizeKey(string unformattedKey) - { - var result = new StringBuilder(); - int currentPosition = 0; - - while (currentPosition + 4 < unformattedKey.Length) - { - result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' '); - currentPosition += 4; - } - - if (currentPosition < unformattedKey.Length) - { - result.Append(unformattedKey.AsSpan(currentPosition)); - } - - return result.ToString().ToLowerInvariant(); - } + return result.ToString().ToLowerInvariant(); } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/CustomerService.cs b/src/Core/Insight.Infrastructure/Services/CustomerService.cs index c88c35b..fd22db3 100644 --- a/src/Core/Insight.Infrastructure/Services/CustomerService.cs +++ b/src/Core/Insight.Infrastructure/Services/CustomerService.cs @@ -4,33 +4,32 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class CustomerService { - public class CustomerService + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public CustomerService(IMongoDatabase database, ILogger logger) { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public CustomerService(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public Task> GetAsync( - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.Customer().GetPagedAsync(filter, sort, offset, limit, cancellationToken); - - public Task> GetAsync( - HttpRequest request, - HttpResponse response, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.Customer().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); + _database = database; + _logger = logger; } + + public Task> GetAsync( + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.Customer().GetPagedAsync(filter, sort, offset, limit, cancellationToken); + + public Task> GetAsync( + HttpRequest request, + HttpResponse response, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.Customer().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/HostService.cs b/src/Core/Insight.Infrastructure/Services/HostService.cs index d39cfc9..756e21e 100644 --- a/src/Core/Insight.Infrastructure/Services/HostService.cs +++ b/src/Core/Insight.Infrastructure/Services/HostService.cs @@ -4,33 +4,32 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class HostService { - public class HostService + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public HostService(IMongoDatabase database, ILogger logger) { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public HostService(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public Task> GetAsync( - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.Host().GetPagedAsync(filter, sort, offset, limit, cancellationToken); - - public Task> GetAsync( - HttpRequest request, - HttpResponse response, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.Host().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); + _database = database; + _logger = logger; } + + public Task> GetAsync( + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.Host().GetPagedAsync(filter, sort, offset, limit, cancellationToken); + + public Task> GetAsync( + HttpRequest request, + HttpResponse response, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.Host().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/IdentityService.cs b/src/Core/Insight.Infrastructure/Services/IdentityService.cs index 03c7ca8..cc266b5 100644 --- a/src/Core/Insight.Infrastructure/Services/IdentityService.cs +++ b/src/Core/Insight.Infrastructure/Services/IdentityService.cs @@ -3,136 +3,135 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using System.Security.Claims; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class IdentityService { - public class IdentityService + private readonly UserManager _userManager; + private readonly RoleManager _roleManager; + private readonly ILogger _logger; + + public IdentityService(UserManager userManager, RoleManager roleManager, ILogger logger) { - private readonly UserManager _userManager; - private readonly RoleManager _roleManager; - private readonly ILogger _logger; + _userManager = userManager; + _roleManager = roleManager; + _logger = logger; + } - public IdentityService(UserManager userManager, RoleManager roleManager, ILogger logger) + public async Task SeedAsync() + { + // SEED ROLES + if (await _roleManager.FindByNameAsync("system") is not InsightRole systemRole) { - _userManager = userManager; - _roleManager = roleManager; - _logger = logger; + var result = await CreateRoleAsync("system"); + if (result.Succeeded is false) throw new InvalidProgramException("seeding: system role failed"); + + systemRole = await _roleManager.FindByNameAsync("system") ?? throw new InvalidProgramException("seeding: system role failed"); } - public async Task SeedAsync() + if (await _roleManager.FindByNameAsync("administrator") is not InsightRole administratorRole) { - // SEED ROLES - if (await _roleManager.FindByNameAsync("system") is not InsightRole systemRole) - { - var result = await CreateRoleAsync("system"); - if (result.Succeeded is false) throw new InvalidProgramException("seeding: system role failed"); + var result = await CreateRoleAsync("administrator"); + if (result.Succeeded is false) throw new InvalidProgramException("seeding: administrator role failed"); - systemRole = await _roleManager.FindByNameAsync("system") ?? throw new InvalidProgramException("seeding: system role failed"); - } - - if (await _roleManager.FindByNameAsync("administrator") is not InsightRole administratorRole) - { - var result = await CreateRoleAsync("administrator"); - if (result.Succeeded is false) throw new InvalidProgramException("seeding: administrator role failed"); - - administratorRole = await _roleManager.FindByNameAsync("administrator") ?? throw new InvalidProgramException("seeding: administrator role failed"); - } - - if (await _roleManager.FindByNameAsync("chat") is not InsightRole chatRole) - { - var result = await CreateRoleAsync("chat"); - if (result.Succeeded is false) throw new InvalidProgramException("seeding: chat role failed"); - - chatRole = await _roleManager.FindByNameAsync("chat") ?? throw new InvalidProgramException("seeding: chat role failed"); - } - - // SEED USERS - if (await _userManager.FindByEmailAsync("system@insight.local") is not InsightUser systemUser) - { - var result = await CreateUserAsync("system@insight.local", "Replica3-Unroasted-Respect"); - if (result.Succeeded is false) throw new InvalidProgramException("seeding: system user failed"); - - systemUser = await _userManager.FindByEmailAsync("system@insight.local") ?? throw new InvalidProgramException("seeding: system user failed"); - } - - if (systemUser.Roles.Any(p => p == systemRole.Id) is false) - { - var assign = await _userManager.AddToRoleAsync(systemUser, systemRole.Name); - if (assign.Succeeded is false) throw new InvalidProgramException("seeding: system user roles failed"); - } + administratorRole = await _roleManager.FindByNameAsync("administrator") ?? throw new InvalidProgramException("seeding: administrator role failed"); } - public async Task CreateUserAsync(string email, string password) + if (await _roleManager.FindByNameAsync("chat") is not InsightRole chatRole) { - var user = new InsightUser - { - UserName = email, - NormalizedUserName = email.ToUpperInvariant(), - Email = email, - NormalizedEmail = email.ToUpperInvariant(), - }; + var result = await CreateRoleAsync("chat"); + if (result.Succeeded is false) throw new InvalidProgramException("seeding: chat role failed"); - return await _userManager.CreateAsync(user, password); + chatRole = await _roleManager.FindByNameAsync("chat") ?? throw new InvalidProgramException("seeding: chat role failed"); } - public async Task CreateRoleAsync(string name) + // SEED USERS + if (await _userManager.FindByEmailAsync("system@insight.local") is not InsightUser systemUser) { - var role = new InsightRole - { - Name = name, - NormalizedName = name.ToUpperInvariant() - }; + var result = await CreateUserAsync("system@insight.local", "Replica3-Unroasted-Respect"); + if (result.Succeeded is false) throw new InvalidProgramException("seeding: system user failed"); - return await _roleManager.CreateAsync(role); + systemUser = await _userManager.FindByEmailAsync("system@insight.local") ?? throw new InvalidProgramException("seeding: system user failed"); } - public async Task LoginAsync(string email, string password, string? code = null) + if (systemUser.Roles.Any(p => p == systemRole.Id) is false) { - if (await _userManager.FindByEmailAsync(email) is not InsightUser user) throw new InvalidDataException("Invalid Credentials"); - if (await _userManager.CheckPasswordAsync(user, password) is false) throw new InvalidDataException("Invalid Credentials"); - - if (await _userManager.GetTwoFactorEnabledAsync(user)) - { - if (string.IsNullOrWhiteSpace(code)) throw new InvalidOperationException("Requires 2FA Code"); - - var authCode = code.Replace(" ", string.Empty).Replace("-", string.Empty); - - if (await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, authCode) is false) - { - throw new InvalidDataException("Invalid 2FA Code"); - } - } - - return user; - } - - public async Task ChangePasswordAsync(InsightUser user, string current, string @new) - { - var result = await _userManager.ChangePasswordAsync(user, current, @new).ConfigureAwait(false); - return result.Succeeded; - } - - public async Task GetByEmailAsync(string key) - { - var result = await _userManager.FindByEmailAsync(key).ConfigureAwait(false); - if (result is not null) return result; - - return null; - } - - public async Task> GetClaimsAsync(InsightUser user, bool includeRoles = true) - { - var claims = await _userManager.GetClaimsAsync(user).ConfigureAwait(false); - - if (includeRoles) - { - var roles = await _userManager.GetRolesAsync(user).ConfigureAwait(false); - foreach (var role in roles) claims.Add(new Claim(ClaimTypes.Role, role)); - } - - claims.Add(new Claim(ClaimTypes.Email, user.Email)); - claims.Add(new Claim("user", user.UserName)); - - return claims; + var assign = await _userManager.AddToRoleAsync(systemUser, systemRole.Name); + if (assign.Succeeded is false) throw new InvalidProgramException("seeding: system user roles failed"); } } + + public async Task CreateUserAsync(string email, string password) + { + var user = new InsightUser + { + UserName = email, + NormalizedUserName = email.ToUpperInvariant(), + Email = email, + NormalizedEmail = email.ToUpperInvariant(), + }; + + return await _userManager.CreateAsync(user, password); + } + + public async Task CreateRoleAsync(string name) + { + var role = new InsightRole + { + Name = name, + NormalizedName = name.ToUpperInvariant() + }; + + return await _roleManager.CreateAsync(role); + } + + public async Task LoginAsync(string email, string password, string? code = null) + { + if (await _userManager.FindByEmailAsync(email) is not InsightUser user) throw new InvalidDataException("Invalid Credentials"); + if (await _userManager.CheckPasswordAsync(user, password) is false) throw new InvalidDataException("Invalid Credentials"); + + if (await _userManager.GetTwoFactorEnabledAsync(user)) + { + if (string.IsNullOrWhiteSpace(code)) throw new InvalidOperationException("Requires 2FA Code"); + + var authCode = code.Replace(" ", string.Empty).Replace("-", string.Empty); + + if (await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, authCode) is false) + { + throw new InvalidDataException("Invalid 2FA Code"); + } + } + + return user; + } + + public async Task ChangePasswordAsync(InsightUser user, string current, string @new) + { + var result = await _userManager.ChangePasswordAsync(user, current, @new).ConfigureAwait(false); + return result.Succeeded; + } + + public async Task GetByEmailAsync(string key) + { + var result = await _userManager.FindByEmailAsync(key).ConfigureAwait(false); + if (result is not null) return result; + + return null; + } + + public async Task> GetClaimsAsync(InsightUser user, bool includeRoles = true) + { + var claims = await _userManager.GetClaimsAsync(user).ConfigureAwait(false); + + if (includeRoles) + { + var roles = await _userManager.GetRolesAsync(user).ConfigureAwait(false); + foreach (var role in roles) claims.Add(new Claim(ClaimTypes.Role, role)); + } + + claims.Add(new Claim(ClaimTypes.Email, user.Email)); + claims.Add(new Claim("user", user.UserName)); + + return claims; + } } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/InventoryService.cs b/src/Core/Insight.Infrastructure/Services/InventoryService.cs index c5e1608..fd0235d 100644 --- a/src/Core/Insight.Infrastructure/Services/InventoryService.cs +++ b/src/Core/Insight.Infrastructure/Services/InventoryService.cs @@ -4,33 +4,32 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class InventoryService { - public class InventoryService + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public InventoryService(IMongoDatabase database, ILogger logger) { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public InventoryService(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public Task> GetAsync( - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.HostApplication().GetPagedAsync(filter, sort, offset, limit, cancellationToken); - - public Task> GetAsync( - HttpRequest request, - HttpResponse response, - FilterDefinition? filter = null, - SortDefinition? sort = null, - int offset = 0, - int limit = 10, - CancellationToken cancellationToken = default) => _database.HostApplication().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); + _database = database; + _logger = logger; } + + public Task> GetAsync( + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.HostApplication().GetPagedAsync(filter, sort, offset, limit, cancellationToken); + + public Task> GetAsync( + HttpRequest request, + HttpResponse response, + FilterDefinition? filter = null, + SortDefinition? sort = null, + int offset = 0, + int limit = 10, + CancellationToken cancellationToken = default) => _database.HostApplication().GetPagedAsync(request, response, filter, sort, offset, limit, cancellationToken); } \ No newline at end of file diff --git a/src/Core/Insight.Infrastructure/Services/TokenService.cs b/src/Core/Insight.Infrastructure/Services/TokenService.cs index 11c0ff3..f9cf6e7 100644 --- a/src/Core/Insight.Infrastructure/Services/TokenService.cs +++ b/src/Core/Insight.Infrastructure/Services/TokenService.cs @@ -8,142 +8,141 @@ using System.Net; using System.Security.Cryptography; using System.Text; -namespace Insight.Infrastructure.Services +namespace Insight.Infrastructure.Services; + +public class TokenService { - public class TokenService + private readonly TokenOptions _options; + private readonly IdentityService _identityService; + private readonly IMongoDatabase _database; + + public TokenService(TokenOptions options, IdentityService identityService, IMongoDatabase database) { - private readonly TokenOptions _options; - private readonly IdentityService _identityService; - private readonly IMongoDatabase _database; + _options = options; + _identityService = identityService; + _database = database; + } - public TokenService(TokenOptions options, IdentityService identityService, IMongoDatabase database) + public async Task GetAsync(string email, string password, string? code = null, IPAddress? ipa = null) + { + var user = await _identityService.LoginAsync(email, password, code).ConfigureAwait(false); + + var accessToken = await CreateAccessTokenAsync(user, ipa).ConfigureAwait(false); + var refreshToken = await CreateRefreshTokenAsync(user, ipa).ConfigureAwait(false); + + return new TokenResponse { - _options = options; - _identityService = identityService; - _database = database; + AccessToken = accessToken.Item1, + ExpireInSeconds = accessToken.Item2, + RefreshToken = refreshToken.Item1 + }; + } + + public async Task RefreshAsync(string refreshToken, IPAddress? ipa = null) + { + if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException(nameof(refreshToken)); + + var user = await _database.User().Find(p => p.RefreshTokens.Any(t => t.Token == refreshToken)).FirstOrDefaultAsync(); + if (user is null || user.RefreshTokens is null) throw new InvalidDataException("Invalid Refresh Token"); + + var token = user.RefreshTokens.First(p => p.Token == refreshToken); + + if (token.IsRevoked) + { + // todo: revoke all descendant tokens in case this token has been compromised + throw new InvalidDataException("Invalid Refresh Token"); } - public async Task GetAsync(string email, string password, string? code = null, IPAddress? ipa = null) + if (token.IsActive is false) { - var user = await _identityService.LoginAsync(email, password, code).ConfigureAwait(false); + throw new InvalidDataException("Invalid Refresh Token"); + } - var accessToken = await CreateAccessTokenAsync(user, ipa).ConfigureAwait(false); - var refreshToken = await CreateRefreshTokenAsync(user, ipa).ConfigureAwait(false); + // remove actual refresh token + user.RefreshTokens.Remove(token); - return new TokenResponse + // remove old refresh tokens from user + user.RefreshTokens.RemoveAll(p => p.IsExpired && p.IsRevoked is false); + + // update users refreshTokens + await _database.User().UpdateOneAsync(Builders + .Filter.Eq(p => p.UserName, user.UserName), Builders + .Update.Set(p => p.RefreshTokens, user.RefreshTokens)); + + // create new refresh token + var newRefreshToken = await CreateRefreshTokenAsync(user, ipa).ConfigureAwait(false); + + // create access token + var accessToken = await CreateAccessTokenAsync(user, ipa).ConfigureAwait(false); + + return new TokenResponse + { + AccessToken = accessToken.Item1, + ExpireInSeconds = accessToken.Item2, + RefreshToken = newRefreshToken.Item1, + }; + } + + public async Task RevokeAsync(string refreshToken, string reason, IPAddress? ipa = null) + { + if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException(nameof(refreshToken)); + + var user = await _database.User().Find(p => p.RefreshTokens.Any(t => t.Token == refreshToken)).FirstOrDefaultAsync(); + if (user is null || user.RefreshTokens is null) throw new InvalidDataException("Invalid Refresh Token"); + + var token = user.RefreshTokens.First(p => p.Token == refreshToken); + + if (token.IsActive is false) + { + throw new InvalidDataException("Invalid Refresh Token"); + } + + token.Revoked = DateTime.Now; + token.RevokedByIp = ipa?.ToString(); + token.ReasonRevoked = reason; + } + + private async Task<(string, int)> CreateAccessTokenAsync(InsightUser user, IPAddress? ipa = null) + { + var claims = await _identityService.GetClaimsAsync(user).ConfigureAwait(false); + + var key = Encoding.UTF8.GetBytes(_options.Key); + var secret = new SymmetricSecurityKey(key); + var signing = new SigningCredentials(secret, SecurityAlgorithms.HmacSha256); + + var securityToken = new JwtSecurityToken( + _options.Issuer?.ToString(), + _options.Audience?.ToString(), + claims, + DateTime.Now, + DateTime.Now.AddSeconds(Convert.ToDouble(_options.Expires)), + signing); + + var token = new JwtSecurityTokenHandler().WriteToken(securityToken); + + return (token, (int)TimeSpan.FromMinutes(Convert.ToDouble(_options.Expires)).TotalSeconds); + } + + private async Task<(string, int)> CreateRefreshTokenAsync(InsightUser user, IPAddress? ipa = null) + { + var randomNumber = new byte[32]; + + using var rng = RandomNumberGenerator.Create(); + rng.GetBytes(randomNumber); + + var refreshToken = Convert.ToBase64String(randomNumber); + + await _database.User() + .UpdateOneAsync(Builders + .Filter.Eq(p => p.UserName, user.UserName), Builders + .Update.AddToSet(p => p.RefreshTokens, new RefreshToken { - AccessToken = accessToken.Item1, - ExpireInSeconds = accessToken.Item2, - RefreshToken = refreshToken.Item1 - }; - } + Token = refreshToken, + Created = DateTime.Now, + Expires = DateTime.Now.AddMinutes(30), // change offset to config based + CreatedByIp = ipa?.ToString() + })); - public async Task RefreshAsync(string refreshToken, IPAddress? ipa = null) - { - if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException(nameof(refreshToken)); - - var user = await _database.User().Find(p => p.RefreshTokens.Any(t => t.Token == refreshToken)).FirstOrDefaultAsync(); - if (user is null || user.RefreshTokens is null) throw new InvalidDataException("Invalid Refresh Token"); - - var token = user.RefreshTokens.First(p => p.Token == refreshToken); - - if (token.IsRevoked) - { - // todo: revoke all descendant tokens in case this token has been compromised - throw new InvalidDataException("Invalid Refresh Token"); - } - - if (token.IsActive is false) - { - throw new InvalidDataException("Invalid Refresh Token"); - } - - // remove actual refresh token - user.RefreshTokens.Remove(token); - - // remove old refresh tokens from user - user.RefreshTokens.RemoveAll(p => p.IsExpired && p.IsRevoked is false); - - // update users refreshTokens - await _database.User().UpdateOneAsync(Builders - .Filter.Eq(p => p.UserName, user.UserName), Builders - .Update.Set(p => p.RefreshTokens, user.RefreshTokens)); - - // create new refresh token - var newRefreshToken = await CreateRefreshTokenAsync(user, ipa).ConfigureAwait(false); - - // create access token - var accessToken = await CreateAccessTokenAsync(user, ipa).ConfigureAwait(false); - - return new TokenResponse - { - AccessToken = accessToken.Item1, - ExpireInSeconds = accessToken.Item2, - RefreshToken = newRefreshToken.Item1, - }; - } - - public async Task RevokeAsync(string refreshToken, string reason, IPAddress? ipa = null) - { - if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException(nameof(refreshToken)); - - var user = await _database.User().Find(p => p.RefreshTokens.Any(t => t.Token == refreshToken)).FirstOrDefaultAsync(); - if (user is null || user.RefreshTokens is null) throw new InvalidDataException("Invalid Refresh Token"); - - var token = user.RefreshTokens.First(p => p.Token == refreshToken); - - if (token.IsActive is false) - { - throw new InvalidDataException("Invalid Refresh Token"); - } - - token.Revoked = DateTime.Now; - token.RevokedByIp = ipa?.ToString(); - token.ReasonRevoked = reason; - } - - private async Task<(string, int)> CreateAccessTokenAsync(InsightUser user, IPAddress? ipa = null) - { - var claims = await _identityService.GetClaimsAsync(user).ConfigureAwait(false); - - var key = Encoding.UTF8.GetBytes(_options.Key); - var secret = new SymmetricSecurityKey(key); - var signing = new SigningCredentials(secret, SecurityAlgorithms.HmacSha256); - - var securityToken = new JwtSecurityToken( - _options.Issuer?.ToString(), - _options.Audience?.ToString(), - claims, - DateTime.Now, - DateTime.Now.AddSeconds(Convert.ToDouble(_options.Expires)), - signing); - - var token = new JwtSecurityTokenHandler().WriteToken(securityToken); - - return (token, (int)TimeSpan.FromMinutes(Convert.ToDouble(_options.Expires)).TotalSeconds); - } - - private async Task<(string, int)> CreateRefreshTokenAsync(InsightUser user, IPAddress? ipa = null) - { - var randomNumber = new byte[32]; - - using var rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomNumber); - - var refreshToken = Convert.ToBase64String(randomNumber); - - await _database.User() - .UpdateOneAsync(Builders - .Filter.Eq(p => p.UserName, user.UserName), Builders - .Update.AddToSet(p => p.RefreshTokens, new RefreshToken - { - Token = refreshToken, - Created = DateTime.Now, - Expires = DateTime.Now.AddMinutes(30), // change offset to config based - CreatedByIp = ipa?.ToString() - })); - - return (refreshToken, (int)TimeSpan.FromMinutes(30).TotalSeconds); - } + return (refreshToken, (int)TimeSpan.FromMinutes(30).TotalSeconds); } } \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IAudioProvider.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IAudioProvider.cs new file mode 100644 index 0000000..961bc17 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IAudioProvider.cs @@ -0,0 +1,6 @@ +namespace Insight.Remote.Shared.Abstractions; + +public interface IAudioProvider +{ + Task InitAsync(CancellationToken cancellationToken); +} diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IClipboardProvider.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IClipboardProvider.cs new file mode 100644 index 0000000..a12f525 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IClipboardProvider.cs @@ -0,0 +1,7 @@ +namespace Insight.Remote.Shared.Abstractions; + +public interface IClipboardProvider +{ + Task InitAsync(CancellationToken cancellationToken); + Task SetText(string clipboardText); +} diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/ICursorProvider.cs b/src/Remote/Insight.Remote.Shared/Abstractions/ICursorProvider.cs new file mode 100644 index 0000000..bcb4364 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/ICursorProvider.cs @@ -0,0 +1,9 @@ +using Insight.Domain.Network.Remote.Messages; + +namespace Insight.Remote.Shared.Abstractions; + +public interface ICursorProvider +{ + Task GetAsync(CancellationToken cancellationToken); + Task SetAsync(int x, int y, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IDesktopApp.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IDesktopApp.cs new file mode 100644 index 0000000..a7aa84a --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IDesktopApp.cs @@ -0,0 +1,8 @@ +using Insight.Domain.Enums; + +namespace Insight.Remote.Shared.Abstractions; + +public interface IDesktopApp : IDisposable +{ + Task InitAsync(RemoteControlMode mode, CancellationToken cancellationToken); +} diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IDispatcher.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IDispatcher.cs new file mode 100644 index 0000000..897da5e --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IDispatcher.cs @@ -0,0 +1,10 @@ +namespace Insight.Remote.Shared.Abstractions; + +public interface IDispatcher +{ + Task RunAsync(CancellationToken cancellationToken); + Task ShutdownAsync(); + + ValueTask InvokeAsync(Action action, CancellationToken cancellationToken = default); + ValueTask InvokeAsync(Func func, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IFileProvider.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IFileProvider.cs new file mode 100644 index 0000000..c238812 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IFileProvider.cs @@ -0,0 +1,13 @@ +using Insight.Remote.Shared.Services; +using Insight.Remote.Shared.ViewModels; + +namespace Insight.Remote.Shared.Abstractions; + +public interface IFileProvider +{ + string GetBaseDirectory(); + + Task ReceiveFile(byte[] buffer, string fileName, string messageId, bool endOfFile, bool startOfFile); + //void OpenFileTransferWindow(Viewer viewer); + Task UploadFile(FileUpload file, Streamer viewer, Action progressUpdateCallback, CancellationToken cancelToken); +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IInputProvider.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IInputProvider.cs new file mode 100644 index 0000000..55f4fd8 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IInputProvider.cs @@ -0,0 +1,16 @@ +using Insight.Remote.Shared.Enums; + +namespace Insight.Remote.Shared.Abstractions; + +public interface IInputProvider +{ + Task InitAsync(CancellationToken cancellationToken); + Task SendKeyDownAsync(string key, CancellationToken cancellationToken); + Task SendKeyUpAsync(string key, CancellationToken cancellationToken); + Task SendMouseMoveAsync(double percentX, double percentY, CancellationToken cancellationToken); + Task SendMouseWheelAsync(int deltaY, CancellationToken cancellationToken); + Task SendTextAsync(string transferText, CancellationToken cancellationToken); + Task ToggleBlockInputAsync(bool toggleOn, CancellationToken cancellationToken); + Task SetKeyStatesUpAsync(CancellationToken cancellationToken); + Task SendMouseButtonActionAsync(int button, ButtonAction buttonAction, double percentX, double percentY, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Abstractions/IScreenProvider.cs b/src/Remote/Insight.Remote.Shared/Abstractions/IScreenProvider.cs new file mode 100644 index 0000000..2532b11 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Abstractions/IScreenProvider.cs @@ -0,0 +1,22 @@ +using Insight.Remote.Shared.Models; +using SkiaSharp; +using System.Drawing; + +namespace Insight.Remote.Shared.Abstractions; + +public interface IScreenProvider : IDisposable +{ + Rectangle CurrentScreenBounds { get; } + string SelectedScreen { get; } + + Task InitAsync(CancellationToken cancellationToken); + + IEnumerable GetDisplayNames(); + Task GetDiffAreaAsync(bool fullscreen, CancellationToken cancellationToken); + Task GetNextFrameAsync(CancellationToken cancellationToken); + + void SetSelectedScreen(string displayName); + int GetScreenCount(); + int GetSelectedScreenIndex(); + Rectangle GetVirtualScreenBounds(); +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Enums/ButtonAction.cs b/src/Remote/Insight.Remote.Shared/Enums/ButtonAction.cs new file mode 100644 index 0000000..71d703d --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Enums/ButtonAction.cs @@ -0,0 +1,7 @@ +namespace Insight.Remote.Shared.Enums; + +public enum ButtonAction +{ + Down, + Up +} diff --git a/src/Remote/Insight.Remote.Shared/Extensions/ImageExtensions.cs b/src/Remote/Insight.Remote.Shared/Extensions/ImageExtensions.cs new file mode 100644 index 0000000..71794eb --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Extensions/ImageExtensions.cs @@ -0,0 +1,181 @@ +using Microsoft.IO; +using RemoteControl.Shared; +using SkiaSharp; + +namespace Insight.Remote.Shared.Extensions; + +public static class ImageExtensions +{ + private static readonly RecyclableMemoryStreamManager _recycleManager = new(); + + public static Result GetImageDiff(this SKBitmap currentFrame, SKBitmap? previousFrame, bool forceFullscreen = false) + { + try + { + if (currentFrame is null) return Result.Fail("Current frame cannot be null."); + if (previousFrame is null || forceFullscreen) return Result.Ok(currentFrame.Copy()); + + if (currentFrame.Height != previousFrame.Height || + currentFrame.Width != previousFrame.Width || + currentFrame.BytesPerPixel != previousFrame.BytesPerPixel) return Result.Fail("Frames are not of equal size."); + + var width = currentFrame.Width; + var height = currentFrame.Height; + var anyChanges = false; + var diffFrame = new SKBitmap(width, height); + + var bytesPerPixel = currentFrame.BytesPerPixel; + var totalSize = currentFrame.ByteCount; + + unsafe + { + byte* scan1 = (byte*)currentFrame.GetPixels().ToPointer(); + byte* scan2 = (byte*)previousFrame.GetPixels().ToPointer(); + byte* scan3 = (byte*)diffFrame.GetPixels().ToPointer(); + + for (var row = 0; row < height; row++) + { + for (var column = 0; column < width; column++) + { + var index = row * width * bytesPerPixel + column * bytesPerPixel; + + byte* data1 = scan1 + index; + byte* data2 = scan2 + index; + byte* data3 = scan3 + index; + + if (data1[0] != data2[0] || + data1[1] != data2[1] || + data1[2] != data2[2] || + data1[3] != data2[3]) + { + anyChanges = true; + data3[0] = data2[0]; + data3[1] = data2[1]; + data3[2] = data2[2]; + data3[3] = data2[3]; + } + } + } + } + + if (anyChanges) return Result.Ok(diffFrame); + + diffFrame.Dispose(); + + return Result.Fail("No difference found."); + } + catch (Exception ex) + { + return Result.Fail(ex); + } + } + + public static SKRect GetDiffArea(this SKBitmap currentFrame, SKBitmap? previousFrame, bool forceFullscreen = false) + { + try + { + if (currentFrame is null) return SKRect.Empty; + if (previousFrame is null || forceFullscreen) return currentFrame.ToRectangle(); + + // test: fake (multiple consumers) + //return currentFrame.ToRectangle(); + + if (currentFrame.Height != previousFrame.Height || + currentFrame.Width != previousFrame.Width || + currentFrame.BytesPerPixel != previousFrame.BytesPerPixel) return SKRect.Empty; + + var width = currentFrame.Width; + var height = currentFrame.Height; + int left = int.MaxValue; + int top = int.MaxValue; + int right = int.MinValue; + int bottom = int.MinValue; + + var bytesPerPixel = currentFrame.BytesPerPixel; + var totalSize = currentFrame.ByteCount; + + unsafe + { + byte* scan1 = (byte*)currentFrame.GetPixels().ToPointer(); + byte* scan2 = (byte*)previousFrame.GetPixels().ToPointer(); + + for (var row = 0; row < height; row++) + { + for (var column = 0; column < width; column++) + { + var index = row * width * bytesPerPixel + column * bytesPerPixel; + + byte* data1 = scan1 + index; + byte* data2 = scan2 + index; + + if (data1[0] != data2[0] || + data1[1] != data2[1] || + data1[2] != data2[2]) + { + + if (row < top) + { + top = row; + } + if (row > bottom) + { + bottom = row; + } + if (column < left) + { + left = column; + } + if (column > right) + { + right = column; + } + } + + } + } + + // Check for valid bounding box. + if (left <= right && top <= bottom) + { + left = Math.Max(left - 2, 0); + top = Math.Max(top - 2, 0); + right = Math.Min(right + 2, width); + bottom = Math.Min(bottom + 2, height); + return new SKRect(left, top, right, bottom); + } + + return SKRect.Empty; + } + } + catch (Exception) + { + return SKRect.Empty; + } + } + + public static byte[] EncodeBitmap(this SKBitmap bitmap, SKEncodedImageFormat format, int quality) + { + using var ms = _recycleManager.GetStream(); + bitmap.Encode(ms, format, quality); + + return ms.ToArray(); + } + + public static SKBitmap CropBitmap(this SKBitmap bitmap, SKRect cropArea) + { + var cropped = new SKBitmap((int)cropArea.Width, (int)cropArea.Height); + + using var canvas = new SKCanvas(cropped); + canvas.DrawBitmap( + bitmap, + cropArea, + new SKRect(0, 0, cropArea.Width, cropArea.Height)); + + return cropped; + } + + public static SKRect ToRectangle(this SKBitmap bitmap) + { + return new SKRect(0, 0, bitmap.Width, bitmap.Height); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Extensions/ServiceCollectionExtensions.cs b/src/Remote/Insight.Remote.Shared/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..8243ea1 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,21 @@ +using Insight.Remote.Shared.Models; +using Insight.Remote.Shared.Services; +using Microsoft.Extensions.DependencyInjection; +using Vaitr.Bus; + +namespace Insight.Remote.Shared.Extensions; + +public static class ServiceCollectionExtensions +{ + public static void AddRemoteControlServices(this IServiceCollection services, Action options) + { + var appOptions = new AppOptions(); + options(appOptions); + + services.AddSingleton(appOptions); + + services.AddHostedService(); + services.AddSingleton(); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Helpers/Disposer.cs b/src/Remote/Insight.Remote.Shared/Helpers/Disposer.cs new file mode 100644 index 0000000..c77dfde --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Helpers/Disposer.cs @@ -0,0 +1,21 @@ +namespace Insight.Remote.Shared.Helpers; + +public static class Disposer +{ + public static void TryDisposeAll(params IDisposable[] disposables) + { + if (disposables is null) + { + return; + } + + foreach (var disposable in disposables) + { + try + { + disposable?.Dispose(); + } + catch { } + } + } +} diff --git a/src/Remote/Insight.Remote.Shared/Helpers/WaitHelper.cs b/src/Remote/Insight.Remote.Shared/Helpers/WaitHelper.cs new file mode 100644 index 0000000..e3d42e6 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Helpers/WaitHelper.cs @@ -0,0 +1,16 @@ +using System.Diagnostics; + +namespace Insight.Remote.Shared.Helpers; + +public static class WaitHelper +{ + public static async Task WaitForAsync(Func condition, TimeSpan timeout, int pollingMs = 10) + { + var sw = Stopwatch.StartNew(); + while (!condition() && sw.Elapsed < timeout) + { + await Task.Delay(pollingMs); + } + return condition(); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj b/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj new file mode 100644 index 0000000..e36fbc0 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj @@ -0,0 +1,25 @@ + + + + net7.0 + Insight.Remote.Shared + Insight + Insight.Remote.Shared + 2023.12.14.0 + enable + enable + True + True + + + + + + + + + + + + + diff --git a/src/Remote/Insight.Remote.Shared/Messages/AudioSampleReady.cs b/src/Remote/Insight.Remote.Shared/Messages/AudioSampleReady.cs new file mode 100644 index 0000000..4fbc2fe --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/AudioSampleReady.cs @@ -0,0 +1,3 @@ +namespace Insight.Remote.Shared.Messages; + +public record AudioSampleReady(byte[] Sample); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/CastRequestDemand.cs b/src/Remote/Insight.Remote.Shared/Messages/CastRequestDemand.cs new file mode 100644 index 0000000..2f7780e --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/CastRequestDemand.cs @@ -0,0 +1,5 @@ +using Insight.Domain.Network.Remote.Messages; + +namespace Insight.Remote.Shared.Messages; + +public record CastRequestDemand(CastRequest Request, bool Accepted); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/ClipboardChanged.cs b/src/Remote/Insight.Remote.Shared/Messages/ClipboardChanged.cs new file mode 100644 index 0000000..23fde27 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/ClipboardChanged.cs @@ -0,0 +1,3 @@ +namespace Insight.Remote.Shared.Messages; + +public record ClipboardChanged(string Text); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/ConnectionStateChanged.cs b/src/Remote/Insight.Remote.Shared/Messages/ConnectionStateChanged.cs new file mode 100644 index 0000000..a7c0297 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/ConnectionStateChanged.cs @@ -0,0 +1,13 @@ +namespace Insight.Remote.Shared.Messages; + +public record ConnectionStateChanged(ConnectionState State); + +public enum ConnectionState +{ + Unknown = 0, + Connecting = 1, + Reconnecting = 2, + Connected = 3, + Disconnected = 4, + Error = 5 +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/CursorChanged.cs b/src/Remote/Insight.Remote.Shared/Messages/CursorChanged.cs new file mode 100644 index 0000000..d320740 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/CursorChanged.cs @@ -0,0 +1,5 @@ +using Insight.Remote.Shared.Models; + +namespace Insight.Remote.Shared.Messages; + +public record CursorChanged(CursorInfo CursorInfo); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/IdentityChanged.cs b/src/Remote/Insight.Remote.Shared/Messages/IdentityChanged.cs new file mode 100644 index 0000000..8f57218 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/IdentityChanged.cs @@ -0,0 +1,3 @@ +namespace Insight.Remote.Shared.Messages; + +public record IdentityChanged(string Id); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/ScreenChanged.cs b/src/Remote/Insight.Remote.Shared/Messages/ScreenChanged.cs new file mode 100644 index 0000000..13deb12 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/ScreenChanged.cs @@ -0,0 +1,5 @@ +using System.Drawing; + +namespace Insight.Remote.Shared.Messages; + +public record ScreenChanged(Rectangle Screen); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/WindowsSessionEnding.cs b/src/Remote/Insight.Remote.Shared/Messages/WindowsSessionEnding.cs new file mode 100644 index 0000000..d99737d --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/WindowsSessionEnding.cs @@ -0,0 +1,5 @@ +using Insight.Domain.Enums; + +namespace Insight.Remote.Shared.Messages; + +public record WindowsSessionEnding(SessionEndReasons Reason); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Messages/WindowsSessionSwitched.cs b/src/Remote/Insight.Remote.Shared/Messages/WindowsSessionSwitched.cs new file mode 100644 index 0000000..2f99794 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Messages/WindowsSessionSwitched.cs @@ -0,0 +1,5 @@ +using Insight.Domain.Enums; + +namespace Insight.Remote.Shared.Messages; + +public record WindowsSessionSwitched(SessionSwitchReason Reason, int SessionId); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Models/AppOptions.cs b/src/Remote/Insight.Remote.Shared/Models/AppOptions.cs new file mode 100644 index 0000000..7a3d6bd --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Models/AppOptions.cs @@ -0,0 +1,6 @@ +namespace Insight.Remote.Shared.Models; + +public class AppOptions +{ + public Uri? Server { get; set; } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Models/CursorInfo.cs b/src/Remote/Insight.Remote.Shared/Models/CursorInfo.cs new file mode 100644 index 0000000..9a63e5e --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Models/CursorInfo.cs @@ -0,0 +1,17 @@ +using System.Drawing; + +namespace Insight.Remote.Shared.Models; + +public class CursorInfo +{ + public byte[] ImageBytes { get; set; } + public Point HotSpot { get; set; } + public string CssOverride { get; set; } + + public CursorInfo(byte[] imageBytes, Point hotspot, string cssOverride = "") + { + ImageBytes = imageBytes; + HotSpot = hotspot; + CssOverride = cssOverride; + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Models/FrameResult.cs b/src/Remote/Insight.Remote.Shared/Models/FrameResult.cs new file mode 100644 index 0000000..b80c35d --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Models/FrameResult.cs @@ -0,0 +1,5 @@ +using SkiaSharp; + +namespace Insight.Remote.Shared.Models; + +public record FrameResult(SKBitmap? Frame, bool Changed, bool Error = false); \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Models/SentFrame.cs b/src/Remote/Insight.Remote.Shared/Models/SentFrame.cs new file mode 100644 index 0000000..fdfe88f --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Models/SentFrame.cs @@ -0,0 +1,13 @@ +namespace Insight.Remote.Shared.Models; + +public readonly struct SentFrame +{ + public SentFrame(int frameSize, DateTimeOffset timestamp) + { + FrameSize = frameSize; + Timestamp = timestamp; + } + + public DateTimeOffset Timestamp { get; } + public int FrameSize { get; } +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Linux/LibX11.cs b/src/Remote/Insight.Remote.Shared/Native/Linux/LibX11.cs new file mode 100644 index 0000000..f7d8078 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Linux/LibX11.cs @@ -0,0 +1,140 @@ +/* + +Copyright 1985, 1986, 1987, 1991, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ + +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Linux; + +public static unsafe class LibX11 +{ + + [DllImport("libX11")] + public static extern IntPtr XGetImage(IntPtr display, IntPtr drawable, int x, int y, int width, int height, long plane_mask, int format); + + [DllImport("libX11")] + public static extern IntPtr XDefaultVisual(IntPtr display, int screen_number); + [DllImport("libX11")] + public static extern int XScreenCount(IntPtr display); + [DllImport("libX11")] + public static extern int XDefaultScreen(IntPtr display); + [DllImport("libX11")] + public static extern IntPtr XOpenDisplay(string display_name); + [DllImport("libX11")] + public static extern void XCloseDisplay(IntPtr display); + [DllImport("libX11")] + public static extern IntPtr XRootWindow(IntPtr display, int screen_number); + + [DllImport("libX11")] + public static extern IntPtr XGetSubImage(IntPtr display, IntPtr drawable, int x, int y, uint width, uint height, ulong plane_mask, int format, IntPtr dest_image, int dest_x, int dest_y); + [DllImport("libX11")] + public static extern IntPtr XScreenOfDisplay(IntPtr display, int screen_number); + [DllImport("libX11")] + public static extern int XDisplayWidth(IntPtr display, int screen_number); + [DllImport("libX11")] + public static extern int XDisplayHeight(IntPtr display, int screen_number); + [DllImport("libX11")] + public static extern int XWidthOfScreen(IntPtr screen); + [DllImport("libX11")] + public static extern int XHeightOfScreen(IntPtr screen); + [DllImport("libX11")] + public static extern IntPtr XDefaultGC(IntPtr display, int screen_number); + [DllImport("libX11")] + public static extern IntPtr XDefaultRootWindow(IntPtr display); + [DllImport("libX11")] + public static extern void XGetInputFocus(IntPtr display, out IntPtr focus_return, out int revert_to_return); + [DllImport("libX11")] + public static extern IntPtr XStringToKeysym(string key); + [DllImport("libX11")] + public static extern uint XKeysymToKeycode(IntPtr display, IntPtr keysym); + + [DllImport("libX11")] + public static extern IntPtr XRootWindowOfScreen(IntPtr screen); + [DllImport("libX11")] + public static extern ulong XNextRequest(IntPtr display); + [DllImport("libX11")] + public static extern void XForceScreenSaver(IntPtr display, int mode); + [DllImport("libX11")] + public static extern void XSync(IntPtr display, bool discard); + [DllImport("libX11")] + public static extern void XDestroyImage(IntPtr ximage); + + [DllImport("libX11")] + public static extern void XNoOp(IntPtr display); + + [DllImport("libX11")] + public static extern void XFree(IntPtr data); + + [DllImport("libX11")] + public static extern int XGetWindowAttributes(IntPtr display, IntPtr window, out XWindowAttributes windowAttributes); + + public struct XImage + { + public int width; + public int height; /* size of image */ + public int xoffset; /* number of pixels offset in X direction */ + public int format; /* XYBitmap, XYPixmap, ZPixmap */ + //public char* data; /* pointer to image data */ + public IntPtr data; /* pointer to image data */ + public int byte_order; /* data byte order, LSBFirst, MSBFirst */ + public int bitmap_unit; /* quant. of scanline 8, 16, 32 */ + public int bitmap_bit_order; /* LSBFirst, MSBFirst */ + public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */ + public int depth; /* depth of image */ + public int bytes_per_line; /* accelerator to next scanline */ + public int bits_per_pixel; /* bits per pixel (ZPixmap) */ + public ulong red_mask; /* bits in z arrangement */ + public ulong green_mask; + public ulong blue_mask; + public IntPtr obdata; /* hook for the object routines to hang on */ + } + + public struct XWindowAttributes + { + public int x; + public int y; + public int width; + public int height; + public int border_width; + public int depth; + public IntPtr visual; + public IntPtr root; + public int @class; + public int bit_gravity; + public int win_gravity; + public int backing_store; + public ulong backing_planes; + public ulong backing_pixel; + public bool save_under; + public IntPtr colormap; + public bool map_installed; + public int map_state; + public long all_event_masks; + public long your_event_mask; + public long do_not_propagate_mask; + public bool override_redirect; + public IntPtr screen; + } +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Linux/LibXtst.cs b/src/Remote/Insight.Remote.Shared/Native/Linux/LibXtst.cs new file mode 100644 index 0000000..0ae3b5b --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Linux/LibXtst.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Linux; + +public class LibXtst +{ + [DllImport("libXtst")] + public static extern bool XTestQueryExtension(IntPtr display, out int event_base, out int error_base, out int major_version, out int minor_version); + [DllImport("libXtst")] + public static extern void XTestFakeKeyEvent(IntPtr display, uint keycode, bool is_press, ulong delay); + [DllImport("libXtst")] + public static extern void XTestFakeButtonEvent(IntPtr display, uint button, bool is_press, ulong delay); + [DllImport("libXtst")] + public static extern void XTestFakeMotionEvent(IntPtr display, int screen_number, int x, int y, ulong delay); +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Linux/Libc.cs b/src/Remote/Insight.Remote.Shared/Native/Linux/Libc.cs new file mode 100644 index 0000000..2abbe71 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Linux/Libc.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Linux; + +public class Libc +{ + [DllImport("libc", SetLastError = true)] + public static extern uint geteuid(); +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Native/Linux/libXrandr.cs b/src/Remote/Insight.Remote.Shared/Native/Linux/libXrandr.cs new file mode 100644 index 0000000..3f7d864 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Linux/libXrandr.cs @@ -0,0 +1,62 @@ +/* + * Copyright © 2000 Compaq Computer Corporation, Inc. + * Copyright © 2002 Hewlett-Packard Company, Inc. + * Copyright © 2006 Intel Corporation + * Copyright © 2008 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Author: Jim Gettys, HP Labs, Hewlett-Packard, Inc. + * Keith Packard, Intel Corporation + */ + + +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Linux; + +public static class LibXrandr +{ + [StructLayout(LayoutKind.Sequential)] + public struct XRRMonitorInfo + { + // Atom + public IntPtr name; + public bool primary; + public bool automatic; + public int noutput; + public int x; + public int y; + public int width; + public int height; + public int mwidth; + public int mheight; + // RROutput* + public IntPtr outputs; + } + + [DllImport("libXrandr")] + public static extern IntPtr XRRGetMonitors(IntPtr display, IntPtr window, bool get_active, out int monitors); + + [DllImport("libXrandr")] + public static extern void XRRFreeMonitors(IntPtr monitors); + + [DllImport("libXrandr")] + public static extern IntPtr XRRAllocateMonitor(IntPtr display, int output); +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/ADVAPI32.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/ADVAPI32.cs new file mode 100644 index 0000000..749ae48 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/ADVAPI32.cs @@ -0,0 +1,364 @@ +using System.Runtime.InteropServices; +using System.Security; + +namespace Insight.Remote.Shared.Native.Windows; + +public static class ADVAPI32 +{ + #region Structs + public struct TOKEN_PRIVILEGES + { + public struct LUID + { + public uint LowPart; + public int HighPart; + } + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct LUID_AND_ATTRIBUTES + { + public LUID Luid; + public uint Attributes; + } + public int PrivilegeCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = ANYSIZE_ARRAY)] + public LUID_AND_ATTRIBUTES[] Privileges; + } + public class USEROBJECTFLAGS + { + public int fInherit = 0; + public int fReserved = 0; + public int dwFlags = 0; + } + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES + { + public int Length; + public IntPtr lpSecurityDescriptor; + public bool bInheritHandle; + } + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct STARTUPINFO + { + public int cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public int dwX; + public int dwY; + public int dwXSize; + public int dwYSize; + public int dwXCountChars; + public int dwYCountChars; + public int dwFillAttribute; + public int dwFlags; + public short wShowWindow; + public short cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + #endregion + + #region Enums + public enum TOKEN_INFORMATION_CLASS + { + /// +     /// The buffer receives a TOKEN_USER structure that contains the user account of the token. +     /// + TokenUser = 1, + + /// +     /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token. +     /// + TokenGroups, + + /// +     /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token. +     /// + TokenPrivileges, + + /// +     /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects. +     /// + TokenOwner, + + /// +     /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects. +     /// + TokenPrimaryGroup, + + /// +     /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects. +     /// + TokenDefaultDacl, + + /// +     /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information. +     /// + TokenSource, + + /// +     /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token. +     /// + TokenType, + + /// +     /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails. +     /// + TokenImpersonationLevel, + + /// +     /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics. +     /// + TokenStatistics, + + /// +     /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token. +     /// + TokenRestrictedSids, + + /// +     /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token. +     /// + TokenSessionId, + + /// +     /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token. +     /// + TokenGroupsAndPrivileges, + + /// +     /// Reserved. +     /// + TokenSessionReference, + + /// +     /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag. +     /// + TokenSandBoxInert, + + /// +     /// Reserved. +     /// + TokenAuditPolicy, + + /// +     /// The buffer receives a TOKEN_ORIGIN value. +     /// + TokenOrigin, + + /// +     /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token. +     /// + TokenElevationType, + + /// +     /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token. +     /// + TokenLinkedToken, + + /// +     /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated. +     /// + TokenElevation, + + /// +     /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered. +     /// + TokenHasRestrictions, + + /// +     /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token. +     /// + TokenAccessInformation, + + /// +     /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token. +     /// + TokenVirtualizationAllowed, + + /// +     /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token. +     /// + TokenVirtualizationEnabled, + + /// +     /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level. +     /// + TokenIntegrityLevel, + + /// +     /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set. +     /// + TokenUIAccess, + + /// +     /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy. +     /// + TokenMandatoryPolicy, + + /// +     /// The buffer receives the token's logon security identifier (SID). +     /// + TokenLogonSid, + + /// +     /// The maximum value for this enumeration +     /// + MaxTokenInfoClass + } + public enum LOGON_TYPE + { + LOGON32_LOGON_INTERACTIVE = 2, + LOGON32_LOGON_NETWORK, + LOGON32_LOGON_BATCH, + LOGON32_LOGON_SERVICE, + LOGON32_LOGON_UNLOCK = 7, + LOGON32_LOGON_NETWORK_CLEARTEXT, + LOGON32_LOGON_NEW_CREDENTIALS + } + public enum LOGON_PROVIDER + { + LOGON32_PROVIDER_DEFAULT, + LOGON32_PROVIDER_WINNT35, + LOGON32_PROVIDER_WINNT40, + LOGON32_PROVIDER_WINNT50 + } + [Flags] + public enum CreateProcessFlags + { + CREATE_BREAKAWAY_FROM_JOB = 0x01000000, + CREATE_DEFAULT_ERROR_MODE = 0x04000000, + CREATE_NEW_CONSOLE = 0x00000010, + CREATE_NEW_PROCESS_GROUP = 0x00000200, + CREATE_NO_WINDOW = 0x08000000, + CREATE_PROTECTED_PROCESS = 0x00040000, + CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, + CREATE_SEPARATE_WOW_VDM = 0x00000800, + CREATE_SHARED_WOW_VDM = 0x00001000, + CREATE_SUSPENDED = 0x00000004, + CREATE_UNICODE_ENVIRONMENT = 0x00000400, + DEBUG_ONLY_THIS_PROCESS = 0x00000002, + DEBUG_PROCESS = 0x00000001, + DETACHED_PROCESS = 0x00000008, + EXTENDED_STARTUPINFO_PRESENT = 0x00080000, + INHERIT_PARENT_AFFINITY = 0x00010000 + } + public enum TOKEN_TYPE : int + { + TokenPrimary = 1, + TokenImpersonation = 2 + } + + public enum SECURITY_IMPERSONATION_LEVEL : int + { + SecurityAnonymous = 0, + SecurityIdentification = 1, + SecurityImpersonation = 2, + SecurityDelegation = 3, + } + + #endregion + + #region Constants + public const int TOKEN_DUPLICATE = 0x0002; + public const uint MAXIMUM_ALLOWED = 0x2000000; + public const int CREATE_NEW_CONSOLE = 0x00000010; + public const int CREATE_NO_WINDOW = 0x08000000; + public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; + public const int STARTF_USESHOWWINDOW = 0x00000001; + public const int DETACHED_PROCESS = 0x00000008; + public const int TOKEN_ALL_ACCESS = 0x000f01ff; + public const int PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF; + public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000; + public const int SYNCHRONIZE = 0x00100000; + + public const int IDLE_PRIORITY_CLASS = 0x40; + public const int NORMAL_PRIORITY_CLASS = 0x20; + public const int HIGH_PRIORITY_CLASS = 0x80; + public const int REALTIME_PRIORITY_CLASS = 0x100; + public const uint SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001; + public const uint SE_PRIVILEGE_ENABLED = 0x00000002; + public const uint SE_PRIVILEGE_REMOVED = 0x00000004; + public const uint SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000; + public const int ANYSIZE_ARRAY = 1; + + public const int UOI_FLAGS = 1; + public const int UOI_NAME = 2; + public const int UOI_TYPE = 3; + public const int UOI_USER_SID = 4; + public const int UOI_HEAPSIZE = 5; + public const int UOI_IO = 6; + #endregion + + #region DLL Imports + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AdjustTokenPrivileges(IntPtr tokenHandle, + [MarshalAs(UnmanagedType.Bool)] bool disableAllPrivileges, + ref TOKEN_PRIVILEGES newState, + uint bufferLengthInBytes, + ref TOKEN_PRIVILEGES previousState, + out uint returnLengthInBytes); + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool CreateProcessAsUser( + IntPtr hToken, + string? lpApplicationName, + string lpCommandLine, + ref SECURITY_ATTRIBUTES lpProcessAttributes, + ref SECURITY_ATTRIBUTES lpThreadAttributes, + bool bInheritHandles, + uint dwCreationFlags, + IntPtr lpEnvironment, + string? lpCurrentDirectory, + ref STARTUPINFO lpStartupInfo, + out PROCESS_INFORMATION lpProcessInformation); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool AllocateLocallyUniqueId(out IntPtr pLuid); + + [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern SECUR32.WinErrors LsaNtStatusToWinError(SECUR32.WinStatusCodes status); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool GetTokenInformation( + IntPtr TokenHandle, + SECUR32.TOKEN_INFORMATION_CLASS TokenInformationClass, + IntPtr TokenInformation, + uint TokenInformationLength, + out uint ReturnLength); + + [DllImport("advapi32.dll", SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LogonUser( + [MarshalAs(UnmanagedType.LPStr)] string pszUserName, + [MarshalAs(UnmanagedType.LPStr)] string pszDomain, + [MarshalAs(UnmanagedType.LPStr)] string pszPassword, + int dwLogonType, + int dwLogonProvider, + out IntPtr phToken); + + [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity] + public static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle); + [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool DuplicateTokenEx( + IntPtr hExistingToken, + uint dwDesiredAccess, + ref SECURITY_ATTRIBUTES lpTokenAttributes, + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, + TOKEN_TYPE TokenType, + out IntPtr phNewToken); + + [DllImport("advapi32.dll", SetLastError = false)] + public static extern uint LsaNtStatusToWinError(uint status); + #endregion +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/GDI32.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/GDI32.cs new file mode 100644 index 0000000..74fb28e --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/GDI32.cs @@ -0,0 +1,79 @@ +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Windows; + +public static class GDI32 +{ + #region Enums + /// + /// Specifies a raster-operation code. These codes define how the color data for the + /// source rectangle is to be combined with the color data for the destination + /// rectangle to achieve the final color. + /// + public enum TernaryRasterOperations : uint + { + /// dest = source + SRCCOPY = 0x00CC0020, + /// dest = source OR dest + SRCPAINT = 0x00EE0086, + /// dest = source AND dest + SRCAND = 0x008800C6, + /// dest = source XOR dest + SRCINVERT = 0x00660046, + /// dest = source AND (NOT dest) + SRCERASE = 0x00440328, + /// dest = (NOT source) + NOTSRCCOPY = 0x00330008, + /// dest = (NOT src) AND (NOT dest) + NOTSRCERASE = 0x001100A6, + /// dest = (source AND pattern) + MERGECOPY = 0x00C000CA, + /// dest = (NOT source) OR dest + MERGEPAINT = 0x00BB0226, + /// dest = pattern + PATCOPY = 0x00F00021, + /// dest = DPSnoo + PATPAINT = 0x00FB0A09, + /// dest = pattern XOR dest + PATINVERT = 0x005A0049, + /// dest = (NOT dest) + DSTINVERT = 0x00550009, + /// dest = BLACK + BLACKNESS = 0x00000042, + /// dest = WHITE + WHITENESS = 0x00FF0062, + /// + /// Capture window as seen on screen. This includes layered windows + /// such as WPF windows with AllowsTransparency="true" + /// + CAPTUREBLT = 0x40000000 + } + #endregion + + #region DLL Imports + + [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); + + [DllImport("gdi32.dll")] + public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); + + [DllImport("GDI32.dll")] + public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); [DllImport("GDI32.dll")] + public static extern IntPtr CreateCompatibleDC(IntPtr hdc); + + [DllImport("GDI32.dll")] + public static extern bool DeleteDC(IntPtr hdc); + + [DllImport("GDI32.dll")] + public static extern bool DeleteObject(IntPtr hObject); + + [DllImport("GDI32.dll")] + public static extern IntPtr GetDeviceCaps(IntPtr hdc, int nIndex); + + [DllImport("GDI32.dll")] + public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); + + #endregion +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/Kernel32.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/Kernel32.cs new file mode 100644 index 0000000..1787667 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/Kernel32.cs @@ -0,0 +1,88 @@ +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Windows; + +public static class Kernel32 +{ + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hSnapshot); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr GetCommandLine(); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetConsoleWindow(); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); + + [DllImport("kernel32.dll")] + public static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId); + + [DllImport("kernel32.dll")] + public static extern uint WTSGetActiveConsoleSessionId(); + + /// + /// contains information about the current state of both physical and virtual memory, including extended memory + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class MEMORYSTATUSEX + { + /// + /// Size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx. + /// + public uint dwLength; + + /// + /// Number between 0 and 100 that specifies the approximate percentage of physical memory that is in use (0 indicates no memory use and 100 indicates full memory use). + /// + public uint dwMemoryLoad; + + /// + /// Total size of physical memory, in bytes. + /// + public ulong ullTotalPhys; + + /// + /// Size of physical memory available, in bytes. + /// + public ulong ullAvailPhys; + + /// + /// Size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead. + /// + public ulong ullTotalPageFile; + + /// + /// Size of available memory to commit, in bytes. The limit is ullTotalPageFile. + /// + public ulong ullAvailPageFile; + + /// + /// Total size of the user mode portion of the virtual address space of the calling process, in bytes. + /// + public ulong ullTotalVirtual; + + /// + /// Size of unreserved and uncommitted memory in the user mode portion of the virtual address space of the calling process, in bytes. + /// + public ulong ullAvailVirtual; + + /// + /// Size of unreserved and uncommitted memory in the extended portion of the virtual address space of the calling process, in bytes. + /// + public ulong ullAvailExtendedVirtual; + + /// + /// Initializes a new instance of the class. + /// + public MEMORYSTATUSEX() + { + dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)); + } + } +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/SECUR32.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/SECUR32.cs new file mode 100644 index 0000000..e96f0dc --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/SECUR32.cs @@ -0,0 +1,376 @@ +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Insight.Remote.Shared.Native.Windows; + +public static class SECUR32 +{ + public enum WinStatusCodes : uint + { + STATUS_SUCCESS = 0 + } + + public enum WinErrors : uint + { + NO_ERROR = 0, + } + public enum WinLogonType + { + LOGON32_LOGON_INTERACTIVE = 2, + LOGON32_LOGON_NETWORK = 3, + LOGON32_LOGON_BATCH = 4, + LOGON32_LOGON_SERVICE = 5, + LOGON32_LOGON_UNLOCK = 7, + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + LOGON32_LOGON_NEW_CREDENTIALS = 9 + } + + // SECURITY_LOGON_TYPE + public enum SecurityLogonType + { + Interactive = 2, // Interactively logged on (locally or remotely) + Network, // Accessing system via network + Batch, // Started via a batch queue + Service, // Service started by service controller + Proxy, // Proxy logon + Unlock, // Unlock workstation + NetworkCleartext, // Network logon with cleartext credentials + NewCredentials, // Clone caller, new default credentials + RemoteInteractive, // Remote, yet interactive. Terminal server + CachedInteractive, // Try cached credentials without hitting the net. + CachedRemoteInteractive, // Same as RemoteInteractive, this is used internally for auditing purpose + CachedUnlock // Cached Unlock workstation + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_UNICODE_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public IntPtr Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TOKEN_SOURCE + { + public TOKEN_SOURCE(string name) + { + SourceName = new byte[8]; + System.Text.Encoding.GetEncoding(1252).GetBytes(name, 0, name.Length, SourceName, 0); + if (!ADVAPI32.AllocateLocallyUniqueId(out SourceIdentifier)) + throw new System.ComponentModel.Win32Exception(); + } + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] SourceName; + public IntPtr SourceIdentifier; + } + [StructLayout(LayoutKind.Sequential)] + public struct KERB_INTERACTIVE_LOGON + { + public KERB_LOGON_SUBMIT_TYPE MessageType; + public string LogonDomainName; + public string UserName; + public string Password; + } + public enum KERB_LOGON_SUBMIT_TYPE + { + KerbInteractiveLogon = 2, + KerbSmartCardLogon = 6, + KerbWorkstationUnlockLogon = 7, + KerbSmartCardUnlockLogon = 8, + KerbProxyLogon = 9, + KerbTicketLogon = 10, + KerbTicketUnlockLogon = 11, + KerbS4ULogon = 12, + KerbCertificateLogon = 13, + KerbCertificateS4ULogon = 14, + KerbCertificateUnlockLogon = 15 + } + public enum TOKEN_INFORMATION_CLASS + { + /// +     /// The buffer receives a TOKEN_USER structure that contains the user account of the token. +     /// + TokenUser = 1, + + /// +     /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token. +     /// + TokenGroups, + + /// +     /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token. +     /// + TokenPrivileges, + + /// +     /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects. +     /// + TokenOwner, + + /// +     /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects. +     /// + TokenPrimaryGroup, + + /// +     /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects. +     /// + TokenDefaultDacl, + + /// +     /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information. +     /// + TokenSource, + + /// +     /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token. +     /// + TokenType, + + /// +     /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails. +     /// + TokenImpersonationLevel, + + /// +     /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics. +     /// + TokenStatistics, + + /// +     /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token. +     /// + TokenRestrictedSids, + + /// +     /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token. +     /// + TokenSessionId, + + /// +     /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token. +     /// + TokenGroupsAndPrivileges, + + /// +     /// Reserved. +     /// + TokenSessionReference, + + /// +     /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag. +     /// + TokenSandBoxInert, + + /// +     /// Reserved. +     /// + TokenAuditPolicy, + + /// +     /// The buffer receives a TOKEN_ORIGIN value. +     /// + TokenOrigin, + + /// +     /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token. +     /// + TokenElevationType, + + /// +     /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token. +     /// + TokenLinkedToken, + + /// +     /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated. +     /// + TokenElevation, + + /// +     /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered. +     /// + TokenHasRestrictions, + + /// +     /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token. +     /// + TokenAccessInformation, + + /// +     /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token. +     /// + TokenVirtualizationAllowed, + + /// +     /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token. +     /// + TokenVirtualizationEnabled, + + /// +     /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level. +     /// + TokenIntegrityLevel, + + /// +     /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set. +     /// + TokenUIAccess, + + /// +     /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy. +     /// + TokenMandatoryPolicy, + + /// +     /// The buffer receives the token's logon security identifier (SID). +     /// + TokenLogonSid, + + /// +     /// The maximum value for this enumeration +     /// + MaxTokenInfoClass + } + [StructLayout(LayoutKind.Sequential)] + public struct QUOTA_LIMITS + { + readonly UInt32 PagedPoolLimit; + readonly UInt32 NonPagedPoolLimit; + readonly UInt32 MinimumWorkingSetSize; + readonly UInt32 MaximumWorkingSetSize; + readonly UInt32 PagefileLimit; + readonly Int64 TimeLimit; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public /*PCHAR*/ IntPtr Buffer; + } + + + [DllImport("secur32.dll", SetLastError = true)] + public static extern WinStatusCodes LsaLogonUser( + [In] IntPtr LsaHandle, + [In] ref LSA_STRING OriginName, + [In] SecurityLogonType LogonType, + [In] UInt32 AuthenticationPackage, + [In] IntPtr AuthenticationInformation, + [In] UInt32 AuthenticationInformationLength, + [In] /*PTOKEN_GROUPS*/ IntPtr LocalGroups, + [In] ref TOKEN_SOURCE SourceContext, + [Out] /*PVOID*/ out IntPtr ProfileBuffer, + [Out] out UInt32 ProfileBufferLength, + [Out] out Int64 LogonId, + [Out] out IntPtr Token, + [Out] out QUOTA_LIMITS Quotas, + [Out] out WinStatusCodes SubStatus + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern WinStatusCodes LsaRegisterLogonProcess( + IntPtr LogonProcessName, + out IntPtr LsaHandle, + out ulong SecurityMode + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern WinStatusCodes LsaLookupAuthenticationPackage([In] IntPtr LsaHandle, [In] ref LSA_STRING PackageName, [Out] out UInt32 AuthenticationPackage); + + [DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + internal static extern int LsaConnectUntrusted( + [In, Out] ref SafeLsaLogonProcessHandle LsaHandle); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern WinStatusCodes LsaConnectUntrusted([Out] out IntPtr LsaHandle); + + [System.Security.SecurityCritical] // auto-generated + internal sealed class SafeLsaLogonProcessHandle : SafeHandleZeroOrMinusOneIsInvalid + { + private SafeLsaLogonProcessHandle() : base(true) { } + + // 0 is an Invalid Handle + internal SafeLsaLogonProcessHandle(IntPtr handle) : base(true) + { + SetHandle(handle); + } + + internal static SafeLsaLogonProcessHandle InvalidHandle + { + get { return new SafeLsaLogonProcessHandle(IntPtr.Zero); } + } + + [System.Security.SecurityCritical] + protected override bool ReleaseHandle() + { + // LsaDeregisterLogonProcess returns an NTSTATUS + return LsaDeregisterLogonProcess(handle) >= 0; + } + } + + [DllImport("secur32.dll", SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + internal static extern int LsaDeregisterLogonProcess(IntPtr handle); + + + public static void CreateNewSession() + { + var kli = new SECUR32.KERB_INTERACTIVE_LOGON() + { + MessageType = SECUR32.KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon, + UserName = "", + Password = "" + }; + IntPtr kerbLogInfo; + SECUR32.LSA_STRING logonProc = new() + { + Buffer = Marshal.StringToHGlobalAuto("InstaLogon"), + Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")), + MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")) + }; + SECUR32.LSA_STRING originName = new() + { + Buffer = Marshal.StringToHGlobalAuto("InstaLogon"), + Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")), + MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")) + }; + SECUR32.LSA_STRING authPackage = new() + { + Buffer = Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A"), + Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A")), + MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A")) + }; + IntPtr hLogonProc = Marshal.AllocHGlobal(Marshal.SizeOf(logonProc)); + Marshal.StructureToPtr(logonProc, hLogonProc, false); + ADVAPI32.AllocateLocallyUniqueId(out IntPtr pluid); + LsaConnectUntrusted(out IntPtr lsaHan); + //SECUR32.LsaRegisterLogonProcess(hLogonProc, out lsaHan, out secMode); + SECUR32.LsaLookupAuthenticationPackage(lsaHan, ref authPackage, out uint authPackID); + + kerbLogInfo = Marshal.AllocHGlobal(Marshal.SizeOf(kli)); + Marshal.StructureToPtr(kli, kerbLogInfo, false); + + var ts = new SECUR32.TOKEN_SOURCE("Insta"); + SECUR32.LsaLogonUser( + lsaHan, + ref originName, + SECUR32.SecurityLogonType.Interactive, + authPackID, + kerbLogInfo, + (uint)Marshal.SizeOf(kerbLogInfo), + IntPtr.Zero, + ref ts, + out IntPtr profBuf, + out uint profBufLen, + out long logonID, + out IntPtr logonToken, + out QUOTA_LIMITS quotas, + out WinStatusCodes subStatus); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/Shlwapi.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/Shlwapi.cs new file mode 100644 index 0000000..c7fe64b --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/Shlwapi.cs @@ -0,0 +1,51 @@ +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Windows; + +// https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-isos +public class Shlwapi +{ + [DllImport("shlwapi.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsOS(OsType osType); +} + +public enum OsType +{ + OS_WINDOWS = 0, + OS_NT = 1, + OS_WIN95ORGREATER = 2, + OS_NT4ORGREATER = 3, + OS_WIN98ORGREATER = 5, + OS_WIN98_GOLD = 6, + OS_WIN2000ORGREATER = 7, + OS_WIN2000PRO = 8, + OS_WIN2000SERVER = 9, + OS_WIN2000ADVSERVER = 10, + OS_WIN2000DATACENTER = 11, + OS_WIN2000TERMINAL = 12, + OS_EMBEDDED = 13, + OS_TERMINALCLIENT = 14, + OS_TERMINALREMOTEADMIN = 15, + OS_WIN95_GOLD = 16, + OS_MEORGREATER = 17, + OS_XPORGREATER = 18, + OS_HOME = 19, + OS_PROFESSIONAL = 20, + OS_DATACENTER = 21, + OS_ADVSERVER = 22, + OS_SERVER = 23, + OS_TERMINALSERVER = 24, + OS_PERSONALTERMINALSERVER = 25, + OS_FASTUSERSWITCHING = 26, + OS_WELCOMELOGONUI = 27, + OS_DOMAINMEMBER = 28, + OS_ANYSERVER = 29, + OS_WOW6432 = 30, + OS_WEBSERVER = 31, + OS_SMALLBUSINESSSERVER = 32, + OS_TABLETPC = 33, + OS_SERVERADMINUI = 34, + OS_MEDIACENTER = 35, + OS_APPLIANCE = 36, +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/User32.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/User32.cs new file mode 100644 index 0000000..46ad8b5 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/User32.cs @@ -0,0 +1,1336 @@ +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Windows; + +public static class User32 +{ + #region Constants + public const int CURSOR_SHOWING = 0x00000001; + public const uint MOUSEEVENTF_ABSOLUTE = 0x8000; + public const int MOUSEEVENTF_LEFTDOWN = 0x02; + public const int MOUSEEVENTF_LEFTUP = 0x04; + public const int MOUSEEVENTF_RIGHTDOWN = 0x08; + public const int MOUSEEVENTF_RIGHTUP = 0x10; + public const int MOUSEEVENTF_MOVE = 0x0001; + public const uint KEYEVENTF_EXTENDEDKEY = 0x0001; + public const uint KEYEVENTF_KEYUP = 0x0002; + + public const int SPIF_SENDWININICHANGE = 0x02; + public const int SPI_SETDESKWALLPAPER = 20; + public const int SPIF_UPDATEINIFILE = 1; + public const int SPIF_SENDCHANGE = 2; + + public static readonly int SPI_GETDESKWALLPAPER = 0x73; + public static readonly int MAX_PATH = 260; + #endregion + + #region Enums + [Flags] + public enum MouseEventFlags : uint + { + LEFTDOWN = 0x00000002, + LEFTUP = 0x00000004, + MIDDLEDOWN = 0x00000020, + MIDDLEUP = 0x00000040, + MOVE = 0x00000001, + ABSOLUTE = 0x00008000, + RIGHTDOWN = 0x00000008, + RIGHTUP = 0x00000010, + WHEEL = 0x00000800, + XDOWN = 0x00000080, + XUP = 0x00000100 + } + [Flags] + public enum MOUSEEVENTF : uint + { + ABSOLUTE = 0x8000, + HWHEEL = 0x01000, + MOVE = 0x0001, + MOVE_NOCOALESCE = 0x2000, + LEFTDOWN = 0x0002, + LEFTUP = 0x0004, + RIGHTDOWN = 0x0008, + RIGHTUP = 0x0010, + MIDDLEDOWN = 0x0020, + MIDDLEUP = 0x0040, + VIRTUALDESK = 0x4000, + WHEEL = 0x0800, + XDOWN = 0x0080, + XUP = 0x0100 + } + public enum MonitorState + { + MonitorStateOn = -1, + MonitorStateOff = 2, + MonitorStateStandBy = 1 + } + [Flags] + public enum KEYEVENTF : uint + { + EXTENDEDKEY = 0x0001, + KEYUP = 0x0002, + SCANCODE = 0x0008, + UNICODE = 0x0004 + } + + public enum VirtualKey : short + { + /// +         ///Left mouse button +         /// + LBUTTON = 0x01, + /// +         ///Right mouse button +         /// + RBUTTON = 0x02, + /// +         ///Control-break processing +         /// + CANCEL = 0x03, + /// +         ///Middle mouse button (three-button mouse) +         /// + MBUTTON = 0x04, + /// +         ///Windows 2000/XP: X1 mouse button +         /// + XBUTTON1 = 0x05, + /// +         ///Windows 2000/XP: X2 mouse button +         /// + XBUTTON2 = 0x06, + /// +         ///BACKSPACE key +         /// + BACK = 0x08, + /// +         ///TAB key +         /// + TAB = 0x09, + /// +         ///CLEAR key +         /// + CLEAR = 0x0C, + /// +         ///ENTER key +         /// + RETURN = 0x0D, + /// +         ///SHIFT key +         /// + SHIFT = 0x10, + /// +         ///CTRL key +         /// + CONTROL = 0x11, + /// +         ///ALT key +         /// + MENU = 0x12, + /// +         ///PAUSE key +         /// + PAUSE = 0x13, + /// +         ///CAPS LOCK key +         /// + CAPITAL = 0x14, + /// +         ///Input Method Editor (IME) Kana mode +         /// + KANA = 0x15, + /// +         ///IME Hangul mode +         /// + HANGUL = 0x15, + /// +         ///IME Junja mode +         /// + JUNJA = 0x17, + /// +         ///IME final mode +         /// + FINAL = 0x18, + /// +         ///IME Hanja mode +         /// + HANJA = 0x19, + /// +         ///IME Kanji mode +         /// + KANJI = 0x19, + /// +         ///ESC key +         /// + ESCAPE = 0x1B, + /// +         ///IME convert +         /// + CONVERT = 0x1C, + /// +         ///IME nonconvert +         /// + NONCONVERT = 0x1D, + /// +         ///IME accept +         /// + ACCEPT = 0x1E, + /// +         ///IME mode change request +         /// + MODECHANGE = 0x1F, + /// +         ///SPACEBAR +         /// + SPACE = 0x20, + /// +         ///PAGE UP key +         /// + PRIOR = 0x21, + /// +         ///PAGE DOWN key +         /// + NEXT = 0x22, + /// +         ///END key +         /// + END = 0x23, + /// +         ///HOME key +         /// + HOME = 0x24, + /// +         ///LEFT ARROW key +         /// + LEFT = 0x25, + /// +         ///UP ARROW key +         /// + UP = 0x26, + /// +         ///RIGHT ARROW key +         /// + RIGHT = 0x27, + /// +         ///DOWN ARROW key +         /// + DOWN = 0x28, + /// +         ///SELECT key +         /// + SELECT = 0x29, + /// +         ///PRINT key +         /// + PRINT = 0x2A, + /// +         ///EXECUTE key +         /// + EXECUTE = 0x2B, + /// +         ///PRINT SCREEN key +         /// + SNAPSHOT = 0x2C, + /// +         ///INS key +         /// + INSERT = 0x2D, + /// +         ///DEL key +         /// + DELETE = 0x2E, + /// +         ///HELP key +         /// + HELP = 0x2F, + /// +         ///0 key +         /// + KEY_0 = 0x30, + /// +         ///1 key +         /// + KEY_1 = 0x31, + /// +         ///2 key +         /// + KEY_2 = 0x32, + /// +         ///3 key +         /// + KEY_3 = 0x33, + /// +         ///4 key +         /// + KEY_4 = 0x34, + /// +         ///5 key +         /// + KEY_5 = 0x35, + /// +         ///6 key +         /// + KEY_6 = 0x36, + /// +         ///7 key +         /// + KEY_7 = 0x37, + /// +         ///8 key +         /// + KEY_8 = 0x38, + /// +         ///9 key +         /// + KEY_9 = 0x39, + /// +         ///A key +         /// + KEY_A = 0x41, + /// +         ///B key +         /// + KEY_B = 0x42, + /// +         ///C key +         /// + KEY_C = 0x43, + /// +         ///D key +         /// + KEY_D = 0x44, + /// +         ///E key +         /// + KEY_E = 0x45, + /// +         ///F key +         /// + KEY_F = 0x46, + /// +         ///G key +         /// + KEY_G = 0x47, + /// +         ///H key +         /// + KEY_H = 0x48, + /// +         ///I key +         /// + KEY_I = 0x49, + /// +         ///J key +         /// + KEY_J = 0x4A, + /// +         ///K key +         /// + KEY_K = 0x4B, + /// +         ///L key +         /// + KEY_L = 0x4C, + /// +         ///M key +         /// + KEY_M = 0x4D, + /// +         ///N key +         /// + KEY_N = 0x4E, + /// +         ///O key +         /// + KEY_O = 0x4F, + /// +         ///P key +         /// + KEY_P = 0x50, + /// +         ///Q key +         /// + KEY_Q = 0x51, + /// +         ///R key +         /// + KEY_R = 0x52, + /// +         ///S key +         /// + KEY_S = 0x53, + /// +         ///T key +         /// + KEY_T = 0x54, + /// +         ///U key +         /// + KEY_U = 0x55, + /// +         ///V key +         /// + KEY_V = 0x56, + /// +         ///W key +         /// + KEY_W = 0x57, + /// +         ///X key +         /// + KEY_X = 0x58, + /// +         ///Y key +         /// + KEY_Y = 0x59, + /// +         ///Z key +         /// + KEY_Z = 0x5A, + /// +         ///Left Windows key (Microsoft Natural keyboard) +         /// + LWIN = 0x5B, + /// +         ///Right Windows key (Natural keyboard) +         /// + RWIN = 0x5C, + /// +         ///Applications key (Natural keyboard) +         /// + APPS = 0x5D, + /// +         ///Computer Sleep key +         /// + SLEEP = 0x5F, + /// +         ///Numeric keypad 0 key +         /// + NUMPAD0 = 0x60, + /// +         ///Numeric keypad 1 key +         /// + NUMPAD1 = 0x61, + /// +         ///Numeric keypad 2 key +         /// + NUMPAD2 = 0x62, + /// +         ///Numeric keypad 3 key +         /// + NUMPAD3 = 0x63, + /// +         ///Numeric keypad 4 key +         /// + NUMPAD4 = 0x64, + /// +         ///Numeric keypad 5 key +         /// + NUMPAD5 = 0x65, + /// +         ///Numeric keypad 6 key +         /// + NUMPAD6 = 0x66, + /// +         ///Numeric keypad 7 key +         /// + NUMPAD7 = 0x67, + /// +         ///Numeric keypad 8 key +         /// + NUMPAD8 = 0x68, + /// +         ///Numeric keypad 9 key +         /// + NUMPAD9 = 0x69, + /// +         ///Multiply key +         /// + MULTIPLY = 0x6A, + /// +         ///Add key +         /// + ADD = 0x6B, + /// +         ///Separator key +         /// + SEPARATOR = 0x6C, + /// +         ///Subtract key +         /// + SUBTRACT = 0x6D, + /// +         ///Decimal key +         /// + DECIMAL = 0x6E, + /// +         ///Divide key +         /// + DIVIDE = 0x6F, + /// +         ///F1 key +         /// + F1 = 0x70, + /// +         ///F2 key +         /// + F2 = 0x71, + /// +         ///F3 key +         /// + F3 = 0x72, + /// +         ///F4 key +         /// + F4 = 0x73, + /// +         ///F5 key +         /// + F5 = 0x74, + /// +         ///F6 key +         /// + F6 = 0x75, + /// +         ///F7 key +         /// + F7 = 0x76, + /// +         ///F8 key +         /// + F8 = 0x77, + /// +         ///F9 key +         /// + F9 = 0x78, + /// +         ///F10 key +         /// + F10 = 0x79, + /// +         ///F11 key +         /// + F11 = 0x7A, + /// +         ///F12 key +         /// + F12 = 0x7B, + /// +         ///F13 key +         /// + F13 = 0x7C, + /// +         ///F14 key +         /// + F14 = 0x7D, + /// +         ///F15 key +         /// + F15 = 0x7E, + /// +         ///F16 key +         /// + F16 = 0x7F, + /// +         ///F17 key   +         /// + F17 = 0x80, + /// +         ///F18 key   +         /// + F18 = 0x81, + /// +         ///F19 key   +         /// + F19 = 0x82, + /// +         ///F20 key   +         /// + F20 = 0x83, + /// +         ///F21 key   +         /// + F21 = 0x84, + /// +         ///F22 key, (PPC only) Key used to lock device. +         /// + F22 = 0x85, + /// +         ///F23 key   +         /// + F23 = 0x86, + /// +         ///F24 key   +         /// + F24 = 0x87, + /// +         ///NUM LOCK key +         /// + NUMLOCK = 0x90, + /// +         ///SCROLL LOCK key +         /// + SCROLL = 0x91, + /// +         ///Left SHIFT key +         /// + LSHIFT = 0xA0, + /// +         ///Right SHIFT key +         /// + RSHIFT = 0xA1, + /// +         ///Left CONTROL key +         /// + LCONTROL = 0xA2, + /// +         ///Right CONTROL key +         /// + RCONTROL = 0xA3, + /// +         ///Left MENU key +         /// + LMENU = 0xA4, + /// +         ///Right MENU key +         /// + RMENU = 0xA5, + /// +         ///Windows 2000/XP: Browser Back key +         /// + BROWSER_BACK = 0xA6, + /// +         ///Windows 2000/XP: Browser Forward key +         /// + BROWSER_FORWARD = 0xA7, + /// +         ///Windows 2000/XP: Browser Refresh key +         /// + BROWSER_REFRESH = 0xA8, + /// +         ///Windows 2000/XP: Browser Stop key +         /// + BROWSER_STOP = 0xA9, + /// +         ///Windows 2000/XP: Browser Search key +         /// + BROWSER_SEARCH = 0xAA, + /// +         ///Windows 2000/XP: Browser Favorites key +         /// + BROWSER_FAVORITES = 0xAB, + /// +         ///Windows 2000/XP: Browser Start and Home key +         /// + BROWSER_HOME = 0xAC, + /// +         ///Windows 2000/XP: Volume Mute key +         /// + VOLUME_MUTE = 0xAD, + /// +         ///Windows 2000/XP: Volume Down key +         /// + VOLUME_DOWN = 0xAE, + /// +         ///Windows 2000/XP: Volume Up key +         /// + VOLUME_UP = 0xAF, + /// +         ///Windows 2000/XP: Next Track key +         /// + MEDIA_NEXT_TRACK = 0xB0, + /// +         ///Windows 2000/XP: Previous Track key +         /// + MEDIA_PREV_TRACK = 0xB1, + /// +         ///Windows 2000/XP: Stop Media key +         /// + MEDIA_STOP = 0xB2, + /// +         ///Windows 2000/XP: Play/Pause Media key +         /// + MEDIA_PLAY_PAUSE = 0xB3, + /// +         ///Windows 2000/XP: Start Mail key +         /// + LAUNCH_MAIL = 0xB4, + /// +         ///Windows 2000/XP: Select Media key +         /// + LAUNCH_MEDIA_SELECT = 0xB5, + /// +         ///Windows 2000/XP: Start Application 1 key +         /// + LAUNCH_APP1 = 0xB6, + /// +         ///Windows 2000/XP: Start Application 2 key +         /// + LAUNCH_APP2 = 0xB7, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_1 = 0xBA, + /// +         ///Windows 2000/XP: For any country/region, the '+' key +         /// + OEM_PLUS = 0xBB, + /// +         ///Windows 2000/XP: For any country/region, the ',' key +         /// + OEM_COMMA = 0xBC, + /// +         ///Windows 2000/XP: For any country/region, the '-' key +         /// + OEM_MINUS = 0xBD, + /// +         ///Windows 2000/XP: For any country/region, the '.' key +         /// + OEM_PERIOD = 0xBE, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_2 = 0xBF, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_3 = 0xC0, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_4 = 0xDB, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_5 = 0xDC, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_6 = 0xDD, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_7 = 0xDE, + /// +         ///Used for miscellaneous characters; it can vary by keyboard. +         /// + OEM_8 = 0xDF, + /// +         ///Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard +         /// + OEM_102 = 0xE2, + /// +         ///Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key +         /// + PROCESSKEY = 0xE5, + /// +         ///Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. +         ///The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, +         ///see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP +         /// + PACKET = 0xE7, + /// +         ///Attn key +         /// + ATTN = 0xF6, + /// +         ///CrSel key +         /// + CRSEL = 0xF7, + /// +         ///ExSel key +         /// + EXSEL = 0xF8, + /// +         ///Erase EOF key +         /// + EREOF = 0xF9, + /// +         ///Play key +         /// + PLAY = 0xFA, + /// +         ///Zoom key +         /// + ZOOM = 0xFB, + /// +         ///Reserved +         /// + NONAME = 0xFC, + /// +         ///PA1 key +         /// + PA1 = 0xFD, + /// +         ///Clear key +         /// + OEM_CLEAR = 0xFE + } + public enum ScanCodeShort : short + { + LBUTTON = 0, + RBUTTON = 0, + CANCEL = 70, + MBUTTON = 0, + XBUTTON1 = 0, + XBUTTON2 = 0, + BACK = 14, + TAB = 15, + CLEAR = 76, + RETURN = 28, + SHIFT = 42, + CONTROL = 29, + MENU = 56, + PAUSE = 0, + CAPITAL = 58, + KANA = 0, + HANGUL = 0, + JUNJA = 0, + FINAL = 0, + HANJA = 0, + KANJI = 0, + ESCAPE = 1, + CONVERT = 0, + NONCONVERT = 0, + ACCEPT = 0, + MODECHANGE = 0, + SPACE = 57, + PRIOR = 73, + NEXT = 81, + END = 79, + HOME = 71, + LEFT = 75, + UP = 72, + RIGHT = 77, + DOWN = 80, + SELECT = 0, + PRINT = 0, + EXECUTE = 0, + SNAPSHOT = 84, + INSERT = 82, + DELETE = 83, + HELP = 99, + KEY_0 = 11, + KEY_1 = 2, + KEY_2 = 3, + KEY_3 = 4, + KEY_4 = 5, + KEY_5 = 6, + KEY_6 = 7, + KEY_7 = 8, + KEY_8 = 9, + KEY_9 = 10, + KEY_A = 30, + KEY_B = 48, + KEY_C = 46, + KEY_D = 32, + KEY_E = 18, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_I = 23, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_M = 50, + KEY_N = 49, + KEY_O = 24, + KEY_P = 25, + KEY_Q = 16, + KEY_R = 19, + KEY_S = 31, + KEY_T = 20, + KEY_U = 22, + KEY_V = 47, + KEY_W = 17, + KEY_X = 45, + KEY_Y = 21, + KEY_Z = 44, + LWIN = 91, + RWIN = 92, + APPS = 93, + SLEEP = 95, + NUMPAD0 = 82, + NUMPAD1 = 79, + NUMPAD2 = 80, + NUMPAD3 = 81, + NUMPAD4 = 75, + NUMPAD5 = 76, + NUMPAD6 = 77, + NUMPAD7 = 71, + NUMPAD8 = 72, + NUMPAD9 = 73, + MULTIPLY = 55, + ADD = 78, + SEPARATOR = 0, + SUBTRACT = 74, + DECIMAL = 83, + DIVIDE = 53, + F1 = 59, + F2 = 60, + F3 = 61, + F4 = 62, + F5 = 63, + F6 = 64, + F7 = 65, + F8 = 66, + F9 = 67, + F10 = 68, + F11 = 87, + F12 = 88, + F13 = 100, + F14 = 101, + F15 = 102, + F16 = 103, + F17 = 104, + F18 = 105, + F19 = 106, + F20 = 107, + F21 = 108, + F22 = 109, + F23 = 110, + F24 = 118, + NUMLOCK = 69, + SCROLL = 70, + LSHIFT = 42, + RSHIFT = 54, + LCONTROL = 29, + RCONTROL = 29, + LMENU = 56, + RMENU = 56, + BROWSER_BACK = 106, + BROWSER_FORWARD = 105, + BROWSER_REFRESH = 103, + BROWSER_STOP = 104, + BROWSER_SEARCH = 101, + BROWSER_FAVORITES = 102, + BROWSER_HOME = 50, + VOLUME_MUTE = 32, + VOLUME_DOWN = 46, + VOLUME_UP = 48, + MEDIA_NEXT_TRACK = 25, + MEDIA_PREV_TRACK = 16, + MEDIA_STOP = 36, + MEDIA_PLAY_PAUSE = 34, + LAUNCH_MAIL = 108, + LAUNCH_MEDIA_SELECT = 109, + LAUNCH_APP1 = 107, + LAUNCH_APP2 = 33, + OEM_1 = 39, + OEM_PLUS = 13, + OEM_COMMA = 51, + OEM_MINUS = 12, + OEM_PERIOD = 52, + OEM_2 = 53, + OEM_3 = 41, + OEM_4 = 26, + OEM_5 = 43, + OEM_6 = 27, + OEM_7 = 40, + OEM_8 = 0, + OEM_102 = 86, + PROCESSKEY = 0, + PACKET = 0, + ATTN = 0, + CRSEL = 0, + EXSEL = 0, + EREOF = 93, + PLAY = 0, + ZOOM = 98, + NONAME = 0, + PA1 = 0, + OEM_CLEAR = 0, + } + [Flags] + public enum ACCESS_MASK : uint + { + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + + STANDARD_RIGHTS_REQUIRED = 0x000F0000, + + STANDARD_RIGHTS_READ = 0x00020000, + STANDARD_RIGHTS_WRITE = 0x00020000, + STANDARD_RIGHTS_EXECUTE = 0x00020000, + + STANDARD_RIGHTS_ALL = 0x001F0000, + + SPECIFIC_RIGHTS_ALL = 0x0000FFFF, + + ACCESS_SYSTEM_SECURITY = 0x01000000, + + MAXIMUM_ALLOWED = 0x02000000, + + GENERIC_READ = 0x80000000, + GENERIC_WRITE = 0x40000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_ALL = 0x10000000, + + DESKTOP_READOBJECTS = 0x00000001, + DESKTOP_CREATEWINDOW = 0x00000002, + DESKTOP_CREATEMENU = 0x00000004, + DESKTOP_HOOKCONTROL = 0x00000008, + DESKTOP_JOURNALRECORD = 0x00000010, + DESKTOP_JOURNALPLAYBACK = 0x00000020, + DESKTOP_ENUMERATE = 0x00000040, + DESKTOP_WRITEOBJECTS = 0x00000080, + DESKTOP_SWITCHDESKTOP = 0x00000100, + + WINSTA_ENUMDESKTOPS = 0x00000001, + WINSTA_READATTRIBUTES = 0x00000002, + WINSTA_ACCESSCLIPBOARD = 0x00000004, + WINSTA_CREATEDESKTOP = 0x00000008, + WINSTA_WRITEATTRIBUTES = 0x00000010, + WINSTA_ACCESSGLOBALATOMS = 0x00000020, + WINSTA_EXITWINDOWS = 0x00000040, + WINSTA_ENUMERATE = 0x00000100, + WINSTA_READSCREEN = 0x00000200, + + WINSTA_ALL_ACCESS = 0x0000037F + } + public enum InputType : uint + { + MOUSE = 0, + KEYBOARD = 1, + HARDWARE = 2 + } + public enum MessageBoxType : long + { + MB_ABORTRETRYIGNORE = 0x00000002L, + MB_CANCELTRYCONTINUE = 0x00000006L, + MB_HELP = 0x00004000L, + MB_OK = 0x00000000L, + MB_OKCANCEL = 0x00000001L, + MB_RETRYCANCEL = 0x00000005L, + MB_YESNO = 0x00000004L, + MB_YESNOCANCEL = 0x00000003L, + MB_ICONEXCLAMATION = 0x00000030L, + MB_ICONWARNING = 0x00000030L, + MB_ICONINFORMATION = 0x00000040L, + MB_ICONASTERISK = 0x00000040L, + MB_ICONQUESTION = 0x00000020L, + MB_ICONSTOP = 0x00000010L, + MB_ICONERROR = 0x00000010L, + MB_ICONHAND = 0x00000010L, + MB_DEFBUTTON1 = 0x00000000L, + MB_DEFBUTTON2 = 0x00000100L, + MB_DEFBUTTON3 = 0x00000200L, + MB_DEFBUTTON4 = 0x00000300L, + MB_APPLMODAL = 0x00000000L, + MB_SYSTEMMODAL = 0x00001000L, + MB_TASKMODAL = 0x00002000L, + MB_DEFAULT_DESKTOP_ONLY = 0x00020000L, + MB_RIGHT = 0x00080000L, + MB_RTLREADING = 0x00100000L, + MB_SETFOREGROUND = 0x00010000L, + MB_TOPMOST = 0x00040000L, + MB_SERVICE_NOTIFICATION = 0x00200000L + } + + public enum MessageBoxResult : int + { + IDABORT = 3, + IDCANCEL = 2, + IDCONTINUE = 11, + IDIGNORE = 5, + IDNO = 7, + IDOK = 1, + IDRETRY = 4, + IDTRYAGAIN = 10, + IDYES = 6, + } + public enum SW + { + SW_HIDE = 0, + SW_SHOWNORMAL = 1, + SW_NORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_SHOWMAXIMIZED = 3, + SW_MAXIMIZE = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_SHOWDEFAULT = 10, + SW_MAX = 10 + } + public enum VkMapType : uint + { + MAPVK_VK_TO_VSC = 0, + MAPVK_VSC_TO_VK = 1, + MAPVK_VK_TO_CHAR = 2, + MAPVK_VSC_TO_VK_EX = 3, + MAPVK_VK_TO_VSC_EX = 4 + } + + #endregion + + #region Structs + [StructLayout(LayoutKind.Sequential)] + public struct ICONINFO + { + public bool fIcon; + public int xHotspot; + public int yHotspot; + public IntPtr hbmMask; + public IntPtr hbmColor; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int x; + public int y; + } + + + [StructLayout(LayoutKind.Sequential)] + public struct CursorInfo + { + public int cbSize; + public int flags; + public IntPtr hCursor; + public POINT ptScreenPos; + } + [StructLayout(LayoutKind.Sequential)] + public struct INPUT + { + public InputType type; + public InputUnion U; + public static int Size + { + get { return Marshal.SizeOf(typeof(INPUT)); } + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct InputUnion + { + [FieldOffset(0)] + public MOUSEINPUT mi; + [FieldOffset(0)] + public KEYBDINPUT ki; + [FieldOffset(0)] + public HARDWAREINPUT hi; + } + [StructLayout(LayoutKind.Sequential)] + public struct MOUSEINPUT + { + public int dx; + public int dy; + public int mouseData; + public MOUSEEVENTF dwFlags; + public uint time; + public UIntPtr dwExtraInfo; + } + [StructLayout(LayoutKind.Sequential)] + public struct KEYBDINPUT + { + public VirtualKey wVk; + public ScanCodeShort wScan; + public KEYEVENTF dwFlags; + public int time; + public UIntPtr dwExtraInfo; + } + [StructLayout(LayoutKind.Sequential)] + public struct HARDWAREINPUT + { + public int uMsg; + public short wParamL; + public short wParamH; + } + #endregion + + #region DLL Imports + [DllImport("user32.dll")] + public static extern bool GetCursorInfo(out CursorInfo pci); + [DllImport("user32.dll", SetLastError = false)] + public static extern IntPtr GetDesktopWindow(); + + [DllImport("user32.dll")] + public static extern IntPtr GetCursor(); + + [DllImport("user32.dll")] + public static extern IntPtr CopyIcon(IntPtr hIcon); + + [DllImport("user32.dll")] + public static extern bool DrawIcon(IntPtr hdc, int x, int y, IntPtr hIcon); + + [DllImport("user32.dll")] + public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo); + + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] + public static extern void Mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo); + + [DllImport("user32.dll")] + public static extern void Keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetCursorPos(out System.Drawing.Point lpPoint); + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetCursorPos(int x, int y); + + [DllImport("user32.dll")] + public static extern IntPtr SetCursor(IntPtr hcursor); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern IntPtr LoadImage(IntPtr hinst, string lpszName, uint uType, + int cxDesired, int cyDesired, uint fuLoad); + + [DllImport("user32.dll")] + public static extern IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, + int nWidth, int nHeight, byte[] pvANDPlane, byte[] pvXORPlane); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool SwitchDesktop(IntPtr hDesktop); + + public delegate bool EnumDesktopsDelegate(string desktop, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern bool EnumDesktopsA(IntPtr hwinsta, EnumDesktopsDelegate lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, ACCESS_MASK dwDesiredAccess); + + public delegate bool EnumWindowStationsDelegate(string windowsStation, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern bool EnumWindowStations(EnumWindowStationsDelegate lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern IntPtr GetShellWindow(); + + public sealed class SafeWindowStationHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeWindowStationHandle() + : base(true) + { + } + + protected override bool ReleaseHandle() + { + return CloseWindowStation(handle); + + } + } + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern bool CloseWindowStation(IntPtr hWinsta); + + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern SafeWindowStationHandle OpenWindowStation([MarshalAs(UnmanagedType.LPTStr)] string lpszWinSta, [MarshalAs(UnmanagedType.Bool)] bool fInherit, ACCESS_MASK dwDesiredAccess); + + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr OpenWindowStationW([MarshalAs(UnmanagedType.LPTStr)] string lpszWinSta, [MarshalAs(UnmanagedType.Bool)] bool fInherit, ACCESS_MASK dwDesiredAccess); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool SetProcessWindowStation(IntPtr hWinSta); + + [DllImport("user32.dll")] + public static extern IntPtr GetWindowDC(IntPtr hWnd); + + public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); + + [DllImport("User32.dll")] + public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); + + [DllImport("User32.dll")] + public static extern IntPtr GetProcessWindowStation(); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool SetThreadDesktop(IntPtr hDesktop); + + [DllImport("user32.dll")] + public static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, ACCESS_MASK dwDesiredAccess); + [DllImport("user32.dll", SetLastError = true)] + public static extern bool CloseDesktop(IntPtr hDesktop); + + public delegate bool EnumDesktopWindowsDelegate(IntPtr hWnd, int lParam); + + [DllImport("user32.dll")] + public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDesktopWindowsDelegate lpfn, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetActiveWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs, int cbSize); + [DllImport("user32.dll", SetLastError = false)] + public static extern UIntPtr GetMessageExtraInfo(); + [DllImport("sas.dll")] + public static extern void SendSAS(bool AsUser); + [DllImport("user32.dll")] + public static extern bool OpenClipboard(IntPtr hWnd); + [DllImport("user32.dll")] + public static extern bool EmptyClipboard(); + [DllImport("user32.dll")] + public static extern bool CloseClipboard(); + [DllImport("user32.dll")] + public static extern IntPtr SetClipboardData(int Format, IntPtr hMem); + + [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)] + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + /* +* SystemParametersInfo( +* SPI_SETDESKWALLPAPER, 0, "filename.bmp", +* SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); +*/ + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int SystemParametersInfo( + int uAction, int uParam, string lpvParam, int fuWinIni); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool LockWorkStation(); + + [DllImport("user32.dll")] + public static extern short VkKeyScan(char ch); + + [DllImport("user32.dll")] + public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam); + + [DllImport("user32.dll", EntryPoint = "BlockInput")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int MessageBox(IntPtr hWnd, string text, string caption, long type); + + [DllImport("USER32.dll")] + public static extern short GetKeyState(VirtualKey nVirtKey); + + [DllImport("user32.dll")] + public static extern uint MapVirtualKeyEx(uint uCode, VkMapType uMapType, IntPtr dwhkl); + + [DllImport("user32.dll")] + public static extern IntPtr GetKeyboardLayout(uint threadId = 0); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool GetUserObjectInformationW(IntPtr hObj, int nIndex, + [Out] byte[] pvInfo, uint nLength, out uint lpnLengthNeeded); + #endregion +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/WTSAPI32.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/WTSAPI32.cs new file mode 100644 index 0000000..3ea95f8 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/WTSAPI32.cs @@ -0,0 +1,78 @@ +using System.Runtime.InteropServices; + +namespace Insight.Remote.Shared.Native.Windows; + +public static class WTSAPI32 +{ + public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; + + public enum WTS_CONNECTSTATE_CLASS + { + WTSActive, + WTSConnected, + WTSConnectQuery, + WTSShadow, + WTSDisconnected, + WTSIdle, + WTSListen, + WTSReset, + WTSDown, + WTSInit + } + + public enum WTS_INFO_CLASS + { + WTSInitialProgram, + WTSApplicationName, + WTSWorkingDirectory, + WTSOEMId, + WTSSessionId, + WTSUserName, + WTSWinStationName, + WTSDomainName, + WTSConnectState, + WTSClientBuildNumber, + WTSClientName, + WTSClientDirectory, + WTSClientProductId, + WTSClientHardwareId, + WTSClientAddress, + WTSClientDisplay, + WTSClientProtocolType, + WTSIdleTime, + WTSLogonTime, + WTSIncomingBytes, + WTSOutgoingBytes, + WTSIncomingFrames, + WTSOutgoingFrames, + WTSClientInfo, + WTSSessionInfo + } + + + [DllImport("wtsapi32.dll", SetLastError = true)] + public static extern int WTSEnumerateSessions( + IntPtr hServer, + int Reserved, + int Version, + ref IntPtr ppSessionInfo, + ref int pCount); + + [DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)] + public static extern void WTSFreeMemory(IntPtr memory); + + [DllImport("Wtsapi32.dll")] + public static extern bool WTSQuerySessionInformation(IntPtr hServer, uint sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out uint pBytesReturned); + + [DllImport("wtsapi32.dll", SetLastError = true)] + static extern IntPtr WTSOpenServer(string pServerName); + + [StructLayout(LayoutKind.Sequential)] + public struct WTS_SESSION_INFO + { + public uint SessionID; + [MarshalAs(UnmanagedType.LPStr)] + public string pWinStationName; + public WTS_CONNECTSTATE_CLASS State; + } +} diff --git a/src/Remote/Insight.Remote.Shared/Native/Windows/Win32Interop.cs b/src/Remote/Insight.Remote.Shared/Native/Windows/Win32Interop.cs new file mode 100644 index 0000000..05db7f5 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Native/Windows/Win32Interop.cs @@ -0,0 +1,285 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Text; +using static Insight.Remote.Shared.Native.Windows.ADVAPI32; +using static Insight.Remote.Shared.Native.Windows.User32; + +namespace Insight.Remote.Shared.Native.Windows; + +// TODO: Use https://github.com/dotnet/pinvoke for all p/invokes. Remove signatures from this project. +public class Win32Interop +{ + private static IntPtr _lastInputDesktop; + + public static List GetActiveSessions() + { + var sessions = new List(); + var consoleSessionId = Kernel32.WTSGetActiveConsoleSessionId(); + sessions.Add(new WindowsSessionDto() + { + Id = consoleSessionId, + Type = WindowsSessionType.Console, + Name = "Console", + Username = GetUsernameFromSessionId(consoleSessionId) + }); + + IntPtr ppSessionInfo = IntPtr.Zero; + var count = 0; + var enumSessionResult = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count); + var dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO)); + var current = ppSessionInfo; + + if (enumSessionResult != 0) + { + for (int i = 0; i < count; i++) + { + var wtsInfo = Marshal.PtrToStructure(current, typeof(WTSAPI32.WTS_SESSION_INFO)); + if (wtsInfo is null) + { + continue; + } + var sessionInfo = (WTSAPI32.WTS_SESSION_INFO)wtsInfo; + current += dataSize; + if (sessionInfo.State == WTSAPI32.WTS_CONNECTSTATE_CLASS.WTSActive && sessionInfo.SessionID != consoleSessionId) + { + + sessions.Add(new WindowsSessionDto() + { + Id = sessionInfo.SessionID, + Name = sessionInfo.pWinStationName, + Type = WindowsSessionType.RDP, + Username = GetUsernameFromSessionId(sessionInfo.SessionID) + }); + } + } + } + + return sessions; + } + + public static string GetCommandLine() + { + var commandLinePtr = Kernel32.GetCommandLine(); + return Marshal.PtrToStringAuto(commandLinePtr) ?? string.Empty; + } + + public static bool GetCurrentDesktop(out string desktopName) + { + var inputDesktop = OpenInputDesktop(); + try + { + byte[] deskBytes = new byte[256]; + if (!GetUserObjectInformationW(inputDesktop, UOI_NAME, deskBytes, 256, out uint lenNeeded)) + { + desktopName = string.Empty; + return false; + } + + desktopName = Encoding.Unicode.GetString(deskBytes.Take((int)lenNeeded).ToArray()).Replace("\0", ""); + return true; + } + finally + { + CloseDesktop(inputDesktop); + } + } + + public static string GetUsernameFromSessionId(uint sessionId) + { + var username = string.Empty; + + if (WTSAPI32.WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTSAPI32.WTS_INFO_CLASS.WTSUserName, out var buffer, out var strLen) && strLen > 1) + { + username = Marshal.PtrToStringAnsi(buffer); + WTSAPI32.WTSFreeMemory(buffer); + } + + return username ?? string.Empty; + } + + public static IntPtr OpenInputDesktop() + { + return User32.OpenInputDesktop(0, true, ACCESS_MASK.GENERIC_ALL); + } + + public static bool CreateInteractiveSystemProcess( + string commandLine, + int targetSessionId, + bool forceConsoleSession, + string desktopName, + bool hiddenWindow, + out PROCESS_INFORMATION procInfo) + { + uint winlogonPid = 0; + var hUserTokenDup = IntPtr.Zero; + var hPToken = IntPtr.Zero; + var hProcess = IntPtr.Zero; + + procInfo = new PROCESS_INFORMATION(); + + // If not force console, find target session. If not present, + // use last active session. + var dwSessionId = Kernel32.WTSGetActiveConsoleSessionId(); + if (!forceConsoleSession) + { + var activeSessions = GetActiveSessions(); + if (activeSessions.Any(x => x.Id == targetSessionId)) + { + dwSessionId = (uint)targetSessionId; + } + else + { + dwSessionId = activeSessions.Last().Id; + } + } + + // Obtain the process ID of the winlogon process that is running within the currently active session. + var processes = Process.GetProcessesByName("winlogon"); + foreach (Process p in processes) + { + if ((uint)p.SessionId == dwSessionId) + { + winlogonPid = (uint)p.Id; + } + } + + // Obtain a handle to the winlogon process. + hProcess = Kernel32.OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid); + + // Obtain a handle to the access token of the winlogon process. + if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken)) + { + Kernel32.CloseHandle(hProcess); + return false; + } + + // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser. + var sa = new SECURITY_ATTRIBUTES(); + sa.Length = Marshal.SizeOf(sa); + + // Copy the access token of the winlogon process; the newly created token will be a primary token. + if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out hUserTokenDup)) + { + Kernel32.CloseHandle(hProcess); + Kernel32.CloseHandle(hPToken); + return false; + } + + // By default, CreateProcessAsUser creates a process on a non-interactive window station, meaning + // the window station has a desktop that is invisible and the process is incapable of receiving + // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user + // interaction with the new process. + var si = new STARTUPINFO(); + si.cb = Marshal.SizeOf(si); + si.lpDesktop = @"winsta0\" + desktopName; + + // Flags that specify the priority and creation method of the process. + uint dwCreationFlags; + if (hiddenWindow) + { + dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = 0; + } + else + { + dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE; + } + + // Create a new process in the current user's logon session. + var result = CreateProcessAsUser( + hUserTokenDup, + null, + commandLine, + ref sa, + ref sa, + false, + dwCreationFlags, + IntPtr.Zero, + null, + ref si, + out procInfo); + + // Invalidate the handles. + Kernel32.CloseHandle(hProcess); + Kernel32.CloseHandle(hPToken); + Kernel32.CloseHandle(hUserTokenDup); + + return result; + } + + public static void SetMonitorState(MonitorState state) + { + SendMessage(0xFFFF, 0x112, 0xF170, (int)state); + } + + public static MessageBoxResult ShowMessageBox(IntPtr owner, + string message, + string caption, + MessageBoxType messageBoxType) + { + return (MessageBoxResult)MessageBox(owner, message, caption, (long)messageBoxType); + } + + public static bool SwitchToInputDesktop() + { + try + { + CloseDesktop(_lastInputDesktop); + var inputDesktop = OpenInputDesktop(); + + if (inputDesktop == IntPtr.Zero) + { + return false; + } + + var result = SetThreadDesktop(inputDesktop) && SwitchDesktop(inputDesktop); + _lastInputDesktop = inputDesktop; + return result; + } + catch + { + return false; + } + } + + public static void SetConsoleWindowVisibility(bool isVisible) + { + var handle = Kernel32.GetConsoleWindow(); + + if (isVisible) + { + ShowWindow(handle, (int)SW.SW_SHOW); + } + else + { + ShowWindow(handle, (int)SW.SW_HIDE); + } + + Kernel32.CloseHandle(handle); + } + + [DataContract] + public class WindowsSessionDto + { + [DataMember(Name = "ID")] + public uint Id { get; set; } + + [DataMember(Name = "Name")] + public string Name { get; set; } = string.Empty; + + [DataMember(Name = "Type")] + public WindowsSessionType Type { get; set; } + + [DataMember(Name = "Username")] + public string Username { get; set; } = string.Empty; + } + + [DataContract] + public enum WindowsSessionType + { + Console = 1, + RDP = 2 + } +} diff --git a/src/Remote/Insight.Remote.Shared/Network/Handlers/RemoteHandler.cs b/src/Remote/Insight.Remote.Shared/Network/Handlers/RemoteHandler.cs new file mode 100644 index 0000000..dd59d99 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Network/Handlers/RemoteHandler.cs @@ -0,0 +1,154 @@ +using Insight.Domain.Enums; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Remote.Messages; +using Insight.Remote.Shared.Messages; +using Insight.Remote.Shared.Services; +using Microsoft.Extensions.Logging; +using Vaitr.Bus; + +namespace Insight.Remote.Shared.Network.Handlers; + +public class RemoteHandler : IMessageHandler +{ + private readonly Bus _bus; + private readonly Streamer _streamer; + private readonly ILogger _logger; + + public RemoteHandler(Bus bus, Streamer streamer, ILogger logger) + { + _bus = bus; + _streamer = streamer; + _logger = logger; + } + + public async ValueTask HandleAsync(RemoteSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + if (message is RemoteSessionResponse sessionResponse) + { + await OnSessionResponse(sender, sessionResponse, cancellationToken); + } + else if (message is CastRequest castRequest) + { + await OnCastRequest(sender, castRequest, cancellationToken); + } + else if (message is CastAbort castAbort) + { + await OnCastAbort(sender, castAbort, cancellationToken); + } + else if (message is CastScreenReceived screenDataReceived) + { + await OnScreenDataReceived(sender, screenDataReceived, cancellationToken); + } + else if (message is CastCursorReceived cursorCallbackData) + { + await OnCursorDataReceived(sender, cursorCallbackData, cancellationToken); + } + } + + private async Task OnSessionResponse(RemoteSession session, RemoteSessionResponse sessionResponse, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {session.Id} => SessionResponse"); + + session.Id = sessionResponse.SessionId; + + await _bus.PublishAsync(new IdentityChanged(session.Id)); + } + + private async Task OnCastRequest(RemoteSession session, CastRequest castRequest, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {session.Id} => CastRequest"); + + //if (request.RequesterName is null || request.ConnectionId is null) return; + + var permitCast = false; + + switch (castRequest.Mode) + { + case RemoteControlMode.Unattended: + { + if (session.AccessKey == castRequest.AccessKey) permitCast = true; + break; + } + case RemoteControlMode.Attended: + { + // timeout source + var waiter = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + // subscribe token for demand response + using var response = _bus.Subscribe((message) => + { + if (message.Accepted is false) return; + + permitCast = true; + waiter.Cancel(); + }, null); + + // send internal demand request + await _bus.PublishAsync(castRequest, cancellationToken); + + // wait for timeout async + try { await Task.Delay(TimeSpan.FromSeconds(5), waiter.Token); } + catch (TaskCanceledException) { } + break; + } + } + + try + { + var response = new CastRequestResponse(castRequest) + { + Accepted = permitCast + }; + + await session.SendAsync(response, cancellationToken); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + + //DesktopStream? viewer = _viewerPool.Values.FirstOrDefault(p => p.ConnectionId == request.ConnectionId); + + //if (viewer is null) + //{ + // viewer = _viewerFactory.Create(); + // _viewerPool.AddOrUpdate(viewer.Uid, viewer, (uid, v) => viewer); + //} + + _ = InitViewerSession(castRequest, cancellationToken); + } + + private async Task OnCastAbort(RemoteSession session, CastAbort castAbort, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {session.Id} => CastAbort"); + + await _streamer.CancelAsync(castAbort, cancellationToken); + } + + private async Task OnScreenDataReceived(RemoteSession session, CastScreenReceived screenDataReceived, CancellationToken cancellationToken) + { + //_logger.LogInformation($"Remote {session.Id} => ScreenDataReceived"); + + await _streamer.FrameReceivedAsync(screenDataReceived, cancellationToken); + } + + private async Task OnCursorDataReceived(RemoteSession session, CastCursorReceived cursorCallbackData, CancellationToken cancellationToken) + { + //_logger.LogInformation($"Remote {session.Id} => ScreenDataReceived"); + + await _streamer.CursorReceivedAsync(cursorCallbackData, cancellationToken); + } + + private async Task InitViewerSession(CastRequest castRequest, CancellationToken cancellationToken) + { + try + { + await _streamer.InitAsync(castRequest, cancellationToken); + } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Network/RemoteSession.cs b/src/Remote/Insight.Remote.Shared/Network/RemoteSession.cs new file mode 100644 index 0000000..181ac6c --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Network/RemoteSession.cs @@ -0,0 +1,103 @@ +using Insight.Domain.Enums; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Remote.Messages; +using Insight.Remote.Shared.Messages; +using Microsoft.Extensions.Logging; +using System.ServiceProcess; +using Vaitr.Bus; +using Vaitr.Network; + +namespace Insight.Remote.Shared.Network; + +public class RemoteSession : TcpSession +{ + public string? Id { get; set; } + public string? AccessKey { get; set; } + public RemoteControlMode Mode { get; } = RunningAsService() ? RemoteControlMode.Unattended : RemoteControlMode.Attended; + + private readonly Bus _bus; + private readonly IEnumerable> _handlers; + + public RemoteSession(Bus bus, IEnumerable> handlers, ISerializer serializer, ILogger logger) : base(serializer, logger) + { + _bus = bus; + _handlers = handlers; + } + + protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) connected", RemoteEndPoint); + + await _bus.PublishAsync(new ConnectionStateChanged(ConnectionState.Connected), cancellationToken); + + await SendAsync(new RemoteSessionRequest + { + Hostname = "Debug Host", + Mode = RemoteControlMode.Attended + }, cancellationToken); + } + + protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) disconnected", RemoteEndPoint); + + await _bus.PublishAsync(new ConnectionStateChanged(ConnectionState.Disconnected)); + } + + protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) + { + //await base.OnSentAsync(context, cancellationToken); + } + + protected override async ValueTask OnReceivedAsync(IPacketContext 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 async ValueTask OnHeartbeatAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) Heartbeat", RemoteEndPoint); + } + + private async ValueTask OnWindowsSessionSwitchedAsync(WindowsSessionSwitched message, CancellationToken cancellationToken) + { + try + { + //await SendAsync(new NotifySessionChanged(message.SessionId, message.Reason), cancellationToken); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + + private async ValueTask OnWindowsSessionEndingAsync(WindowsSessionEnding message, CancellationToken cancellationToken) + { + //await DisconnectViewersAsync(); + } + + private static bool RunningAsService() + { + return false; + + if (OperatingSystem.IsWindows()) + { + return ServiceController.GetServices().Any(serviceController => serviceController.ServiceName.Equals("remotecontrol")); + } + + return false; + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Reactive/AsyncRelayCommand.cs b/src/Remote/Insight.Remote.Shared/Reactive/AsyncRelayCommand.cs new file mode 100644 index 0000000..d6be6c0 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Reactive/AsyncRelayCommand.cs @@ -0,0 +1,96 @@ +using System.Windows.Input; + +namespace Insight.Remote.Shared.Reactive; + +public class AsyncRelayCommand : ICommand +{ + private readonly Func _canExecute; + private readonly Func _execute; + public AsyncRelayCommand(Func execute) + { + _execute = execute; + _canExecute = () => true; + } + + public AsyncRelayCommand(Func execute, Func canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler? CanExecuteChanged; + public bool CanExecute(object? parameter) + { + return _canExecute.Invoke(); + } + + public void Execute(object? parameter) + { + _execute.Invoke(); + } + + public void NotifyCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } +} + +public class AsyncRelayCommand : ICommand +{ + private readonly Func _canExecute; + private readonly Func _execute; + + public AsyncRelayCommand(Func execute) + { + _execute = execute; + _canExecute = (parameter) => true; + } + + public AsyncRelayCommand(Func execute, Func canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler? CanExecuteChanged; + + public bool CanExecute(object? parameter) + { + if (parameter is null) + { + return _canExecute.Invoke(default); + } + + if (parameter is not T typedParam) + { + throw new InvalidOperationException("Paramter is not of the correct type."); + } + + return _canExecute.Invoke(typedParam); + } + + // Async void is una*void*able here (heh, heh) due to ICommand's interface. + // Though we shouldn't need to in modern .NET, we're handling UnobservedTaskException + // in IServiceProviderExtensions.UseRemoteControl. In older versions of .NET, this + // would have been required to prevent the app from terminating. + public async void Execute(object? parameter) + { + if (parameter is null) + { + await _execute.Invoke(default); + return; + } + + if (parameter is not T typedParam) + { + throw new InvalidOperationException("Paramter is not of the correct type."); + } + + await _execute.Invoke(typedParam); + } + + public void NotifyCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } +} diff --git a/src/Remote/Insight.Remote.Shared/Reactive/ObservableObject.cs b/src/Remote/Insight.Remote.Shared/Reactive/ObservableObject.cs new file mode 100644 index 0000000..db2edd8 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Reactive/ObservableObject.cs @@ -0,0 +1,45 @@ +using System.Collections.Concurrent; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Insight.Remote.Shared.Reactive; + +public class ObservableObject : INotifyPropertyChanged +{ + private readonly ConcurrentDictionary _backingFields = new(); + + public event PropertyChangedEventHandler? PropertyChanged; + + public void NotifyPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected T? Get([CallerMemberName] string propertyName = "") + { + if (_backingFields.TryGetValue(propertyName, out var value) && + value is T typedValue) + { + return typedValue; + } + + return default; + } + + protected T Get(T defaultValue, [CallerMemberName] string propertyName = "") + { + if (_backingFields.TryGetValue(propertyName, out var value) && + value is T typedValue) + { + return typedValue; + } + + return defaultValue; + } + + protected void Set(T newValue, [CallerMemberName] string propertyName = "") + { + _backingFields.AddOrUpdate(propertyName, newValue, (k, v) => newValue); + NotifyPropertyChanged(propertyName); + } +} diff --git a/src/Remote/Insight.Remote.Shared/Reactive/RelayCommand.cs b/src/Remote/Insight.Remote.Shared/Reactive/RelayCommand.cs new file mode 100644 index 0000000..dcd449e --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Reactive/RelayCommand.cs @@ -0,0 +1,87 @@ +using System.Windows.Input; + +namespace Insight.Remote.Shared.Reactive; + +public class RelayCommand : ICommand +{ + private readonly Func _canExecute; + private readonly Action _execute; + public RelayCommand(Action execute) + { + _execute = execute; + _canExecute = () => true; + } + + public RelayCommand(Action execute, Func canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler? CanExecuteChanged; + public bool CanExecute(object? parameter) + { + return _canExecute.Invoke(); + } + + public void Execute(object? parameter) + { + _execute.Invoke(); + } +} + +public class RelayCommand : ICommand +{ + private readonly Func _canExecute; + private readonly Action _execute; + + public RelayCommand(Action execute) + { + _execute = execute; + _canExecute = (parameter) => true; + } + + public RelayCommand(Action execute, Func canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler? CanExecuteChanged; + + public bool CanExecute(object? parameter) + { + if (parameter is null) + { + return _canExecute.Invoke(default); + } + + if (parameter is not T typedParam) + { + throw new InvalidOperationException("Paramter is not of the correct type."); + } + + return _canExecute.Invoke(typedParam); + } + + public void Execute(object? parameter) + { + if (parameter is null) + { + _execute.Invoke(default); + return; + } + + if (parameter is not T typedParam) + { + throw new InvalidOperationException("Paramter is not of the correct type."); + } + + _execute.Invoke(typedParam); + } + + public void NotifyCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } +} diff --git a/src/Remote/Insight.Remote.Shared/Services/Runtime.cs b/src/Remote/Insight.Remote.Shared/Services/Runtime.cs new file mode 100644 index 0000000..5715875 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Services/Runtime.cs @@ -0,0 +1,68 @@ +using Insight.Domain.Enums; +using Insight.Remote.Shared.Abstractions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Insight.Remote.Shared.Services; + +public class Runtime : IHostedService +{ + private readonly CancellationTokenSource _cts = new(); + + private Task? _desktopTask; + private Thread? _uiThread; + + private readonly IDesktopApp _desktopApp; + private readonly IDispatcher _dispatcher; + private readonly IHostApplicationLifetime _lifetime; + private readonly ILogger _logger; + + public Runtime( + IDesktopApp desktopApp, + IDispatcher dispatcher, + IHostApplicationLifetime lifetime, + ILogger logger) + { + _desktopApp = desktopApp; + _dispatcher = dispatcher; + _lifetime = lifetime; + _logger = logger; + + _lifetime.ApplicationStopping.Register(_cts.Cancel); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + // init app (os specific providers) + _desktopTask = _desktopApp.InitAsync(RemoteControlMode.Attended, _cts.Token); + + // init dispatcher + if (false) // if mode is attended (interactive) + { + // init ui thread + _uiThread = await _dispatcher.RunAsync(_cts.Token); + } + + // todo + // report unattended accesskey to api ($"https://localhost:7024?mode=Unattended&sessionId={appState.SessionId}&accessKey={appState.AccessKey}") + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + _cts.Cancel(); + + if (_desktopTask is not null) await _desktopTask; + + if (_uiThread is not null) + { + await _dispatcher.ShutdownAsync(); + + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)); + + while (_uiThread.ThreadState == ThreadState.Running) + { + await Task.Delay(100, timeout.Token); + } + } + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/Services/Streamer.cs b/src/Remote/Insight.Remote.Shared/Services/Streamer.cs new file mode 100644 index 0000000..0f218a2 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Services/Streamer.cs @@ -0,0 +1,362 @@ +using Insight.Domain.Network; +using Insight.Domain.Network.Remote.Messages; +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Extensions; +using Insight.Remote.Shared.Models; +using Insight.Remote.Shared.Network; +using Microsoft.Extensions.Logging; +using Microsoft.IO; +using SkiaSharp; +using System.Collections.Concurrent; +using System.Diagnostics; +using Vaitr.Bus; +using Vaitr.Network; + +namespace Insight.Remote.Shared.Services; + +public class Streamer : IDisposable +{ + public int ImageQuality { get; set; } = 50; + public bool HasControl { get; set; } = true; + public bool HasAudio { get; set; } = false; + public bool IsResponsive { get; private set; } = true; + public double CurrentFps { get; private set; } + public double CurrentMbps { get; private set; } + public TimeSpan? RoundTripLatency { get; private set; } + + private CancellationTokenSource? _cts; + private volatile int _framesSentSinceLastReceipt; + private DateTimeOffset _lastFrameReceived = DateTimeOffset.Now; + private DateTimeOffset _lastFrameSent = DateTimeOffset.Now; + private uint _pingFailures = 0; + + private readonly RecyclableMemoryStreamManager _recycleStreams = new(); + private readonly ConcurrentQueue _fpsQueue = new(); + private readonly ConcurrentQueue _sentFrames = new(); + + private readonly Bus _bus; + private readonly ISessionPool _remotePool; + private readonly IFileProvider _fileProvider; + private readonly IClipboardProvider _clipboardProvider; + private readonly IAudioProvider _audioProvider; + private readonly IInputProvider _inputProvider; + private readonly ICursorProvider _cursorProvider; + private readonly IScreenProvider _screenProvider; + private readonly ILogger _logger; + + public Streamer( + Bus bus, + ISessionPool remotePool, + IFileProvider fileProvider, + IClipboardProvider clipboardProvider, + IAudioProvider audioProvider, + IInputProvider inputProvider, + ICursorProvider cursorProvider, + IScreenProvider screenProvider, + ILogger logger) + { + _bus = bus; + _remotePool = remotePool; + _fileProvider = fileProvider; + _clipboardProvider = clipboardProvider; + _audioProvider = audioProvider; + _inputProvider = inputProvider; + _cursorProvider = cursorProvider; + _screenProvider = screenProvider; + _logger = logger; + } + + public async Task InitAsync(CastRequest request, CancellationToken cancellationToken) + { + _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + if (_remotePool.FirstOrDefault().Value is not RemoteSession remoteSession) return; + + //using var audioToken = _bus.SubscribeAsync(async (message, token) => await OnAudioSampleReadyAsync(remoteSession, message, stop.Token), null); + //using var clipboardToken = _bus.SubscribeAsync(async (message, token) => await OnClipboardChangedAsync(remoteSession, message, stop.Token), null); + + //using var sessionSwitchToken = _bus.Subscribe((x) => stop?.Cancel(), null); + //using var sessionEndingToken = _bus.Subscribe((x) => stop?.Cancel(), null); + + try + { + // send initial display informations + var screenBounds = _screenProvider.CurrentScreenBounds; + + await remoteSession.SendAsync(new CastDisplay + { + Id = remoteSession.Id, + DisplayNames = _screenProvider.GetDisplayNames(), + SelectedDisplay = _screenProvider.SelectedScreen, + ScreenWidth = screenBounds.Width, + ScreenHeight = screenBounds.Height, + MachineName = Environment.MachineName + }, cancellationToken); + + //if (OperatingSystem.IsWindows()) + //{ + // await SendChunkedAsync(new WindowsSessionsDto(Win32Interop.GetActiveSessions()), DtoType.WindowsSessions, stop.Token); + //} + + // start => wait for async stream tasks + await Task.WhenAll( + StreamMetricAsync(remoteSession, _cts.Token), + StreamScreenAsync(remoteSession, _cts.Token), + StreamCursorAsync(remoteSession, _cts.Token) + ); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + finally + { + _logger.LogInformation("Viewer Disconnected ({responsive})", IsResponsive); + _cts?.Cancel(); + + IsResponsive = true; + } + } + + private async Task StreamMetricAsync(RemoteSession remoteSession, CancellationToken cancellationToken) + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1000)); + + while (await timer.WaitForNextTickAsync(cancellationToken) && IsResponsive) + { + // calculate mbps + if (_sentFrames.IsEmpty) CurrentMbps = 0; + else if (_sentFrames.Count == 1) CurrentMbps = _sentFrames.First().FrameSize / 1024 / 1024 * 8; + else if (_sentFrames.Count > 1) CurrentMbps = (double)_sentFrames.Sum(x => x.FrameSize) / 1024 / 1024 * 8 / (_sentFrames.Last().Timestamp - _sentFrames.First().Timestamp).TotalSeconds; + + _sentFrames.Clear(); + + // calculate fps + if (_fpsQueue.IsEmpty) CurrentFps = 0; + else CurrentFps = _fpsQueue.Count / (_fpsQueue.Last() - _fpsQueue.First()).TotalSeconds; + + _fpsQueue.Clear(); + + // calculate latency + try + { + var tick = Stopwatch.GetTimestamp(); + + // new one + //var result = await _client.Connection.InvokeAsync(nameof(PingViewer), new PingViewer(ConnectionId), cancellationToken); + + RoundTripLatency = Stopwatch.GetElapsedTime(tick); + IsResponsive = true; + _pingFailures = 0; + } + catch (Exception) + { + RoundTripLatency = null; + IsResponsive = _pingFailures > 3; + _pingFailures++; + } + + await remoteSession.SendAsync(new CastMetric + { + Id = remoteSession.Id, + Timestamp = DateTimeOffset.Now, + Mbps = CurrentMbps, + Fps = CurrentFps, + RTT = RoundTripLatency.HasValue ? RoundTripLatency.Value.TotalMilliseconds : 0, + }, cancellationToken); + } + } + + private async Task StreamScreenAsync(RemoteSession remoteSession, CancellationToken cancellationToken) + { + var init = true; + var errors = 0; + + while (cancellationToken.IsCancellationRequested is false && IsResponsive) + { + if (errors >= 3) + { + IsResponsive = false; + break; + } + + // Prevent publisher from overwhelming consumer bewteen receipts. + var synced = await WaitForAsync(() => _framesSentSinceLastReceipt < 10, TimeSpan.FromSeconds(3), 10, cancellationToken); + + // Prevent viewer from getting too far behind. + synced &= await WaitForAsync(() => _lastFrameSent - _lastFrameReceived < TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), 10, cancellationToken); + + if (synced is false) + { + _logger.LogWarning("Viewer is behind on frames and did not catch up in time."); + + errors++; + continue; + } + + var frameResult = await _screenProvider.GetNextFrameAsync(cancellationToken); + if (frameResult.Frame is null || frameResult.Error) + { + // consider message viewer to reconnect or doing screenProvider rewrite to dispatch on seperate thread + await Task.Delay(1000, cancellationToken); + continue; + } + else if (frameResult.Changed is false) + { + await Task.Delay(20, cancellationToken); + continue; + } + + var diffArea = await _screenProvider.GetDiffAreaAsync(init, cancellationToken); + if (diffArea.IsEmpty) + { + await Task.Delay(20, cancellationToken); + continue; + } + + using var croppedFrame = frameResult.Frame.CropBitmap(diffArea); + + var encodedImageBytes = croppedFrame.EncodeBitmap(SKEncodedImageFormat.Jpeg, ImageQuality); + if (encodedImageBytes.Length == 0) + { + await Task.Delay(20, cancellationToken); + continue; + } + + Interlocked.Increment(ref _framesSentSinceLastReceipt); + + var frame = new SentFrame(encodedImageBytes.Length, DateTimeOffset.Now); + _lastFrameSent = frame.Timestamp; + _sentFrames.Enqueue(frame); + _fpsQueue.Enqueue(DateTimeOffset.Now); + + init = false; + + var screenBounds = _screenProvider.CurrentScreenBounds; + + await remoteSession.SendAsync(new CastScreen + { + Id = remoteSession.Id, + Timestamp = _lastFrameSent, + ViewWidth = screenBounds.Width, + ViewHeight = screenBounds.Height, + Left = diffArea.Left, + Top = diffArea.Top, + Width = diffArea.Width, + Height = diffArea.Height, + Image = encodedImageBytes, + }, cancellationToken); + } + } + + private async Task StreamCursorAsync(RemoteSession remoteSession, CancellationToken cancellationToken) + { + CastCursor? cacheCursor = null; + + while (cancellationToken.IsCancellationRequested is false && IsResponsive) + { + try + { + var cursor = await _cursorProvider.GetAsync(cancellationToken); + if (cursor is null || cursor == cacheCursor) continue; + + cursor.Id = remoteSession.Id; + + await remoteSession.SendAsync(cursor, cancellationToken); + cacheCursor = cursor; + } + catch (Exception ex) + { + + } + finally + { + await Task.Delay(20, cancellationToken); + } + } + } + + //public async Task SendFileAsync(FileUpload fileUpload, Action progressUpdateCallback, CancellationToken cancellationToken) + //{ + // try + // { + // var messageId = Guid.NewGuid().ToString(); + // var fileDto = new FileDto() + // { + // EndOfFile = false, + // FileName = fileUpload.DisplayName, + // MessageId = messageId, + // StartOfFile = true + // }; + + // await SendChunkedAsync(fileDto, DtoType.File, cancellationToken); + + // using var fs = File.OpenRead(fileUpload.FilePath); + // using var br = new BinaryReader(fs); + // while (fs.Position < fs.Length) + // { + // if (cancellationToken.IsCancellationRequested) + // { + // return; + // } + + // fileDto = new FileDto() + // { + // Buffer = br.ReadBytes(40_000), + // FileName = fileUpload.DisplayName, + // MessageId = messageId + // }; + + // await SendChunkedAsync(fileDto, DtoType.File, cancellationToken); + + // progressUpdateCallback((double)fs.Position / fs.Length); + // } + + // fileDto = new FileDto() + // { + // EndOfFile = true, + // FileName = fileUpload.DisplayName, + // MessageId = messageId, + // StartOfFile = false + // }; + + // await SendChunkedAsync(fileDto, DtoType.File, cancellationToken); + + // progressUpdateCallback(1); + // } + // catch (Exception ex) + // { + // _logger.LogError(ex, "Error while sending file."); + // } + //} + + public async Task CancelAsync(CastAbort castAbort, CancellationToken cancellationToken) + { + _cts?.Cancel(); + } + + public async Task FrameReceivedAsync(CastScreenReceived screenDataReceived, CancellationToken cancellationToken) + { + _lastFrameReceived = screenDataReceived.Timestamp.Value.ToLocalTime(); + _framesSentSinceLastReceipt = 0; + } + + public async Task CursorReceivedAsync(CastCursorReceived cursorCallbackData, CancellationToken cancellationToken) + { + await _cursorProvider.SetAsync(cursorCallbackData.X, cursorCallbackData.Y, cancellationToken); + } + + private static async Task WaitForAsync(Func condition, TimeSpan timeout, int pollingMs = 10, CancellationToken cancellationToken = default) + { + var ts = Stopwatch.GetTimestamp(); + + while (!condition() && Stopwatch.GetElapsedTime(ts) < timeout) await Task.Delay(pollingMs, cancellationToken); + return condition(); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Shared/ViewModels/FileUpload.cs b/src/Remote/Insight.Remote.Shared/ViewModels/FileUpload.cs new file mode 100644 index 0000000..a5c756b --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/ViewModels/FileUpload.cs @@ -0,0 +1,23 @@ +using Insight.Remote.Shared.Reactive; + +namespace Insight.Remote.Shared.ViewModels; + +public partial class FileUpload : ObservableObject +{ + public string FilePath + { + get => Get(defaultValue: string.Empty); + set => Set(value); + } + + + public double PercentProgress + { + get => Get(); + set => Set(value); + } + + public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource(); + + public string DisplayName => Path.GetFileName(FilePath); +} diff --git a/src/Remote/Insight.Remote.Windows/Insight.Remote.Windows.csproj b/src/Remote/Insight.Remote.Windows/Insight.Remote.Windows.csproj new file mode 100644 index 0000000..3ec6630 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Insight.Remote.Windows.csproj @@ -0,0 +1,37 @@ + + + + Exe + net7.0-windows + true + true + Remote Control + remote + 2023.12.14.0 + Insight.Remote.Windows + enable + enable + True + + + + + + + + + + + + + + + + $(DefaultXamlRuntime) + + + $(DefaultXamlRuntime) + + + + diff --git a/src/Remote/Insight.Remote.Windows/Models/DirectXOutput.cs b/src/Remote/Insight.Remote.Windows/Models/DirectXOutput.cs new file mode 100644 index 0000000..ccec816 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Models/DirectXOutput.cs @@ -0,0 +1,36 @@ +using Insight.Remote.Shared.Helpers; +using SharpDX.Direct3D11; +using SharpDX.DXGI; + +namespace Insight.Remote.Windows.Models; + +public class DirectXOutput : IDisposable +{ + public DirectXOutput(Adapter1 adapter, + SharpDX.Direct3D11.Device device, + OutputDuplication outputDuplication, + Texture2D texture2D, + DisplayModeRotation rotation) + { + Adapter = adapter; + Device = device; + OutputDuplication = outputDuplication; + Texture2D = texture2D; + Rotation = rotation; + Bounds = new Rectangle(0, 0, texture2D.Description.Width, texture2D.Description.Height); + } + + public Adapter1 Adapter { get; } + public Rectangle Bounds { get; set; } + public SharpDX.Direct3D11.Device Device { get; } + public OutputDuplication OutputDuplication { get; } + public DisplayModeRotation Rotation { get; } + public Texture2D Texture2D { get; } + + public void Dispose() + { + OutputDuplication.ReleaseFrame(); + Disposer.TryDisposeAll(OutputDuplication, Texture2D, Adapter, Device); + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Program.cs b/src/Remote/Insight.Remote.Windows/Program.cs new file mode 100644 index 0000000..7b15e5a --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Program.cs @@ -0,0 +1,77 @@ +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Extensions; +using Insight.Remote.Shared.Network; +using Insight.Remote.Shared.Network.Handlers; +using Insight.Remote.Windows.Services; +using Insight.Remote.Windows.ViewModels; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Vaitr.Network; +using Vaitr.Network.Hosting; + +namespace Insight.Remote.Windows; + +internal class Program +{ + public static async Task Main(string[] args) + { + var builder = Host.CreateDefaultBuilder(args); + builder.UseWindowsService(); + + builder.ConfigureLogging(logger => + { + logger.ClearProviders(); + logger.SetMinimumLevel(LogLevel.Trace); + logger.AddSimpleConsole(options => + { + options.IncludeScopes = false; + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; + }); + + logger.AddFilter("Microsoft", LogLevel.None); + }); + + builder.ConfigureServices(services => + { + // CORE + services.AddRemoteControlServices(options => + { + options.Server = new Uri("https://localhost:7024"); + }); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // NETWORKING + services.UseHostedClient(options => + { + options.Host = "127.0.0.1"; + options.Port = 3003; + options.Keepalive = 10000; + options.Timeout = 30000; + options.Encryption = Encryption.Tls12; + options.Compression = true; + + options.UseSerializer>(); + }); + + services.AddSingleton(); + services.AddSingleton, RemoteHandler>(); + }); + + var host = builder.Build(); + await host.RunAsync(); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Properties/launchSettings.json b/src/Remote/Insight.Remote.Windows/Properties/launchSettings.json new file mode 100644 index 0000000..1f711eb --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Attended": { + "commandName": "Project", + "commandLineArgs": "" + }, + "Unattended": { + "commandName": "Project", + "commandLineArgs": "-m Unattended -s deb9a957-9c88-4705-b58e-b9f20b1e64f0 -a vERyLonGAndCOMpleXKeY -o Immense -r Han" + } + } +} diff --git a/src/Remote/Insight.Remote.Windows/Resources/Styles.xaml b/src/Remote/Insight.Remote.Windows/Resources/Styles.xaml new file mode 100644 index 0000000..f0d8157 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Resources/Styles.xaml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinApp.cs b/src/Remote/Insight.Remote.Windows/Services/WinApp.cs new file mode 100644 index 0000000..2fd5ac8 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinApp.cs @@ -0,0 +1,200 @@ +using Insight.Domain.Enums; +using Insight.Domain.Network.Remote.Messages; +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Messages; +using Insight.Remote.Shared.Native.Windows; +using Insight.Remote.Windows.ViewModels; +using Insight.Remote.Windows.Views; +using Microsoft.Extensions.Logging; +using Microsoft.Win32; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Windows; +using Vaitr.Bus; + +namespace Insight.Remote.Windows.Services; + +internal class WinApp : IDesktopApp +{ + private MainWindow? _mainWindow; + private bool _disposed; + + private readonly IInputProvider _inputProvider; + private readonly ICursorProvider _cursorProvider; + private readonly IAudioProvider _audioProvider; + private readonly IClipboardProvider _clipboardProvider; + private readonly IDispatcher _dispatcher; + private readonly MainViewModel _maiViewModel; + private readonly Bus _bus; + private readonly ILogger _logger; + + public WinApp( + IInputProvider inputProvider, + ICursorProvider cursorProvider, + IAudioProvider audioProvider, + IClipboardProvider clipboardProvider, + IDispatcher dispatcher, + MainViewModel mainViewModel, + Bus bus, + ILogger logger) + { + _inputProvider = inputProvider; + _cursorProvider = cursorProvider; + _audioProvider = audioProvider; + _clipboardProvider = clipboardProvider; + _dispatcher = dispatcher; + _maiViewModel = mainViewModel; + _bus = bus; + _logger = logger; + } + + public async Task InitAsync(RemoteControlMode mode, CancellationToken cancellationToken) + { + // subscriptions + using var requestToken = _bus.SubscribeAsync(OnCastRequestAsync, null); + + // maintain list of background tasks + var tasks = new List + { + InvokeEventsAsync(cancellationToken), + + _inputProvider.InitAsync(cancellationToken), + _audioProvider.InitAsync(cancellationToken), + _clipboardProvider.InitAsync(cancellationToken) + }; + + // switch user modes + if (mode == RemoteControlMode.Attended) + { + // generate and show main window + await _dispatcher.InvokeAsync(() => + { + _mainWindow = new MainWindow(_maiViewModel); + _mainWindow.Show(); + }, cancellationToken); + } + else + { + if (Win32Interop.GetCurrentDesktop(out var currentDesktopName) is false) + { + _logger.LogWarning("Failed to get initial desktop name."); + } + else + { + _logger.LogInformation("Setting initial desktop to {currentDesktopName}.", currentDesktopName); + } + + if (Win32Interop.SwitchToInputDesktop() is false) + { + _logger.LogWarning("Failed to set initial desktop."); + } + } + + // await tasks => rewrite thread of clipboard + foreach (var task in tasks) + { + try + { + await task; + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + private async Task InvokeEventsAsync(CancellationToken cancellationToken) + { + SystemEvents.SessionSwitch += OnSessionSwitch; + SystemEvents.SessionEnding += OnSessionEnding; + + var queue = new ConcurrentQueue(); + + using var switchToken = _bus.Subscribe(queue.Enqueue, null); + using var endingToken = _bus.Subscribe(queue.Enqueue, null); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + if (queue.TryDequeue(out var result) is false) + { + await Task.Delay(1000, cancellationToken); + continue; + } + + if (result is SessionSwitchEventArgs switchArgs) + { + _logger.LogInformation("Session changing. Reason: {reason}", switchArgs.Reason); + + var reason = (Domain.Enums.SessionSwitchReason)(int)switchArgs.Reason; + await _bus.PublishAsync(new WindowsSessionSwitched(reason, Process.GetCurrentProcess().SessionId), cancellationToken); + } + else if (result is SessionEndedEventArgs endingArgs) + { + _logger.LogInformation("Session ending. Reason: {reason}", endingArgs.Reason); + + var reason = (Domain.Enums.SessionEndReasons)endingArgs.Reason; + await _bus.PublishAsync(new WindowsSessionEnding(reason), cancellationToken); + } + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + + SystemEvents.SessionSwitch -= OnSessionSwitch; + SystemEvents.SessionEnding -= OnSessionEnding; + } + + private async ValueTask OnCastRequestAsync(CastRequest request, CancellationToken cancellationToken) + { + if (true) // test + { + await _bus.PublishAsync(new CastRequestDemand(request, true), cancellationToken); + return; + } + + if (request.Mode != RemoteControlMode.Unattended) + { + var result = await _dispatcher.InvokeAsync(() => + { + return System.Windows.MessageBox.Show($"Accept connection request from {request.RequesterName}?", "Connection Request", + MessageBoxButton.YesNo, + MessageBoxImage.Question); + }, cancellationToken); + + switch (result) + { + case MessageBoxResult.No: await _bus.PublishAsync(new CastRequestDemand(request, false), cancellationToken); return; + } + } + + await _bus.PublishAsync(new CastRequestDemand(request, true), cancellationToken); + } + + private void OnSessionSwitch(object sender, SessionSwitchEventArgs e) => _bus.Publish(e); + private void OnSessionEnding(object sender, SessionEndingEventArgs e) => _bus.Publish(e); + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) + { + + } + + _disposed = true; + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinAudioProvider.cs b/src/Remote/Insight.Remote.Windows/Services/WinAudioProvider.cs new file mode 100644 index 0000000..e5f2079 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinAudioProvider.cs @@ -0,0 +1,82 @@ +using Insight.Domain.Network.Remote.Messages; +using Insight.Remote.Shared.Abstractions; +using Microsoft.Extensions.Logging; +using NAudio.Wave; +using System.IO; +using Vaitr.Bus; + +namespace Insight.Remote.Windows.Services; + +internal class WinAudioProvider : IAudioProvider +{ + private WasapiLoopbackCapture? _capturer; + private WaveFormat? _targetFormat; + + private readonly Bus _bus; + private readonly ILogger _logger; + + public WinAudioProvider(Bus bus, ILogger logger) + { + _bus = bus; + _logger = logger; + } + + public async Task InitAsync(CancellationToken cancellationToken) + { + _capturer = new WasapiLoopbackCapture(); + + try + { + _targetFormat ??= new WaveFormat(16000, 8, 1); + + _capturer.DataAvailable += OnDataAvailable; + _capturer.StartRecording(); + + while (cancellationToken.IsCancellationRequested is false) + { + await Task.Delay(500, cancellationToken); + } + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex, "Error while creating audio capturer. Make sure a sound device is installed and working."); + } + finally + { + _capturer.DataAvailable -= OnDataAvailable; + _capturer.StopRecording(); + _capturer.Dispose(); + } + } + + private async void OnDataAvailable(object? sender, WaveInEventArgs args) + { + if (_capturer is null || args.Buffer.All(x => x == 0) || args.BytesRecorded <= 0) return; + + // gets auto-disposed after writer + using var inputStream = new MemoryStream(); + + // writer needs to be disposed here + using (var writer = new WaveFileWriter(inputStream, _capturer.WaveFormat)) + writer.Write(args.Buffer); + + // gets auto-disposed after reader + using var workStream = new MemoryStream(inputStream.ToArray()); + using var reader = new WaveFileReader(workStream); + + using var outputStream = new MemoryStream(); + + // resample to 16-bit + using (var resampler = new MediaFoundationResampler(reader, _targetFormat)) + { + WaveFileWriter.WriteWavFileToStream(outputStream, resampler); + } + + // invoke event + await _bus.PublishAsync(new CastAudio + { + Buffer = outputStream.ToArray() + }, cancellationToken: default); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinClipboardProvider.cs b/src/Remote/Insight.Remote.Windows/Services/WinClipboardProvider.cs new file mode 100644 index 0000000..e212e47 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinClipboardProvider.cs @@ -0,0 +1,92 @@ +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Messages; +using Insight.Remote.Shared.Native.Windows; +using Microsoft.Extensions.Logging; +using System.Collections.Concurrent; +using Vaitr.Bus; +using Clipboard = System.Windows.Clipboard; + +namespace Insight.Remote.Windows.Services; + +internal class WinClipboardProvider : IClipboardProvider +{ + private readonly ConcurrentQueue _setQueue = new(); + + private readonly Bus _bus; + private readonly ILogger _logger; + + public WinClipboardProvider(Bus bus, ILogger logger) + { + _bus = bus; + _logger = logger; + } + + public async Task InitAsync(CancellationToken cancellationToken) + { + var thread = new Thread(async () => + { + string? lastClipboardValue = null; + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + if (_setQueue.TryDequeue(out var item)) + { + //_logger.LogCritical("set clipboard"); + + if (string.IsNullOrWhiteSpace(item)) Clipboard.Clear(); + else Clipboard.SetText(item); + } + else + { + // _logger.LogCritical("get clipboard"); + + Win32Interop.SwitchToInputDesktop(); + + if (Clipboard.ContainsText() && Clipboard.GetText() != lastClipboardValue) + { + lastClipboardValue = Clipboard.GetText(); + + await _bus.PublishAsync(new ClipboardChanged(lastClipboardValue), cancellationToken); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while setting clipboard text."); + } + finally + { + Thread.Sleep(1000); + } + } + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.IsBackground = true; + thread.Start(); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + if (thread.IsAlive) + { + await Task.Delay(1000, cancellationToken); + } + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + public Task SetText(string clipboardText) + { + _setQueue.Enqueue(clipboardText); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinCursorProvider.cs b/src/Remote/Insight.Remote.Windows/Services/WinCursorProvider.cs new file mode 100644 index 0000000..047dae2 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinCursorProvider.cs @@ -0,0 +1,112 @@ +using Insight.Domain.Network.Remote.Messages; +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Native.Windows; +using Microsoft.Extensions.Logging; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; + +namespace Insight.Remote.Windows.Services; + +internal class WinCursorProvider : ICursorProvider +{ + private readonly SemaphoreSlim _semaphore = new(1, 1); + private readonly ILogger _logger; + + private User32.CursorInfo _cursor = new(); + private CastCursor _lastCursor = new(); + private nint _lastHandle = 0; + private byte[] _lastIconBytes = Array.Empty(); + + public WinCursorProvider(ILogger logger) + { + _logger = logger; + } + + public async Task GetAsync(CancellationToken cancellationToken) + { + try + { + await _semaphore.WaitAsync(cancellationToken); + + _cursor.cbSize = Marshal.SizeOf(_cursor); + + if (User32.GetCursorInfo(out _cursor) is false) return null; + + if (_cursor.flags != User32.CURSOR_SHOWING) + { + _lastCursor = EmptyCursor; + return _lastCursor; + } + + if (_cursor.ptScreenPos.x == _lastCursor.X && + _cursor.ptScreenPos.y == _lastCursor.Y && + _cursor.hCursor == _lastHandle) return _lastCursor; + + var iconBytes = Array.Empty(); + + if (_cursor.hCursor == _lastHandle) iconBytes = _lastIconBytes; + else + { + using var ms = new MemoryStream(); + using var icon = Icon.FromHandle(_cursor.hCursor); + icon.ToBitmap().Save(ms, ImageFormat.Png); + + iconBytes = ms.ToArray(); + _lastIconBytes = iconBytes; + } + + _lastHandle = _cursor.hCursor; + + _lastCursor = new CastCursor + { + X = _cursor.ptScreenPos.x, + Y = _cursor.ptScreenPos.y, + Icon = iconBytes + }; + + return _lastCursor; + } + catch (TaskCanceledException) + { + return null; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return null; + } + finally + { + _semaphore.Release(); + } + } + + public async Task SetAsync(int x, int y, CancellationToken cancellationToken) + { + if (_semaphore.CurrentCount == 0) return; + + try + { + await _semaphore.WaitAsync(cancellationToken); + + User32.SetCursorPos(x, y); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + finally + { + _semaphore.Release(); + } + } + + private static CastCursor EmptyCursor => new() + { + X = 0, + Y = 0, + Icon = Array.Empty() + }; +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinDispatcher.cs b/src/Remote/Insight.Remote.Windows/Services/WinDispatcher.cs new file mode 100644 index 0000000..58a2f29 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinDispatcher.cs @@ -0,0 +1,78 @@ +using Insight.Remote.Shared.Abstractions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System.Windows.Threading; +using Application = System.Windows.Application; + +namespace Insight.Remote.Windows.Services; + +internal class WinDispatcher : IDispatcher +{ + private Application? _app; + + private readonly ManualResetEvent _appSignal = new(false); + + private readonly IHostApplicationLifetime _lifetime; + private readonly ILogger _logger; + + public WinDispatcher(IHostApplicationLifetime lifetime, ILogger logger) + { + _lifetime = lifetime; + _logger = logger; + } + + public async Task RunAsync(CancellationToken cancellationToken) + { + var startSignal = new SemaphoreSlim(0, 1); + + var thread = new Thread(() => + { + _app = new Application(); + _app.Startup += (s, e) => startSignal.Release(); + _app.Run(); + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + await startSignal.WaitAsync(cancellationToken).ConfigureAwait(false); + _appSignal.Set(); + + return thread; + } + + public async Task ShutdownAsync() + { + _lifetime.StopApplication(); + + if (_app is null) return; + + try + { + _logger.LogInformation("Shutting down WPF thread."); + + await InvokeAsync(_app.Shutdown); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + + public async ValueTask InvokeAsync(Action action, CancellationToken cancellationToken = default) + { + if (_app is null) return; + if (_appSignal.WaitOne(5000) is false) return; + + await _app.Dispatcher.InvokeAsync(action, DispatcherPriority.Normal, cancellationToken); + } + + public async ValueTask InvokeAsync(Func func, CancellationToken cancellationToken = default) + { + if (_app is null) return default; + if (_appSignal.WaitOne(5000) is false) return default; + + return await _app.Dispatcher.InvokeAsync(func, DispatcherPriority.Normal, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinFileProvider.cs b/src/Remote/Insight.Remote.Windows/Services/WinFileProvider.cs new file mode 100644 index 0000000..7c0f5a2 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinFileProvider.cs @@ -0,0 +1,172 @@ +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Services; +using Insight.Remote.Shared.ViewModels; +using Microsoft.Extensions.Logging; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Windows; +using MessageBoxOptions = System.Windows.MessageBoxOptions; + +namespace Insight.Remote.Windows.Services; + +internal class WinFileProvider : IFileProvider +{ + private static MessageBoxResult? _result; + private static readonly ConcurrentDictionary _partialTransfers = new(); + private static readonly SemaphoreSlim _writeLock = new(1, 1); + private readonly ILogger _logger; + + public WinFileProvider(ILogger logger) + { + _logger = logger; + } + + public string GetBaseDirectory() + { + var programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); + return Directory.CreateDirectory(Path.Combine(programDataPath, "RemoteControl", "Shared")).FullName; + } + + public async Task ReceiveFile(byte[] buffer, string fileName, string messageId, bool endOfFile, bool startOfFile) + { + try + { + await _writeLock.WaitAsync(); + + var baseDir = GetBaseDirectory(); + + SetFileOrFolderPermissions(baseDir); + + if (startOfFile) + { + var filePath = Path.Combine(baseDir, fileName); + + if (File.Exists(filePath)) + { + var count = 0; + var ext = Path.GetExtension(fileName); + var fileWithoutExt = Path.GetFileNameWithoutExtension(fileName); + while (File.Exists(filePath)) + { + filePath = Path.Combine(baseDir, $"{fileWithoutExt}-{count}{ext}"); + count++; + } + } + + File.Create(filePath).Close(); + SetFileOrFolderPermissions(filePath); + var fs = new FileStream(filePath, FileMode.OpenOrCreate); + _partialTransfers.AddOrUpdate(messageId, fs, (k, v) => fs); + } + + var fileStream = _partialTransfers[messageId]; + + if (buffer?.Length > 0) + { + await fileStream.WriteAsync(buffer); + } + + if (endOfFile) + { + fileStream.Close(); + _partialTransfers.Remove(messageId, out _); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while receiving file."); + } + finally + { + _writeLock.Release(); + if (endOfFile) + { + await Task.Run(ShowTransferComplete); + } + } + } + + public async Task UploadFile(FileUpload fileUpload, Streamer viewer, Action progressUpdateCallback, CancellationToken cancelToken) + { + try + { + //await viewer.SendFileAsync(fileUpload, progressUpdateCallback, cancelToken); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while uploading file."); + } + } + + private void SetFileOrFolderPermissions(string path) + { + FileSystemSecurity ds; + + var aclSections = AccessControlSections.Access | AccessControlSections.Group | AccessControlSections.Owner; + if (File.Exists(path)) + { + ds = new FileSecurity(path, aclSections); + } + else if (Directory.Exists(path)) + { + ds = new DirectorySecurity(path, aclSections); + } + else + { + return; + } + + var sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); + var account = (NTAccount)sid.Translate(typeof(NTAccount)); + + var accessAlreadySet = false; + + foreach (FileSystemAccessRule rule in ds.GetAccessRules(true, true, typeof(NTAccount))) + { + if (rule.IdentityReference == account && + rule.FileSystemRights.HasFlag(FileSystemRights.Modify) && + rule.AccessControlType == AccessControlType.Allow) + { + accessAlreadySet = true; + break; + } + } + + if (!accessAlreadySet) + { + ds.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.Modify, AccessControlType.Allow)); + if (File.Exists(path)) + { + new FileInfo(path).SetAccessControl((FileSecurity)ds); + } + else if (Directory.Exists(path)) + { + new DirectoryInfo(path).SetAccessControl((DirectorySecurity)ds); + } + } + } + + private void ShowTransferComplete() + { + // Prevent multiple dialogs from popping up. + if (_result is null) + { + _result = System.Windows.MessageBox.Show("File transfer complete. Show folder?", + "Transfer Complete", + MessageBoxButton.YesNo, + MessageBoxImage.Question, + MessageBoxResult.Yes, + MessageBoxOptions.ServiceNotification); + + if (_result == MessageBoxResult.Yes) + { + Process.Start("explorer.exe", GetBaseDirectory()); + } + + _result = null; + } + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinInputProvider.cs b/src/Remote/Insight.Remote.Windows/Services/WinInputProvider.cs new file mode 100644 index 0000000..ac325c9 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinInputProvider.cs @@ -0,0 +1,406 @@ +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Enums; +using Insight.Remote.Shared.Native.Windows; +using Microsoft.Extensions.Logging; +using System.Threading.Channels; + +namespace Insight.Remote.Windows.Services; + +internal class WinInputProvider : IInputProvider +{ + private volatile bool _inputBlocked; + + private readonly Channel _inputs = Channel.CreateUnbounded(new UnboundedChannelOptions { AllowSynchronousContinuations = false }); + + private readonly IScreenProvider _screenProvider; + private readonly ILogger _logger; + + public WinInputProvider(IScreenProvider screenProvider, ILogger logger) + { + _screenProvider = screenProvider; + _logger = logger; + } + + public async Task InitAsync(CancellationToken cancellationToken) + { + try + { + while (cancellationToken.IsCancellationRequested is false) + { + await ThreadedInputProcessing(cancellationToken); + await Task.Delay(1000, cancellationToken); + } + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + private async Task ThreadedInputProcessing(CancellationToken cancellationToken) + { + var semaphore = new SemaphoreSlim(0, 1); + + var thread = new Thread(async () => + { + //_logger.LogInformation("New input processing thread started on thread {threadId}", Thread.CurrentThread.ManagedThreadId); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + if (await _inputs.Reader.WaitToReadAsync(cancellationToken) is false) return; + + // Thread likely has hooks in current desktop. SendKeys will create one with no way to unhook it + // Start a new thread for processing input => break + if (Win32Interop.SwitchToInputDesktop() is false) + { + _logger.LogWarning("Desktop switch failed during input processing."); + break; + } + + var input = await _inputs.Reader.ReadAsync(cancellationToken); + if (_inputBlocked) continue; + + input(); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + + //_logger.LogInformation("Stopping input processing on thread {threadId}", Environment.CurrentManagedThreadId); + semaphore.Release(); + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + try + { + await semaphore.WaitAsync(cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SendKeyDownAsync(string key, CancellationToken cancellationToken) + { + if (ConvertJavaScriptKeyToVirtualKey(key, out var keyCode) is false || keyCode is null) return; + + try + { + await _inputs.Writer.WriteAsync(() => + { + var union = new User32.InputUnion() + { + ki = new User32.KEYBDINPUT() + { + wVk = keyCode.Value, + wScan = (User32.ScanCodeShort)User32.MapVirtualKeyEx((uint)keyCode.Value, User32.VkMapType.MAPVK_VK_TO_VSC, User32.GetKeyboardLayout()), + time = 0, + dwExtraInfo = User32.GetMessageExtraInfo() + } + }; + + var input = new User32.INPUT() { type = User32.InputType.KEYBOARD, U = union }; + _ = User32.SendInput(1, new User32.INPUT[] { input }, User32.INPUT.Size); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SendKeyUpAsync(string key, CancellationToken cancellationToken) + { + if (ConvertJavaScriptKeyToVirtualKey(key, out var keyCode) is false || keyCode is null) return; + + try + { + await _inputs.Writer.WriteAsync(() => + { + var union = new User32.InputUnion() + { + ki = new User32.KEYBDINPUT() + { + wVk = keyCode.Value, + wScan = (User32.ScanCodeShort)User32.MapVirtualKeyEx((uint)keyCode.Value, User32.VkMapType.MAPVK_VK_TO_VSC, User32.GetKeyboardLayout()), + time = 0, + dwFlags = User32.KEYEVENTF.KEYUP, + dwExtraInfo = User32.GetMessageExtraInfo() + } + }; + var input = new User32.INPUT() { type = User32.InputType.KEYBOARD, U = union }; + _ = User32.SendInput(1, new User32.INPUT[] { input }, User32.INPUT.Size); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SendMouseButtonActionAsync(int button, ButtonAction buttonAction, double percentX, double percentY, CancellationToken cancellationToken) + { + try + { + await _inputs.Writer.WriteAsync(() => + { + User32.MOUSEEVENTF mouseEvent; + switch (button) + { + case 0: + switch (buttonAction) + { + case ButtonAction.Down: + mouseEvent = User32.MOUSEEVENTF.LEFTDOWN; + break; + case ButtonAction.Up: + mouseEvent = User32.MOUSEEVENTF.LEFTUP; + break; + default: + return; + } + break; + case 1: + switch (buttonAction) + { + case ButtonAction.Down: + mouseEvent = User32.MOUSEEVENTF.MIDDLEDOWN; + break; + case ButtonAction.Up: + mouseEvent = User32.MOUSEEVENTF.MIDDLEUP; + break; + default: + return; + } + break; + case 2: + switch (buttonAction) + { + case ButtonAction.Down: + mouseEvent = User32.MOUSEEVENTF.RIGHTDOWN; + break; + case ButtonAction.Up: + mouseEvent = User32.MOUSEEVENTF.RIGHTUP; + break; + default: + return; + } + break; + default: + return; + } + + var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, _screenProvider); + + // Coordinates must be normalized. The bottom-right coordinate is mapped to 65535. + var normalizedX = xyPercent.Item1 * 65535D; + var normalizedY = xyPercent.Item2 * 65535D; + var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = User32.MOUSEEVENTF.ABSOLUTE | mouseEvent | User32.MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = User32.GetMessageExtraInfo() } }; + var input = new User32.INPUT() { type = User32.InputType.MOUSE, U = union }; + _ = User32.SendInput(1, new User32.INPUT[] { input }, User32.INPUT.Size); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SendMouseMoveAsync(double percentX, double percentY, CancellationToken cancellationToken) + { + try + { + await _inputs.Writer.WriteAsync(() => + { + var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, _screenProvider); + // Coordinates must be normalized. The bottom-right coordinate is mapped to 65535. + var normalizedX = xyPercent.Item1 * 65535D; + var normalizedY = xyPercent.Item2 * 65535D; + var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = User32.MOUSEEVENTF.ABSOLUTE | User32.MOUSEEVENTF.MOVE | User32.MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = User32.GetMessageExtraInfo() } }; + var input = new User32.INPUT() { type = User32.InputType.MOUSE, U = union }; + _ = User32.SendInput(1, new User32.INPUT[] { input }, User32.INPUT.Size); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SendMouseWheelAsync(int deltaY, CancellationToken cancellationToken) + { + try + { + await _inputs.Writer.WriteAsync(() => + { + if (deltaY < 0) + { + deltaY = -120; + } + else if (deltaY > 0) + { + deltaY = 120; + } + var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = User32.MOUSEEVENTF.WHEEL, dx = 0, dy = 0, time = 0, mouseData = deltaY, dwExtraInfo = User32.GetMessageExtraInfo() } }; + var input = new User32.INPUT() { type = User32.InputType.MOUSE, U = union }; + _ = User32.SendInput(1, new User32.INPUT[] { input }, User32.INPUT.Size); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SendTextAsync(string transferText, CancellationToken cancellationToken) + { + try + { + await _inputs.Writer.WriteAsync(() => + { + SendKeys.SendWait(transferText); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task SetKeyStatesUpAsync(CancellationToken cancellationToken) + { + try + { + await _inputs.Writer.WriteAsync(() => + { + foreach (User32.VirtualKey key in Enum.GetValues(typeof(User32.VirtualKey))) + { + try + { + var state = User32.GetKeyState(key); + if (state == -127) + { + var union = new User32.InputUnion() + { + ki = new User32.KEYBDINPUT() + { + wVk = key, + wScan = 0, + time = 0, + dwFlags = User32.KEYEVENTF.KEYUP, + dwExtraInfo = User32.GetMessageExtraInfo() + } + }; + var input = new User32.INPUT() { type = User32.InputType.KEYBOARD, U = union }; + _ = User32.SendInput(1, new User32.INPUT[] { input }, User32.INPUT.Size); + } + } + catch { } + } + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + public async Task ToggleBlockInputAsync(bool toggleOn, CancellationToken cancellationToken) + { + try + { + await _inputs.Writer.WriteAsync(() => + { + _inputBlocked = toggleOn; + var result = User32.BlockInput(toggleOn); + + _logger.LogInformation("Result of ToggleBlockInput set to {toggleOn}: {result}", toggleOn, result); + }, cancellationToken); + } + catch (TaskCanceledException) { } + catch (Exception ex) + { + _logger.LogError(ex.ToString()); + } + } + + private bool ConvertJavaScriptKeyToVirtualKey(string key, out User32.VirtualKey? result) + { + result = key switch + { + "Down" or "ArrowDown" => User32.VirtualKey.DOWN, + "Up" or "ArrowUp" => User32.VirtualKey.UP, + "Left" or "ArrowLeft" => User32.VirtualKey.LEFT, + "Right" or "ArrowRight" => User32.VirtualKey.RIGHT, + "Enter" => User32.VirtualKey.RETURN, + "Esc" or "Escape" => User32.VirtualKey.ESCAPE, + "Alt" => User32.VirtualKey.MENU, + "Control" => User32.VirtualKey.CONTROL, + "Shift" => User32.VirtualKey.SHIFT, + "PAUSE" => User32.VirtualKey.PAUSE, + "BREAK" => User32.VirtualKey.PAUSE, + "Backspace" => User32.VirtualKey.BACK, + "Tab" => User32.VirtualKey.TAB, + "CapsLock" => User32.VirtualKey.CAPITAL, + "Delete" => User32.VirtualKey.DELETE, + "Home" => User32.VirtualKey.HOME, + "End" => User32.VirtualKey.END, + "PageUp" => User32.VirtualKey.PRIOR, + "PageDown" => User32.VirtualKey.NEXT, + "NumLock" => User32.VirtualKey.NUMLOCK, + "Insert" => User32.VirtualKey.INSERT, + "ScrollLock" => User32.VirtualKey.SCROLL, + "F1" => User32.VirtualKey.F1, + "F2" => User32.VirtualKey.F2, + "F3" => User32.VirtualKey.F3, + "F4" => User32.VirtualKey.F4, + "F5" => User32.VirtualKey.F5, + "F6" => User32.VirtualKey.F6, + "F7" => User32.VirtualKey.F7, + "F8" => User32.VirtualKey.F8, + "F9" => User32.VirtualKey.F9, + "F10" => User32.VirtualKey.F10, + "F11" => User32.VirtualKey.F11, + "F12" => User32.VirtualKey.F12, + "Meta" => User32.VirtualKey.LWIN, + "ContextMenu" => User32.VirtualKey.MENU, + _ => key.Length == 1 ? (User32.VirtualKey)User32.VkKeyScan(Convert.ToChar(key)) : null + }; + + if (result is not null) return true; + + _logger.LogWarning("Unable to parse key input: {key}.", key); + return false; + } + + private static Tuple GetAbsolutePercentFromRelativePercent(double percentX, double percentY, IScreenProvider capturer) + { + var absoluteX = capturer.CurrentScreenBounds.Width * percentX + capturer.CurrentScreenBounds.Left - capturer.GetVirtualScreenBounds().Left; + var absoluteY = capturer.CurrentScreenBounds.Height * percentY + capturer.CurrentScreenBounds.Top - capturer.GetVirtualScreenBounds().Top; + return new Tuple(absoluteX / capturer.GetVirtualScreenBounds().Width, absoluteY / capturer.GetVirtualScreenBounds().Height); + } + + private static Tuple GetAbsolutePointFromRelativePercent(double percentX, double percentY, IScreenProvider capturer) + { + var absoluteX = capturer.CurrentScreenBounds.Width * percentX + capturer.CurrentScreenBounds.Left; + var absoluteY = capturer.CurrentScreenBounds.Height * percentY + capturer.CurrentScreenBounds.Top; + return new Tuple(absoluteX, absoluteY); + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Services/WinScreenCapturer.cs b/src/Remote/Insight.Remote.Windows/Services/WinScreenCapturer.cs new file mode 100644 index 0000000..33cd979 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Services/WinScreenCapturer.cs @@ -0,0 +1,520 @@ +// The DirectX capture code is based off examples from the +// SharpDX Samples at https://github.com/sharpdx/SharpDX. + +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Extensions; +using Insight.Remote.Shared.Models; +using Insight.Remote.Shared.Native.Windows; +using Insight.Remote.Windows.Models; +using Microsoft.Extensions.Logging; +using Microsoft.Win32; +using RemoteControl.Shared; +using SharpDX; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using SkiaSharp; +using SkiaSharp.Views.Desktop; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using Vaitr.Bus; +using Result = RemoteControl.Shared.Result; + +namespace Insight.Remote.Windows.Services; + +internal class WinScreenCapturer : IScreenProvider +{ + public event EventHandler? ScreenChanged; + public Rectangle GetVirtualScreenBounds() => SystemInformation.VirtualScreen; + + private SKBitmap? CurrentFrame + { + get => _currentFrame; + set + { + if (_currentFrame != null) + { + _previousFrame?.Dispose(); + _previousFrame = _currentFrame; + } + _currentFrame = value; + } + } + + public int GetScreenCount() => Screen.AllScreens.Length; + public IEnumerable GetDisplayNames() => Screen.AllScreens.Select(x => x.DeviceName); + public Rectangle CurrentScreenBounds { get; private set; } = Screen.PrimaryScreen?.Bounds ?? Rectangle.Empty; + public string SelectedScreen { get; private set; } = Screen.PrimaryScreen?.DeviceName ?? string.Empty; + + private readonly object _screenBoundsLock = new(); + private SKBitmap? _currentFrame; + private SKBitmap? _previousFrame; + private bool _initialized; + + private readonly SemaphoreSlim _semaphore = new(1, 1); + private readonly Dictionary _bitBltScreens = new(); + private readonly Dictionary _directxScreens = new(); + + private readonly Bus _bus; + private readonly ILogger _logger; + + public WinScreenCapturer(Bus bus, ILogger logger) + { + _bus = bus; + _logger = logger; + + SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; + } + + public async Task InitAsync(CancellationToken cancellationToken) + { + Win32Interop.SwitchToInputDesktop(); + + InitBitBlt(); + InitDirectX(); + + _initialized = true; + } + + public void SetSelectedScreen(string displayName) + { + lock (_screenBoundsLock) + { + if (displayName == SelectedScreen) return; + + if (_bitBltScreens.ContainsKey(displayName)) SelectedScreen = displayName; + else SelectedScreen = _bitBltScreens.Keys.First(); + + RefreshCurrentScreenBounds(); + } + } + + public async Task GetNextFrameAsync(CancellationToken cancellationToken) + { + try + { + await _semaphore.WaitAsync(cancellationToken); + + if (Win32Interop.SwitchToInputDesktop() is false) + { + // Something will occasionally prevent this from succeeding after active + // desktop has changed to/from WinLogon (err code 170). I'm guessing a hook + // is getting put in the desktop, which causes SetThreadDesktop to fail. + // The caller can start a new thread, which seems to resolve it. + var errCode = Marshal.GetLastWin32Error(); + + _logger.LogError("Failed to switch to input desktop. Last Win32 error code: {errCode}", errCode); + + return new FrameResult(null, false, true); + } + + if (_initialized is false) + { + _logger.LogWarning("Init needed in GetNextFrame."); + await InitAsync(cancellationToken); + } + + var frame = GetDirectXFrame(); + if (frame is null || frame.IsSuccess is false) + { + var bitBltResult = GetBitBltFrame(); + if (bitBltResult.IsSuccess is false) + { + var ex = bitBltResult.Exception ?? new("Unknown error."); + _logger.LogError(ex, "Error while getting next frame."); + return new FrameResult(null, false, true); + } + + CurrentFrame = bitBltResult.Value; + } + else if (frame.IsSuccess && frame.Bitmap is not null && IsEmpty(frame.Bitmap) is false) + { + CurrentFrame = frame.Bitmap; + } + + return new FrameResult(CurrentFrame, frame is not null && frame.HadChanges, false); + } + catch (TaskCanceledException) + { + return new FrameResult(null, false, true); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while getting next frame."); + _initialized = false; + + return new FrameResult(null, false, true); + } + finally + { + _semaphore.Release(); + } + } + + public SKRect GetFrameDiffArea(bool fullscreen) + { + if (CurrentFrame is null) return SKRect.Empty; + return CurrentFrame.GetDiffArea(_previousFrame, fullscreen); + } + + public async Task GetDiffAreaAsync(bool fullscreen, CancellationToken cancellationToken) + { + if (CurrentFrame is null) return SKRect.Empty; + + try + { + await _semaphore.WaitAsync(cancellationToken); + + return CurrentFrame.GetDiffArea(_previousFrame, fullscreen); + } + finally + { + _semaphore.Release(); + } + } + + public int GetSelectedScreenIndex() + { + if (_bitBltScreens.TryGetValue(SelectedScreen, out var index)) return index; + return 0; + } + + private Result GetBitBltFrame() + { + try + { + using var bitmap = new Bitmap(CurrentScreenBounds.Width, CurrentScreenBounds.Height, PixelFormat.Format32bppArgb); + using (var graphic = Graphics.FromImage(bitmap)) + { + graphic.CopyFromScreen(CurrentScreenBounds.Left, CurrentScreenBounds.Top, 0, 0, new Size(CurrentScreenBounds.Width, CurrentScreenBounds.Height)); + } + return Result.Ok(bitmap.ToSKBitmap()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Capturer error in BitBltCapture."); + _initialized = false; + return Result.Fail("Error while capturing BitBlt frame."); + } + } + + private DxCaptureResult GetDirectXFrame() + { + if (_directxScreens.TryGetValue(SelectedScreen, out var dxOutput) is false) + { + return DxCaptureResult.Fail("DirectX output not found."); + } + + try + { + var outputDuplication = dxOutput.OutputDuplication; + var device = dxOutput.Device; + var texture2D = dxOutput.Texture2D; + var bounds = dxOutput.Bounds; + + var result = outputDuplication.TryAcquireNextFrame(timeoutInMilliseconds: 25, out var duplicateFrameInfo, out var screenResource); + + if (!result.Success) + { + return DxCaptureResult.TryAcquireFailed(result); + } + + if (duplicateFrameInfo.AccumulatedFrames == 0) + { + try + { + outputDuplication.ReleaseFrame(); + } + catch { } + return DxCaptureResult.NoAccumulatedFrames(result); + } + + using Texture2D screenTexture2D = screenResource.QueryInterface(); + device.ImmediateContext.CopyResource(screenTexture2D, texture2D); + var dataBox = device.ImmediateContext.MapSubresource(texture2D, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None); + using var bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); + var bitmapData = bitmap.LockBits(bounds, ImageLockMode.WriteOnly, bitmap.PixelFormat); + var dataBoxPointer = dataBox.DataPointer; + var bitmapDataPointer = bitmapData.Scan0; + for (var y = 0; y < bounds.Height; y++) + { + Utilities.CopyMemory(bitmapDataPointer, dataBoxPointer, bounds.Width * 4); + dataBoxPointer = IntPtr.Add(dataBoxPointer, dataBox.RowPitch); + bitmapDataPointer = IntPtr.Add(bitmapDataPointer, bitmapData.Stride); + } + bitmap.UnlockBits(bitmapData); + device.ImmediateContext.UnmapSubresource(texture2D, 0); + screenResource?.Dispose(); + + switch (dxOutput.Rotation) + { + case DisplayModeRotation.Unspecified: + case DisplayModeRotation.Identity: + break; + case DisplayModeRotation.Rotate90: + bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone); + break; + case DisplayModeRotation.Rotate180: + bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone); + break; + case DisplayModeRotation.Rotate270: + bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); + break; + default: + break; + } + + return DxCaptureResult.Ok(bitmap.ToSKBitmap(), result); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while getting DirectX frame."); + } + finally + { + try + { + dxOutput.OutputDuplication.ReleaseFrame(); + } + catch { } + } + + return DxCaptureResult.Fail("Failed to get DirectX frame."); + } + + private void ClearDirectXOutputs() + { + foreach (var screen in _directxScreens.Values) + { + try + { + screen.Dispose(); + } + catch { } + } + + _directxScreens.Clear(); + } + + private void InitBitBlt() + { + _bitBltScreens.Clear(); + + for (var i = 0; i < Screen.AllScreens.Length; i++) + { + _bitBltScreens.Add(Screen.AllScreens[i].DeviceName, i); + } + } + + private void InitDirectX() + { + try + { + ClearDirectXOutputs(); + + using var factory = new Factory1(); + foreach (var adapter in factory.Adapters1.Where(x => (x.Outputs?.Length ?? 0) > 0)) + { + foreach (var output in adapter.Outputs) + { + //_logger.LogWarning(output.Description.DeviceName); + + try + { + var device = new SharpDX.Direct3D11.Device(adapter); + var output1 = output.QueryInterface(); + + var bounds = output1.Description.DesktopBounds; + var width = bounds.Right - bounds.Left; + var height = bounds.Bottom - bounds.Top; + + // Create Staging texture CPU-accessible + var textureDesc = new Texture2DDescription + { + CpuAccessFlags = CpuAccessFlags.Read, + BindFlags = BindFlags.None, + Format = Format.B8G8R8A8_UNorm, + Width = width, + Height = height, + OptionFlags = ResourceOptionFlags.None, + MipLevels = 1, + ArraySize = 1, + SampleDescription = { Count = 1, Quality = 0 }, + Usage = ResourceUsage.Staging + }; + + var texture2D = new Texture2D(device, textureDesc); + + _directxScreens.Add( + output1.Description.DeviceName, + new DirectXOutput(adapter, + device, + output1.DuplicateOutput(device), + texture2D, + output1.Description.Rotation)); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while initializing DirectX."); + } + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while initializing DirectX."); + } + } + + private bool IsEmpty(SKBitmap bitmap) + { + if (bitmap is null) return true; + + var height = bitmap.Height; + var width = bitmap.Width; + var bytesPerPixel = bitmap.BytesPerPixel; + + try + { + unsafe + { + byte* scan = (byte*)bitmap.GetPixels(); + + for (var row = 0; row < height; row++) + { + for (var column = 0; column < width; column++) + { + var index = row * width * bytesPerPixel + column * bytesPerPixel; + + byte* data = scan + index; + + for (var i = 0; i < bytesPerPixel; i++) + { + if (data[i] != 0) + { + return false; + } + } + } + } + + return true; + } + } + catch + { + return true; + } + } + + private void RefreshCurrentScreenBounds() + { + CurrentScreenBounds = Screen.AllScreens[_bitBltScreens[SelectedScreen]].Bounds; + + _initialized = false; + } + + private void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e) + { + RefreshCurrentScreenBounds(); + } + + public void Dispose() + { + try + { + SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged; + ClearDirectXOutputs(); + GC.SuppressFinalize(this); + } + catch { } + } + + private class DxCaptureResult + { + public SKBitmap? Bitmap { get; init; } + public SharpDX.Result? DxResult { get; init; } + public string FailureReason { get; init; } = string.Empty; + + [MemberNotNull(nameof(Bitmap))] + public bool HadChanges { get; init; } + + public bool IsSuccess { get; init; } + + internal static DxCaptureResult Fail(string failureReason) + { + return new DxCaptureResult() + { + FailureReason = failureReason + }; + } + + internal static DxCaptureResult Fail(string failureReason, SharpDX.Result dxResult) + { + return new DxCaptureResult() + { + FailureReason = failureReason, + DxResult = dxResult + }; + } + + internal static DxCaptureResult NoAccumulatedFrames(SharpDX.Result dxResult) + { + return new DxCaptureResult() + { + FailureReason = "No frames were accumulated.", + DxResult = dxResult, + IsSuccess = true + }; + } + + internal static DxCaptureResult Ok(SKBitmap sKBitmap, SharpDX.Result result) + { + return new DxCaptureResult() + { + Bitmap = sKBitmap, + DxResult = result, + HadChanges = true, + IsSuccess = true, + }; + } + + internal static DxCaptureResult TryAcquireFailed(SharpDX.Result dxResult) + { + if (dxResult.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Code) + { + return new DxCaptureResult() + { + FailureReason = "Timed out while waiting for the next frame.", + DxResult = dxResult, + IsSuccess = true + }; + } + return new DxCaptureResult() + { + FailureReason = "TryAcquireFrame returned failure.", + DxResult = dxResult + }; + } + } +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/ViewModels/MainViewModel.cs b/src/Remote/Insight.Remote.Windows/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..4badc14 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/ViewModels/MainViewModel.cs @@ -0,0 +1,157 @@ +using Insight.Remote.Shared.Abstractions; +using Insight.Remote.Shared.Messages; +using Insight.Remote.Shared.Native.Windows; +using Insight.Remote.Shared.Reactive; +using Insight.Remote.Shared.Services; +using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Security.Principal; +using Vaitr.Bus; + +namespace Insight.Remote.Windows.ViewModels; + +public class MainViewModel : ViewModelBase +{ + public string StatusMessage + { + get => Get() ?? string.Empty; + set => Set(value); + } + + public bool IsAdministrator { get; } = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); + public bool CanElevateToAdmin => !IsAdministrator; + public bool CanElevateToService => IsAdministrator && !WindowsIdentity.GetCurrent().IsSystem; + + public RelayCommand ElevateToAdminCommand { get; } + public RelayCommand ElevateToServiceCommand { get; } + + private string? _lastId; + + private readonly List _subscriptions = new(); + + private readonly Bus _bus; + private readonly IDispatcher _dispatcher; + private readonly ILogger _logger; + + public MainViewModel(Bus bus, IDispatcher dispatcher, ILogger logger) + { + _bus = bus; + _dispatcher = dispatcher; + _logger = logger; + + ElevateToAdminCommand = new RelayCommand(ElevateToAdmin, () => CanElevateToAdmin); + ElevateToServiceCommand = new RelayCommand(ElevateToService, () => CanElevateToService); + + _subscriptions.Add(_bus.SubscribeAsync(OnConnectionStateChangedAsync, null)); + _subscriptions.Add(_bus.SubscribeAsync(OnIdentityChangedAsync, null)); + } + + private async ValueTask OnConnectionStateChangedAsync(ConnectionStateChanged message, CancellationToken cancellationToken) + { + switch (message.State) + { + case ConnectionState.Connecting: + { + await _dispatcher.InvokeAsync(() => StatusMessage = "Connecting...", cancellationToken); + break; + } + case ConnectionState.Reconnecting: + { + await _dispatcher.InvokeAsync(() => StatusMessage = "Reconnecting...", cancellationToken); + break; + } + case ConnectionState.Connected: + { + await _dispatcher.InvokeAsync(() => StatusMessage = "Connected", cancellationToken); + break; + } + case ConnectionState.Disconnected: + { + await _dispatcher.InvokeAsync(() => StatusMessage = "Disconnected", cancellationToken); + break; + } + case ConnectionState.Error: + { + await _dispatcher.InvokeAsync(() => StatusMessage = "Error", cancellationToken); + break; + } + } + } + + private async ValueTask OnIdentityChangedAsync(IdentityChanged message, CancellationToken cancellationToken) + { + var format = ""; + for (var i = 0; i < message.Id.Length; i += 3) format += $"{message.Id.Substring(i, 3)} "; + + _lastId = format.Trim(); + + if (_lastId is not null) await _dispatcher.InvokeAsync(() => StatusMessage = _lastId, cancellationToken); + } + + public void Shutdown() + { + _dispatcher.ShutdownAsync(); + } + + private void ElevateToAdmin() + { + try + { + var commandLine = Win32Interop.GetCommandLine().Replace(" --elevate", ""); + var sections = commandLine.Split('"', StringSplitOptions.RemoveEmptyEntries); + var filePath = sections.First(); + var arguments = string.Join('"', sections.Skip(1)); + var psi = new ProcessStartInfo(filePath, arguments) + { + Verb = "RunAs", + UseShellExecute = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + Process.Start(psi); + Environment.Exit(0); + } + // Exception can be thrown if UAC is dialog is cancelled. + catch { } + } + + private void ElevateToService() + { + try + { + var psi = new ProcessStartInfo("cmd.exe") + { + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true + }; + + var commandLine = Win32Interop.GetCommandLine().Replace(" --elevate", ""); + var sections = commandLine.Split('"', StringSplitOptions.RemoveEmptyEntries); + var filePath = sections.First(); + var arguments = string.Join('"', sections.Skip(1)); + + _logger.LogInformation("Creating temporary service with file path {filePath} and arguments {arguments}.", + filePath, + arguments); + + psi.Arguments = $"/c sc create RemoteControl_Temp binPath=\"{filePath} {arguments} --elevate\""; + Process.Start(psi)?.WaitForExit(); + psi.Arguments = "/c sc start RemoteControl_Temp"; + Process.Start(psi)?.WaitForExit(); + psi.Arguments = "/c sc delete RemoteControl_Temp"; + Process.Start(psi)?.WaitForExit(); + + _dispatcher.ShutdownAsync(); + } + catch { } + } +} + +public class FakeMainViewModel : ViewModelBase +{ + public RelayCommand ElevateToAdminCommand { get; } = new(() => { }); + public RelayCommand ElevateToServiceCommand { get; } = new(() => { }); + + public bool IsAdministrator => false; + public string StatusMessage { get; set; } = "test"; +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/ViewModels/ViewModelBase.cs b/src/Remote/Insight.Remote.Windows/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..b1cccf8 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/ViewModels/ViewModelBase.cs @@ -0,0 +1,13 @@ +using Insight.Remote.Shared.Reactive; + +namespace Insight.Remote.Windows.ViewModels; + +public interface IViewModelBase +{ + string? ProductName { get; } +} + +public abstract class ViewModelBase : ObservableObject, IViewModelBase +{ + public string? ProductName { get; } = "Remote Control"; +} \ No newline at end of file diff --git a/src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml b/src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml new file mode 100644 index 0000000..01dc5c2 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml.cs b/src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml.cs new file mode 100644 index 0000000..f60aec3 --- /dev/null +++ b/src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml.cs @@ -0,0 +1,63 @@ +using Insight.Remote.Windows.ViewModels; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; +using Button = System.Windows.Controls.Button; +using MessageBox = System.Windows.MessageBox; + +namespace Insight.Remote.Windows.Views; + +public partial class MainWindow : Window +{ + public MainViewModel? ViewModel => DataContext as MainViewModel; + + public MainWindow() + { + InitializeComponent(); + } + + public MainWindow(MainViewModel viewModel) + { + DataContext = viewModel; + InitializeComponent(); + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + if (DesignerProperties.GetIsInDesignMode(this)) return; + if (DataContext is not MainViewModel vm) return; + } + + private void Window_Closing(object sender, CancelEventArgs e) + { + if (DataContext is not MainViewModel vm) return; + vm.Shutdown(); + } + + private void OptionsButton_Click(object sender, RoutedEventArgs e) + { + if (sender is not Button button) return; + button.ContextMenu.IsOpen = true; + } + + private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + DragMove(); + } + + private void ElevateToAdminMenuItem_Click(object sender, RoutedEventArgs e) + { + if (DataContext is not MainViewModel vm) return; + + if (vm.ElevateToAdminCommand.CanExecute(null)) vm.ElevateToAdminCommand.Execute(null); + else MessageBox.Show("Unable to execute in current state.", "Unable to Execute", MessageBoxButton.OK, MessageBoxImage.Warning); + } + + private void ElevateToServiceMenuItem_Click(object sender, RoutedEventArgs e) + { + if (DataContext is not MainViewModel vm) return; + + if (vm.ElevateToServiceCommand.CanExecute(null)) vm.ElevateToServiceCommand.Execute(null); + else MessageBox.Show("Unable to execute in current state.", "Unable to Execute", MessageBoxButton.OK, MessageBoxImage.Warning); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Constants/Appsettings.cs b/src/Server/Insight.Server/Constants/Appsettings.cs index 29f0dd7..07a59f5 100644 --- a/src/Server/Insight.Server/Constants/Appsettings.cs +++ b/src/Server/Insight.Server/Constants/Appsettings.cs @@ -1,14 +1,13 @@ -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"; +namespace Insight.Server; - internal const string WebServerPort = "web.server.port"; - internal const string WebServerCertificate = "web.server.certificate"; - internal const string WebServerCertificatePassword = "web.server.certificate.password"; - } +internal static class Appsettings +{ + internal const string AgentServerPort = "agent.server.port"; + internal const string AgentServerCertificate = "agent.server.certificate"; + internal const string AgentServerCertificatePassword = "agent.server.certificate.password"; + internal const string DispatchWebmatic = "dispatch.webmatic"; + + internal const string WebServerPort = "web.server.port"; + internal const string WebServerCertificate = "web.server.certificate"; + internal const string WebServerCertificatePassword = "web.server.certificate.password"; } \ No newline at end of file diff --git a/src/Server/Insight.Server/Extensions/Async.cs b/src/Server/Insight.Server/Extensions/Async.cs index 2b41b74..ed2cd7e 100644 --- a/src/Server/Insight.Server/Extensions/Async.cs +++ b/src/Server/Insight.Server/Extensions/Async.cs @@ -1,53 +1,52 @@ using System.Threading.Tasks.Dataflow; -namespace Insight.Server.Extensions +namespace Insight.Server.Extensions; + +public static class Async { - public static class Async + public static async Task ParallelForEach( + this IAsyncEnumerable source, + Func body, + int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded, + TaskScheduler scheduler = null) { - public static async Task ParallelForEach( - this IAsyncEnumerable source, - Func body, - int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded, - TaskScheduler scheduler = null) + var options = new ExecutionDataflowBlockOptions { - var options = new ExecutionDataflowBlockOptions - { - MaxDegreeOfParallelism = maxDegreeOfParallelism - }; + MaxDegreeOfParallelism = maxDegreeOfParallelism + }; - if (scheduler != null) - options.TaskScheduler = scheduler; + if (scheduler != null) + options.TaskScheduler = scheduler; - var block = new ActionBlock(body, options); + var block = new ActionBlock(body, options); - await foreach (var item in source) - block.Post(item); + await foreach (var item in source) + block.Post(item); - block.Complete(); - await block.Completion; - } + block.Complete(); + await block.Completion; + } - public static async Task ParallelForEach( - this IEnumerable source, - Func body, - int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded, - TaskScheduler scheduler = null) + public static async Task ParallelForEach( + this IEnumerable source, + Func body, + int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded, + TaskScheduler scheduler = null) + { + var options = new ExecutionDataflowBlockOptions { - var options = new ExecutionDataflowBlockOptions - { - MaxDegreeOfParallelism = maxDegreeOfParallelism - }; + MaxDegreeOfParallelism = maxDegreeOfParallelism + }; - if (scheduler != null) - options.TaskScheduler = scheduler; + if (scheduler != null) + options.TaskScheduler = scheduler; - var block = new ActionBlock(body, options); + var block = new ActionBlock(body, options); - foreach (var item in source) - block.Post(item); + foreach (var item in source) + block.Post(item); - block.Complete(); - await block.Completion; - } + block.Complete(); + await block.Completion; } } diff --git a/src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs b/src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs index 6f0abe0..3bc0aa5 100644 --- a/src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs +++ b/src/Server/Insight.Server/Extensions/ConfigurationExtensions.cs @@ -1,14 +1,13 @@ using Microsoft.Extensions.Configuration; -namespace Insight.Server.Extensions +namespace Insight.Server.Extensions; + +public static class ConfigurationExtensions { - public static class ConfigurationExtensions + public static IConfigurationBuilder Defaults(this IConfigurationBuilder configuration) { - public static IConfigurationBuilder Defaults(this IConfigurationBuilder configuration) - { - configuration.Sources.Clear(); - configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); - return configuration.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); - } + configuration.Sources.Clear(); + configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + return configuration.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); } } \ No newline at end of file diff --git a/src/Server/Insight.Server/Insight.Server.csproj b/src/Server/Insight.Server/Insight.Server.csproj index 8c489df..8d4b992 100644 --- a/src/Server/Insight.Server/Insight.Server.csproj +++ b/src/Server/Insight.Server/Insight.Server.csproj @@ -3,16 +3,12 @@ Exe net7.0 - latest Insight server - 2025.2.24.0 - 2025.2.24.0 + 2023.12.14.0 Insight.Server enable enable - none - true @@ -46,20 +42,10 @@ - - - - - - - - - - diff --git a/src/Server/Insight.Server/Models/MonitorMessage.cs b/src/Server/Insight.Server/Models/MonitorMessage.cs index e3eef9a..3886d21 100644 --- a/src/Server/Insight.Server/Models/MonitorMessage.cs +++ b/src/Server/Insight.Server/Models/MonitorMessage.cs @@ -1,27 +1,26 @@ -using Insight.Agent.Enums; +using Insight.Domain.Enums; -namespace Insight.Server.Models +namespace Insight.Server.Models; + +internal class MonitorMessage { - 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 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 - } + public enum ApplicationEnum + { + Unknown = 0, + Insight = 1, + Acronis = 2, + Veeam = 3, + QNAP = 4, + FreeNas = 5 } } diff --git a/src/Server/Insight.Server/Network/AgentSession.cs b/src/Server/Insight.Server/Network/Agent/AgentSession.cs similarity index 52% rename from src/Server/Insight.Server/Network/AgentSession.cs rename to src/Server/Insight.Server/Network/Agent/AgentSession.cs index 31871b6..e97f48e 100644 --- a/src/Server/Insight.Server/Network/AgentSession.cs +++ b/src/Server/Insight.Server/Network/Agent/AgentSession.cs @@ -1,19 +1,24 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Server.Network.Handlers.Agent; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; +using Insight.Server.Network.Agent.Handlers; using Microsoft.Extensions.Logging; -using System.Net.Sockets; using Vaitr.Network; -namespace Insight.Server.Network; +namespace Insight.Server.Network.Agent; -public class AgentSession(AgentHandler agentHandler, IEnumerable> handlers, Socket socket, Stream stream, TcpConnectionOptions options, MemPackSerializer serializer, ILogger logger) - : TcpSession(socket, stream, options, serializer, logger) +public class AgentSession : TcpSession { public string? Id { get; set; } - private readonly AgentHandler _agentHandler = agentHandler; - private readonly IEnumerable> _handlers = handlers; + private readonly AgentHandler _agentHandler; + private readonly IEnumerable> _handlers; + + public AgentSession(AgentHandler agentHandler, IEnumerable> handlers, ISerializer serializer, ILogger logger) : base(serializer, logger) + { + _agentHandler = agentHandler; + _handlers = handlers; + } protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken) { @@ -40,14 +45,18 @@ public class AgentSession(AgentHandler agentHandler, IEnumerable context, CancellationToken cancellationToken) + protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) { + await base.OnSentAsync(context, cancellationToken); + await _agentHandler.StatisticUpdateAsync(this, cancellationToken); } - protected override async ValueTask OnReceivedAsync(PacketContext context, CancellationToken cancellationToken) + protected override async ValueTask OnReceivedAsync(IPacketContext context, CancellationToken cancellationToken) { - if (Id is null && context.Data is not Authentication) return; + await base.OnReceivedAsync(context, cancellationToken); + + if (Id is null && context.Packet is not AuthenticationResponse) return; await _agentHandler.StatisticUpdateAsync(this, cancellationToken); @@ -55,23 +64,16 @@ public class AgentSession(AgentHandler agentHandler, IEnumerable +{ + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public AgentHandler(IMongoDatabase database, ILogger logger) + { + _database = database; + _logger = logger; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case AuthenticationRequest authenticationRequest: + await OnAuthenticationRequestAsync(sender, authenticationRequest, cancellationToken); + break; + case AuthenticationResponse authenticationResponse: + await OnAuthenticationResponseAsync(sender, authenticationResponse, cancellationToken); + break; + } + } + + private async ValueTask OnAuthenticationRequestAsync(AgentSession session, AuthenticationRequest message, CancellationToken cancellationToken) + { + await session.SendAsync(message, cancellationToken); + + for (int i = 0; i < 200; i++) + { + if (session.Id is not null) + { + _logger.LogInformation("Agent ({ep?}) authenticated", session.RemoteEndPoint); + return; + } + + await Task.Delay(50, cancellationToken).ConfigureAwait(false); + } + + _logger.LogError("Authentication Timeout ({ep?})", session.RemoteEndPoint); + session.Disconnect(); + } + + private async ValueTask OnAuthenticationResponseAsync(AgentSession session, AuthenticationResponse authentication, CancellationToken cancellationToken) + { + if (authentication is null) + { + throw new NullReferenceException($"authentication failed (empty response)"); + } + + if (authentication.Serial == default) + { + throw new InvalidDataException($"authentication failed ({nameof(authentication.Serial)})"); + } + + //if (authentication.Version == default || authentication.Version < Domain.Constants.Configuration.Version) + //{ + // throw new InvalidDataException($"authentication failed ({nameof(authentication.Version)})"); + //} + + // upsert agent + await _database.Agent().UpdateOneAsync(Builders + .Filter + .Eq(p => p.Serial, authentication.Serial.ToString()), Builders.Update + .SetOnInsert(p => p.Insert, DateTime.Now) + .SetOnInsert(p => p.Serial, authentication.Serial.ToString()) + .Set(p => p.Update, DateTime.Now) + .Set(p => p.Connected, DateTime.Now) + .Set(p => p.Version, authentication.Version) + .Set(p => p.Endpoint, session.RemoteEndPoint?.ToString()) + .Set(p => p.Hostname, authentication.Hostname), new UpdateOptions + { + IsUpsert = true + }, cancellationToken) + .ConfigureAwait(false); + + // get agent + var agentEntity = await _database.Agent() + .Find(Builders + .Filter + .Eq(p => p.Serial, authentication.Serial.ToString())) + .FirstOrDefaultAsync(cancellationToken) + .ConfigureAwait(false); + + // set session id + session.Id = agentEntity.Id; + } + + public async ValueTask ConnectedAsync(AgentSession session, CancellationToken cancellationToken) + { + if (session.Id is null) return; + + // insert connect log + await _database.AgentLog() + .InsertOneAsync(new AgentLogEntity + { + Insert = DateTime.Now, + Agent = session.Id, + Category = CategoryEnum.Network.ToString(), + Status = StatusEnum.Information.ToString(), + Message = $"Connected ({session.RemoteEndPoint})", + Timestamp = DateTime.Now + }, cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + public async ValueTask DisconnectedAsync(AgentSession session, CancellationToken cancellationToken) + { + if (session.Id is null) return; + + // insert disconnect log + await _database.AgentLog() + .InsertOneAsync(new AgentLogEntity + { + Insert = DateTime.Now, + Agent = session.Id, + Category = CategoryEnum.Network.ToString(), + Status = StatusEnum.Information.ToString(), + Message = $"Disconnected ({session.RemoteEndPoint})", + Timestamp = DateTime.Now + }, cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + public async ValueTask StatisticUpdateAsync(AgentSession session, CancellationToken cancellationToken) + { + if (session.Id is null) return; + + // update agents stats + await _database.Agent().UpdateOneAsync(Builders + .Filter + .Eq(p => p.Id, session.Id), Builders + .Update + .Set(p => p.Update, DateTime.Now) + .Set(p => p.Activity, session.Activity) + .Set(p => p.SentBytes, session.SentBytes) + .Set(p => p.ReceivedBytes, session.ReceivedBytes) + .Set(p => p.SentPackets, session.SentPackets) + .Set(p => p.ReceivedPackets, session.ReceivedPackets), null, cancellationToken) + .ConfigureAwait(false); + } + + public async ValueTask LogAsync(AgentSession session, AgentLogEntity log, CancellationToken cancellationToken) + { + if (session.Id is null) return; + + await _database.AgentLog() + .InsertOneAsync(log, cancellationToken: cancellationToken) + .ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/CustomHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/CustomHandler.cs new file mode 100644 index 0000000..885b94c --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/CustomHandler.cs @@ -0,0 +1,31 @@ +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 : IMessageHandler +{ + private readonly ILogger _logger; + + public CustomHandler(ILogger logger) + { + _logger = logger; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Response response: + await OnResponseAsync(sender, response, cancellationToken); + break; + } + } + + private async ValueTask OnResponseAsync(AgentSession sender, Response response, CancellationToken cancellationToken) + { + _logger.LogWarning($"Response: {response.ResponseData}"); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/DriveHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/DriveHandler.cs new file mode 100644 index 0000000..bc1323d --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/DriveHandler.cs @@ -0,0 +1,146 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public DriveHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection drives: + await OnDrivesAsync(sender, drives, cancellationToken); + break; + } + } + + private async ValueTask OnDrivesAsync(AgentSession session, List drives, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var driveBulk = new List>(); + + if (drives is not null && drives.Any()) + { + foreach (var drive in drives) + { + var driveFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Index, drive.Index) + }); + + var driveUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Index, drive.Index) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch) + .Set(p => p.Company, drive.Manufacturer) + .Set(p => p.Name, drive.Name) + .Set(p => p.Size, drive.Size) + .Set(p => p.Type, drive.InterfaceType) + .Set(p => p.Serial, drive.SerialNumber) + .Set(p => p.Firmware, drive.FirmwareRevision) + .Set(p => p.Status, drive.Status) + .Set(p => p.Pnp, drive.PNPDeviceID); + + driveBulk.Add(new UpdateOneModel(driveFilter, driveUpdate) + { + IsUpsert = true + }); + } + } + + driveBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var driveResult = await _database.HostDrive().BulkWriteAsync(driveBulk, cancellationToken: cancellationToken); + + // volumes + + var volumeBulk = new List>(); + + if (drives is not null && drives.Any()) + { + 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(); + + if (drive.Volumes is not null && drive.Volumes.Any()) + { + foreach (var volume in drive.Volumes) + { + var volumeFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Drive, driveId), + Builders.Filter.Eq(x => x.Index, volume.Index) + }); + + var volumeUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Drive, driveId) + .SetOnInsert(p => p.Index, volume.Index) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch) + .Set(p => p.Name, volume.Name) + .Set(p => p.Label, volume.Id) + .Set(p => p.Serial, volume.SerialNumber) + .Set(p => p.Size, volume.Size) + .Set(p => p.FreeSpace, volume.FreeSpace) + .Set(p => p.Type, volume.Type) + .Set(p => p.FileSystem, volume.FileSystem) + .Set(p => p.Compressed, volume.Compressed) + .Set(p => p.Bootable, volume.Bootable) + .Set(p => p.Primary, volume.PrimaryPartition) + .Set(p => p.Boot, volume.Bootable) + .Set(p => p.BlockSize, volume.BlockSize) + .Set(p => p.Blocks, volume.NumberOfBlocks) + .Set(p => p.StartingOffset, volume.StartingOffset) + .Set(p => p.Provider, volume.ProviderName); + + volumeBulk.Add(new UpdateOneModel(volumeFilter, volumeUpdate) + { + IsUpsert = true + }); + } + } + } + } + + volumeBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var volumeResult = await _database.HostVolume().BulkWriteAsync(volumeBulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/EventHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/EventHandler.cs new file mode 100644 index 0000000..ed4bdec --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/EventHandler.cs @@ -0,0 +1,267 @@ +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 static Insight.Domain.Network.Agent.Messages.Event; + +namespace Insight.Server.Network.Agent.Handlers; + +public class EventHandler : IMessageHandler +{ + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public EventHandler(IMongoDatabase database, ILogger logger) + { + _database = database; + _logger = logger; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Event @event: + await OnEventAsync(sender, @event, cancellationToken); + break; + } + } + + private async ValueTask OnEventAsync(AgentSession session, Event @event, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + if (FilterEventId(@event)) return; + + var hostLog = await InsertHostLogAsync(hostEntity, @event, cancellationToken); + + if (hostLog is null || FilterMonitoringHostLog(hostLog)) return; + + await _database.HostLogMonitoring() + .InsertOneAsync(new HostLogMonitoringEntity + { + Host = hostEntity.Id, + Insert = hostLog.Insert, + Timestamp = hostLog.Timestamp, + Category = hostLog.Category, + Status = hostLog.Status, + Message = hostLog.Message, + Dispatch = DispatchEnum.Pending.ToString() + }, cancellationToken: cancellationToken); + } + + private async ValueTask InsertAgentLogAsync(AgentSession session, Event @event, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent() + .Aggregate() + .Match(Builders.Filter.Eq(p => p.Id, session.Id)) + .Lookup(_database.Host(), p => p.Serial, p => p.Agent, p => p.Hosts) + .FirstOrDefaultAsync(cancellationToken); + + if (agentEntity is null) return null; + + StatusEnum? status = @event.Status switch + { + StatusType.Information => StatusEnum.Information, + StatusType.Warning => StatusEnum.Warning, + StatusType.Error => StatusEnum.Error, + _ => null + }; + + CategoryEnum? category = @event.Category.ToLower() switch + { + "network" => CategoryEnum.Network, + "application" => CategoryEnum.Application, + "security" => CategoryEnum.Security, + "system" => CategoryEnum.System, + _ => null + }; + + var date = DateTime.Now; + + var log = new AgentLogEntity + { + Insert = date, + Agent = agentEntity.Id, + Timestamp = @event.Timestamp, + EventId = @event.EventId.ToString(), + Source = @event.Source, + Status = status.ToString(), + Category = category.ToString(), + Message = @event.Message + }; + + await _database.AgentLog().InsertOneAsync(log, cancellationToken: cancellationToken); + return log; + } + + private async ValueTask InsertHostLogAsync(HostEntity hostEntity, Event @event, CancellationToken cancellationToken) + { + StatusEnum? status = @event.Status switch + { + StatusType.Information => StatusEnum.Information, + StatusType.Warning => StatusEnum.Warning, + StatusType.Error => StatusEnum.Error, + StatusType.Critical => StatusEnum.Error, + StatusType.Unknown => null, + _ => null + }; + + CategoryEnum? category = 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 bool FilterEventId(Event @event) + { + var filter = new List + { + 0, + 3, + 4, + 16, + 37, + 900, + 902, + 903, + 1001, + 1003, + 1008, + 1023, + 1066, + 4624, + 4625, + 4634, + 4648, + 4672, + 4776, + 4798, + 4799, + 5058, + 5059, + 5061, + 5379, + 5612, + 5781, + //7036, + 8014, + 8016, + 8019, + 8194, + 8224, + 9009, + 9027, + 10001, + 10016, + 16384, + 16394, + 36874, + 36888, + }; + + if (filter.Any(p => p == @event.EventId)) return true; + return false; + } + + private bool FilterMonitoringHostLog(HostLogEntity hostLog) + { + //_logger.LogDebug($"try filter event: {hostLog.Category}.{hostLog.Source}.{hostLog.Status}"); + + if (Enum.TryParse(hostLog.Status, out StatusType status) is false) return true; + + if (hostLog.Category == CategoryEnum.System.ToString()) + { + if (hostLog.Source == "Service Control Manager" && status < StatusType.Warning) return true; + } + + if (hostLog.Category == CategoryEnum.Task.ToString()) + { + if (status < StatusType.Error) return true; + } + + // skip rdp infos + if (hostLog.Category == CategoryEnum.RDP.ToString()) + { + if (hostLog.EventId == "261") return true; + } + + // skip smbclient (veeam errors) + if (hostLog.Category == CategoryEnum.Security.ToString()) + { + if (hostLog.Source == "Microsoft-Windows-SMBClient") return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/InterfaceHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/InterfaceHandler.cs new file mode 100644 index 0000000..70f4579 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/InterfaceHandler.cs @@ -0,0 +1,300 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public InterfaceHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection interfaces: + await OnInterfacesAsync(sender, interfaces, cancellationToken); + break; + } + } + + private async ValueTask OnInterfacesAsync(AgentSession session, List interfaces, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + // interfaces + + if (interfaces is not null && interfaces.Any()) + { + var interfaceBulk = new List>(); + + foreach (var @interface in interfaces) + { + var interfaceFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Index, @interface.Index) + }); + + var interfaceUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Index, @interface.Index) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch) + + .Set(p => p.Mac, @interface?.Mac) + .Set(p => p.Name, @interface?.Name) + .Set(p => p.Description, @interface?.Description) + .Set(p => p.Physical, @interface?.Physical) + .Set(p => p.Status, @interface?.Status?.ToString()) + .Set(p => p.Suffix, @interface?.Suffix) + .Set(p => p.Speed, @interface?.Speed) + .Set(p => p.Ipv4Mtu, @interface?.Ipv4Mtu) + .Set(p => p.Ipv4Dhcp, @interface?.Ipv4Dhcp) + + .Set(p => p.Ipv4Forwarding, @interface?.Ipv4Forwarding) + .Set(p => p.Ipv6Mtu, @interface?.Ipv6Mtu) + .Set(p => p.Sent, @interface?.Sent) + .Set(p => p.Received, @interface?.Received) + .Set(p => p.IncomingPacketsDiscarded, @interface?.IncomingPacketsDiscarded) + .Set(p => p.IncomingPacketsWithErrors, @interface?.IncomingPacketsWithErrors) + .Set(p => p.IncomingUnknownProtocolPackets, @interface?.IncomingUnknownProtocolPackets) + .Set(p => p.OutgoingPacketsDiscarded, @interface?.OutgoingPacketsDiscarded) + .Set(p => p.OutgoingPacketsWithErrors, @interface?.OutgoingPacketsWithErrors); + + interfaceBulk.Add(new UpdateOneModel(interfaceFilter, interfaceUpdate) + { + IsUpsert = true + }); + } + + interfaceBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var interfaceResult = await _database.HostInterface().BulkWriteAsync(interfaceBulk, cancellationToken: cancellationToken); + } + + // addresses + + if (interfaces is not null && interfaces.Any()) + { + var addressBulk = new List>(); + + foreach (var @interface in interfaces) + { + var interfaceId = await _database.HostInterface() + .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) + .Project(p => p.Id) + .FirstOrDefaultAsync(); + + if (@interface.Addresses is not null && @interface.Addresses.Any()) + { + foreach (var address in @interface.Addresses) + { + var addressFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Interface, interfaceId), + Builders.Filter.Eq(x => x.Address, address?.IpAddress?.Address), + Builders.Filter.Eq(x => x.Mask, address?.Ipv4Mask?.Address) + }); + + var addressUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Interface, interfaceId) + .SetOnInsert(p => p.Address, address?.IpAddress?.Address) + .SetOnInsert(p => p.Mask, address?.Ipv4Mask?.Address) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch); + + addressBulk.Add(new UpdateOneModel(addressFilter, addressUpdate) + { + IsUpsert = true + }); + } + } + } + + addressBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var addressResult = await _database.HostInterfaceAddress().BulkWriteAsync(addressBulk, cancellationToken: cancellationToken); + } + + // gateways + + if (interfaces is not null && interfaces.Any()) + { + var gatewayBulk = new List>(); + + foreach (var @interface in interfaces) + { + var interfaceId = await _database.HostInterface() + .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) + .Project(p => p.Id) + .FirstOrDefaultAsync(); + + if (@interface.Gateways is not null && @interface.Gateways.Any()) + { + foreach (var gateway in @interface.Gateways) + { + var gatewayFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Interface, interfaceId), + Builders.Filter.Eq(x => x.Address, gateway?.Address) + }); + + var gatewayUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Interface, interfaceId) + .SetOnInsert(p => p.Address, gateway?.Address) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch); + + gatewayBulk.Add(new UpdateOneModel(gatewayFilter, gatewayUpdate) + { + IsUpsert = true + }); + } + } + } + + gatewayBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var gatewayResult = await _database.HostInterfaceGateway().BulkWriteAsync(gatewayBulk, cancellationToken: cancellationToken); + } + + // nameservers + + if (interfaces is not null && interfaces.Any()) + { + var nameserverBulk = new List>(); + + foreach (var @interface in interfaces) + { + var interfaceId = await _database.HostInterface() + .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) + .Project(p => p.Id) + .FirstOrDefaultAsync(); + + if (@interface.Dns is not null && @interface.Dns.Any()) + { + foreach (var nameserver in @interface.Dns) + { + var nameserverFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Interface, interfaceId), + Builders.Filter.Eq(x => x.Address, nameserver?.Address) + }); + + var nameserverUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Interface, interfaceId) + .SetOnInsert(p => p.Address, nameserver?.Address) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch); + + nameserverBulk.Add(new UpdateOneModel(nameserverFilter, nameserverUpdate) + { + IsUpsert = true + }); + } + } + } + + nameserverBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var nameserverResult = await _database.HostInterfaceNameserver().BulkWriteAsync(nameserverBulk, cancellationToken: cancellationToken); + } + + // routes + + if (interfaces is not null && interfaces.Any()) + { + var routeBulk = new List>(); + + foreach (var @interface in interfaces) + { + var interfaceId = await _database.HostInterface() + .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) + .Project(p => p.Id) + .FirstOrDefaultAsync(); + + if (@interface.Routes is not null && @interface.Routes.Any()) + { + foreach (var route in @interface.Routes) + { + var routeFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Interface, interfaceId), + Builders.Filter.Eq(x => x.Destination, route?.Destination?.Address), + Builders.Filter.Eq(x => x.Gateway, route?.Gateway?.Address), + Builders.Filter.Eq(x => x.Mask, route?.Mask), + }); + + var routeUpdate = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Interface, interfaceId) + .SetOnInsert(p => p.Destination, route?.Destination?.Address) + .SetOnInsert(p => p.Gateway, route?.Gateway?.Address) + .SetOnInsert(p => p.Mask, route?.Mask) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch) + + .Set(p => p.Metric, route?.Metric); + + routeBulk.Add(new UpdateOneModel(routeFilter, routeUpdate) + { + IsUpsert = true + }); + } + } + } + + routeBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var routeResult = await _database.HostInterfaceRoute().BulkWriteAsync(routeBulk, cancellationToken: cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/MainboardHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/MainboardHandler.cs similarity index 80% rename from src/Server/Insight.Server/Network/Handlers/Agent/MainboardHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/MainboardHandler.cs index 10e6121..fcbc2b0 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/MainboardHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/MainboardHandler.cs @@ -1,12 +1,12 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; +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.Handlers.Agent +namespace Insight.Server.Network.Agent.Handlers { - public class MainboardHandler : IAgentMessageHandler + public class MainboardHandler : IMessageHandler { private readonly IMongoDatabase _database; @@ -15,11 +15,13 @@ namespace Insight.Server.Network.Handlers.Agent _database = database; } - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is Mainboard mainboard) + switch (message) { - await OnMainboardAsync(sender, mainboard, cancellationToken); + case Mainboard mainboard: + await OnMainboardAsync(sender, mainboard, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/MemoryHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/MemoryHandler.cs new file mode 100644 index 0000000..b6f1b33 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/MemoryHandler.cs @@ -0,0 +1,84 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public MemoryHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection memory: + await OnMemoryAsync(sender, memory, cancellationToken); + break; + } + } + + private async ValueTask OnMemoryAsync(AgentSession session, List memory, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (memory is not null && memory.Any()) + { + foreach (var mem in memory) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Index, mem.Index) + }); + + var updateDefinition = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Index, mem.Index) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch) + .Set(p => p.Company, mem.Manufacturer) + .Set(p => p.Name, mem.Model) + .Set(p => p.Tag, mem.Tag) + .Set(p => p.Location, mem.Location) + .Set(p => p.Serial, mem.Serial) + .Set(p => p.Capacity, mem.Capacity) + .Set(p => p.Clock, mem.Speed) + .Set(p => p.CurrentClock, mem.ConfiguredSpeed) + .Set(p => p.Voltage, mem.Voltage) + .Set(p => p.CurrentVoltage, mem.ConfiguredVoltage); + + bulk.Add(new UpdateOneModel(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostMemory().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/OperationSystemHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/OperationSystemHandler.cs new file mode 100644 index 0000000..eb8b7af --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/OperationSystemHandler.cs @@ -0,0 +1,52 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public OperationSystemHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case OperationSystem os: + await OnOperationSystemAsync(sender, os, cancellationToken); + break; + } + } + + private async ValueTask OnOperationSystemAsync(AgentSession session, OperationSystem operatingSystem, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var date = DateTime.Now; + + await _database.HostOs().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .Set(p => p.Update, date) + .Set(p => p.Name, operatingSystem.Name) + .Set(p => p.Version, operatingSystem.Version) + .Set(p => p.Architecture, operatingSystem.Architecture.ToString()) + .Set(p => p.SerialNumber, operatingSystem.SerialNumber) + .Set(p => p.Virtual, operatingSystem.Virtual) + .Set(p => p.Installed, operatingSystem.InstallDate), new UpdateOptions + { + IsUpsert = true + }, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/PrinterHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/PrinterHandler.cs new file mode 100644 index 0000000..83a53cd --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/PrinterHandler.cs @@ -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 PrinterHandler : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public PrinterHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection printers: + await OnPrintersAsync(sender, printers, cancellationToken); + break; + } + } + + private async ValueTask OnPrintersAsync(AgentSession session, List printers, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (printers is not null && printers.Any()) + { + foreach (var printer in printers) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Name, printer.Name) + }); + + var updateDefinition = Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .SetOnInsert(p => p.Name, printer.Name) + .Set(p => p.Update, date) + .Set(p => p.Batch, batch) + .Set(p => p.Port, printer.Port) + .Set(p => p.Location, printer.Location) + .Set(p => p.Comment, printer.Comment); + + bulk.Add(new UpdateOneModel(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostPrinter().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/ProcessorHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/ProcessorHandler.cs new file mode 100644 index 0000000..67b3ffb --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/ProcessorHandler.cs @@ -0,0 +1,88 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public ProcessorHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection processors: + await OnProcessorsAsync(sender, processors, cancellationToken); + break; + } + } + + private async ValueTask OnProcessorsAsync(AgentSession session, List processors, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (processors is not null && processors.Any()) + { + foreach (var processor in processors) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Index, processor.Index) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostProcessor().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/ServiceHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/ServiceHandler.cs new file mode 100644 index 0000000..9df3799 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/ServiceHandler.cs @@ -0,0 +1,82 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public ServiceHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection services: + await OnServicesAsync(sender, services, cancellationToken); + break; + } + } + + private async ValueTask OnServicesAsync(AgentSession session, List services, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (services is not null && services.Any()) + { + foreach (var service in services) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Name, service.Name) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostService().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/SessionHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/SessionHandler.cs new file mode 100644 index 0000000..0dfb293 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/SessionHandler.cs @@ -0,0 +1,78 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public SessionHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection sessions: + await OnSessionsAsync(sender, sessions, cancellationToken); + break; + } + } + + private async ValueTask OnSessionsAsync(AgentSession session, List sessions, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (sessions is not null && sessions.Any()) + { + foreach (var sess in sessions) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Sid, sess.Sid) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostSession().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/SoftwareHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/SoftwareHandler.cs new file mode 100644 index 0000000..f236ae6 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/SoftwareHandler.cs @@ -0,0 +1,79 @@ +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; +using Insight.Infrastructure.Entities; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Insight.Server.Network.Agent.Handlers; + +public class SoftwareHandler : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public SoftwareHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection applications: + await OnApplicationsAsync(sender, applications, cancellationToken); + break; + } + } + + private async ValueTask OnApplicationsAsync(AgentSession session, List applications, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (applications is not null && applications.Any()) + { + foreach (var app in applications) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Name, app.Name), + Builders.Filter.Eq(x => x.Architecture, app.Architecture?.ToString()) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostApplication().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/StoragePoolHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/StoragePoolHandler.cs new file mode 100644 index 0000000..712897f --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/StoragePoolHandler.cs @@ -0,0 +1,254 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public StoragePoolHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection storagePools: + await OnStoragePoolsAsync(sender, storagePools, cancellationToken); + break; + } + } + + private async ValueTask OnStoragePoolsAsync(AgentSession session, List? storagePools, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + // storagepools + + if (storagePools is not null && storagePools.Any()) + { + var storagepoolBulk = new List>(); + + foreach (var storagePool in storagePools) + { + var storagePoolFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.UniqueId, storagePool.UniqueId) + }); + + List? states = null; + + if (storagePool.States is not null) + { + states = new List(); + + foreach (var state in storagePool.States) + { + states.Add(state.ToString()); + } + } + + var storagePoolUpdate = Builders.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(storagePoolFilter, storagePoolUpdate) + { + IsUpsert = true + }); + } + + storagepoolBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var storagePoolResult = await _database.HostStoragePool().BulkWriteAsync(storagepoolBulk, cancellationToken: cancellationToken); + } + + // physicaldisks + + if (storagePools is not null && storagePools.Any()) + { + var physicalDiskBulk = new List>(); + + 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(); + + if (storagePool.PhysicalDisks is not null && storagePool.PhysicalDisks.Any()) + { + foreach (var physicalDisk in storagePool.PhysicalDisks) + { + var physicalDiskFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.StoragePool, storagePoolId), + Builders.Filter.Eq(x => x.UniqueId, physicalDisk.UniqueId) + }); + + List? states = null; + + if (physicalDisk.States is not null) + { + states = new List(); + + foreach (var state in physicalDisk.States) + { + states.Add(state.ToString()); + } + } + + var physicalDiskUpdate = Builders.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(physicalDiskFilter, physicalDiskUpdate) + { + IsUpsert = true + }); + } + } + } + + physicalDiskBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var physicalDiskResult = await _database.HostStoragePoolPhysicalDisk().BulkWriteAsync(physicalDiskBulk, cancellationToken: cancellationToken); + } + + // virtual disks + + if (storagePools is not null && storagePools.Any()) + { + var virtualDiskBulk = new List>(); + + foreach (var storagePool in storagePools) + { + if (storagePool.VirtualDisks is not null && storagePool.VirtualDisks.Any()) + { + 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(); + + var virtualDiskFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.StoragePool, storagePoolId), + Builders.Filter.Eq(x => x.UniqueId, virtualDisk.UniqueId) + }); + + List? states = null; + + if (virtualDisk.States is not null) + { + states = new List(); + + foreach (var state in virtualDisk.States) + { + states.Add(state.ToString()); + } + } + + var virtualDiskUpdate = Builders.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(virtualDiskFilter, virtualDiskUpdate) + { + IsUpsert = true + }); + } + } + } + + virtualDiskBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var virtualDiskResult = await _database.HostStoragePoolVirtualDisk().BulkWriteAsync(virtualDiskBulk, cancellationToken: cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/SystemInfoHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/SystemInfoHandler.cs new file mode 100644 index 0000000..3f11402 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/SystemInfoHandler.cs @@ -0,0 +1,50 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public SystemInfoHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(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.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var date = DateTime.Now; + + await _database.HostSystem().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders.Update + .SetOnInsert(p => p.Insert, date) + .SetOnInsert(p => p.Host, hostEntity.Id) + .Set(p => p.Update, date) + .Set(p => p.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); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs new file mode 100644 index 0000000..de0aeae --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs @@ -0,0 +1,290 @@ +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 class TrapHandler : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public TrapHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(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.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + if (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.Any() is false) return false; + + monitoring = new MonitorMessage + { + Community = packet.Community, + Category = CategoryEnum.Monitoring, + Endpoint = packet.Endpoint, + Timestamp = packet.Timestamp + }; + + if (Enum.TryParse(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; + } + + private static bool ParseAcronis(List> 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 parsed = false; + + try + { + var trim = data[1].Value.Split(new string[] { ":" }, StringSplitOptions.None); + task = trim[1].Split(new string[] { "'" }, StringSplitOptions.None)[1].Split("'")[0].Trim(); + message = trim[1].Split(new string[] { "' " }, StringSplitOptions.None)[1].Trim(); + + parsed = true; + } + catch (Exception) + { + // skipped for base64 parse + } + + if (parsed) return true; + + try + { + var content = Regex.Replace(data[1].Value, @"\s+", ""); + 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); + + var trim = content.Split(new string[] { ":" }, StringSplitOptions.None); + task = trim[1].Split(new string[] { "'" }, StringSplitOptions.None)[1].Split("'")[0].Trim(); + message = trim[1].Split(new string[] { "' " }, StringSplitOptions.None)[1].Trim(); + + parsed = true; + } + catch (Exception ex) + { + //_logger.LogError("{ex}", ex); + } + + if (parsed) return true; + return false; + } + + private static bool ParseVeeam(List> 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> 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 + { + { "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 ex) + { + //_logger.LogError("{ex}", ex); + } + + if (parsed) return true; + return false; + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/UpdateHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/UpdateHandler.cs new file mode 100644 index 0000000..7f90eca --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/UpdateHandler.cs @@ -0,0 +1,122 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public UpdateHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(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.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (updates is not null) + { + if (updates.Installed is not null && updates.Installed.Any()) + { + foreach (var update in updates.Installed) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Serial, update.Id) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + + if (updates.Pending is not null && updates.Pending.Any()) + { + foreach (var update in updates.Pending) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Serial, update.Id) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + } + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostUpdate().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/UserHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/UserHandler.cs new file mode 100644 index 0000000..824117f --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/UserHandler.cs @@ -0,0 +1,189 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public UserHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection users: + await OnUsersAsync(sender, users, cancellationToken); + break; + } + } + + private async ValueTask OnUsersAsync(AgentSession session, List? users, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + // users + + if (users is not null && users.Any()) + { + var userBulk = new List>(); + + foreach (var user in users) + { + var userFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Domain, user?.Domain), + Builders.Filter.Eq(x => x.Name, user?.Name) + }); + + var userUpdate = Builders.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(userFilter, userUpdate) + { + IsUpsert = true + }); + } + + userBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var userResult = await _database.HostSystemUser().BulkWriteAsync(userBulk, cancellationToken: cancellationToken); + } + + // groups + + if (users is not null && users.Any()) + { + var groupBulk = new List>(); + + 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.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Domain, group?.Domain), + Builders.Filter.Eq(x => x.Name, group?.Name) + }); + + var groupUpdate = Builders.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(groupFilter, groupUpdate) + { + IsUpsert = true + }); + } + + groupBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var groupResult = await _database.HostSystemGroup().BulkWriteAsync(groupBulk, cancellationToken: cancellationToken); + } + + // relations + + if (users is not null && users.Any()) + { + var relationBulk = new List>(); + + 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(); + + if (user.Groups is not null && user.Groups.Any()) + { + 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(); + + var relationFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.User, userId), + Builders.Filter.Eq(x => x.Group, groupId) + }); + + var relationUpdate = Builders.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(relationFilter, relationUpdate) + { + IsUpsert = true + }); + } + } + } + + relationBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var relationResult = await _database.HostSystemUserSystemGroup().BulkWriteAsync(relationBulk, cancellationToken: cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/VideocardHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/VideocardHandler.cs new file mode 100644 index 0000000..ca0fba9 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/VideocardHandler.cs @@ -0,0 +1,78 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public VideocardHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection videocards: + await OnVideocardsAsync(sender, videocards, cancellationToken); + break; + } + } + + private async ValueTask OnVideocardsAsync(AgentSession session, List? videocards, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + var bulk = new List>(); + + if (videocards is not null && videocards.Any()) + { + foreach (var videocard in videocards) + { + var filterDefinition = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.Name, videocard.Model) + }); + + var updateDefinition = Builders.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(filterDefinition, updateDefinition) + { + IsUpsert = true + }); + } + + bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var result = await _database.HostVideocard().BulkWriteAsync(bulk, cancellationToken: cancellationToken); + } + } +} diff --git a/src/Server/Insight.Server/Network/Agent/Handlers/VirtualMaschineHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/VirtualMaschineHandler.cs new file mode 100644 index 0000000..6737b81 --- /dev/null +++ b/src/Server/Insight.Server/Network/Agent/Handlers/VirtualMaschineHandler.cs @@ -0,0 +1,174 @@ +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 : IMessageHandler +{ + private readonly IMongoDatabase _database; + + public VirtualMaschineHandler(IMongoDatabase database) + { + _database = database; + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Collection virtualMaschines: + await OnVirtualMaschinesAsync(sender, virtualMaschines, cancellationToken); + break; + } + } + + private async ValueTask OnVirtualMaschinesAsync(AgentSession session, List? virtualMaschines, CancellationToken cancellationToken) + { + var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); + if (agentEntity is null) return; + + var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); + if (hostEntity is null) return; + + var batch = ObjectId.GenerateNewId().ToString(); + var date = DateTime.Now; + + // virtual maschines + if (virtualMaschines is not null && virtualMaschines.Any()) + { + var virtualMaschineBulk = new List>(); + + foreach (var virtualMaschine in virtualMaschines) + { + var virtualMaschineFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.UniqueId, virtualMaschine.Id.ToString()) + }); + + var virtualMaschineUpdate = Builders.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(virtualMaschineFilter, virtualMaschineUpdate) + { + IsUpsert = true + }); + } + + virtualMaschineBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var virtualMaschineResult = await _database.HostHypervisorVirtualMaschine().BulkWriteAsync(virtualMaschineBulk, cancellationToken: cancellationToken); + } + + // virtual maschine configurations + + if (virtualMaschines is not null && virtualMaschines.Any()) + { + var configurationBulk = new List>(); + + 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(); + + if (virtualmaschine.Configurations is not null && virtualmaschine.Configurations.Any()) + { + foreach (var config in virtualmaschine.Configurations) + { + var configFilter = Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Eq(x => x.VirtualMaschine, virtualMaschineId), + Builders.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; + + var configUpdate = Builders.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(configFilter, configUpdate) + { + IsUpsert = true + }); + } + } + } + + configurationBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + { + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) + }))); + + var configurationResult = await _database.HostVirtualMaschineConfig().BulkWriteAsync(configurationBulk, cancellationToken: cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/AgentHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/AgentHandler.cs deleted file mode 100644 index d3f08be..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/AgentHandler.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Insight.Agent.Enums; -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class AgentHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public AgentHandler(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is AuthenticationRequest authenticationRequest) - { - await AuthenticationRequestAsync(sender, authenticationRequest, cancellationToken); - } - - if (message is Authentication authentication) - { - await AuthenticationAsync(sender, authentication, cancellationToken); - } - } - - private async ValueTask AuthenticationRequestAsync(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 AuthenticationAsync(AgentSession session, Authentication authentication, CancellationToken cancellationToken) - { - if (authentication is null) - { - throw new NullReferenceException($"authentication failed (empty response)"); - } - - if (authentication.Serial == default) - { - throw new InvalidDataException($"authentication failed ({nameof(authentication.Serial)})"); - } - - //if (authentication.Version == default || authentication.Version < Domain.Constants.Configuration.Version) - //{ - // throw new InvalidDataException($"authentication failed ({nameof(authentication.Version)})"); - //} - - // upsert agent - await _database.Agent().UpdateOneAsync(Builders - .Filter - .Eq(p => p.Serial, authentication.Serial.ToString()), Builders.Update - .SetOnInsert(p => p.Insert, DateTime.Now) - .SetOnInsert(p => p.Serial, authentication.Serial.ToString()) - .Set(p => p.Update, DateTime.Now) - .Set(p => p.Connected, DateTime.Now) - .Set(p => p.Version, authentication.Version) - .Set(p => p.Endpoint, session.RemoteEndPoint?.ToString()) - .Set(p => p.Hostname, authentication.Hostname), new UpdateOptions - { - IsUpsert = true - }, cancellationToken) - .ConfigureAwait(false); - - // get agent - var agentEntity = await _database.Agent() - .Find(Builders - .Filter - .Eq(p => p.Serial, authentication.Serial.ToString())) - .FirstOrDefaultAsync(cancellationToken) - .ConfigureAwait(false); - - // set session id - session.Id = agentEntity.Id; - } - - public async ValueTask ConnectedAsync(AgentSession session, CancellationToken cancellationToken) - { - if (session.Id is null) return; - - // insert connect log - await _database.AgentLog() - .InsertOneAsync(new AgentLogEntity - { - Insert = DateTime.Now, - Agent = session.Id, - Category = CategoryEnum.Network.ToString(), - Status = StatusEnum.Information.ToString(), - Message = $"Connected ({session.RemoteEndPoint})", - Timestamp = DateTime.Now - }, cancellationToken: cancellationToken) - .ConfigureAwait(false); - } - - public async ValueTask DisconnectedAsync(AgentSession session, CancellationToken cancellationToken) - { - if (session.Id is null) return; - - // insert disconnect log - await _database.AgentLog() - .InsertOneAsync(new AgentLogEntity - { - Insert = DateTime.Now, - Agent = session.Id, - Category = CategoryEnum.Network.ToString(), - Status = StatusEnum.Information.ToString(), - Message = $"Disconnected ({session.RemoteEndPoint})", - Timestamp = DateTime.Now - }, cancellationToken: cancellationToken) - .ConfigureAwait(false); - } - - public async ValueTask StatisticUpdateAsync(AgentSession session, CancellationToken cancellationToken) - { - if (session.Id is null) return; - - // update agents stats - await _database.Agent().UpdateOneAsync(Builders - .Filter - .Eq(p => p.Id, session.Id), Builders - .Update - .Set(p => p.Update, DateTime.Now) - .Set(p => p.Activity, session.Activity) - .Set(p => p.SentBytes, session.TotalOutputBytes) - .Set(p => p.ReceivedBytes, session.TotalInputBytes) - .Set(p => p.SentPackets, session.TotalOutputPackets) - .Set(p => p.ReceivedPackets, session.TotalInputPackets), null, cancellationToken) - .ConfigureAwait(false); - } - - public async ValueTask LogAsync(AgentSession session, AgentLogEntity log, CancellationToken cancellationToken) - { - if (session.Id is null) return; - - await _database.AgentLog() - .InsertOneAsync(log, cancellationToken: cancellationToken) - .ConfigureAwait(false); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/ConsoleHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/ConsoleHandler.cs deleted file mode 100644 index 2d9946e..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/ConsoleHandler.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Web.Messages; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; -using Vaitr.Network; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class ConsoleHandler : IAgentMessageHandler - { - private readonly TcpSessionPool _webPool; - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public ConsoleHandler( - TcpSessionPool webPool, - IMongoDatabase database, - ILogger logger) - { - _webPool = webPool; - _database = database; - _logger = logger; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is ConsoleQuery consoleQuery) - { - await OnConsoleQueryAsync(sender, consoleQuery, cancellationToken); - } - } - - private async ValueTask OnConsoleQueryAsync(AgentSession session, ConsoleQuery query, CancellationToken cancellationToken) - { - // check if web online - if (_webPool.FirstOrDefault().Value is not WebSession web) return; - - await web.SendAsync(new ConsoleQueryProxy - { - Id = query.Id, - HostId = query.HostId, - Query = query.Query, - Data = query.Data, - Errors = query.Errors, - HadErrors = query.HadErrors - }, cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/DriveHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/DriveHandler.cs deleted file mode 100644 index ec5652c..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/DriveHandler.cs +++ /dev/null @@ -1,145 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class DriveHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public DriveHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is DriveList drives) - { - await OnDrivesAsync(sender, drives, cancellationToken); - } - } - - private async ValueTask OnDrivesAsync(AgentSession session, List drives, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var driveBulk = new List>(); - - if (drives is not null && drives.Any()) - { - foreach (var drive in drives) - { - var driveFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Index, drive.Index) - }); - - var driveUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Index, drive.Index) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch) - .Set(p => p.Company, drive.Manufacturer) - .Set(p => p.Name, drive.Name) - .Set(p => p.Size, drive.Size) - .Set(p => p.Type, drive.InterfaceType) - .Set(p => p.Serial, drive.SerialNumber) - .Set(p => p.Firmware, drive.FirmwareRevision) - .Set(p => p.Status, drive.Status) - .Set(p => p.Pnp, drive.PNPDeviceID); - - driveBulk.Add(new UpdateOneModel(driveFilter, driveUpdate) - { - IsUpsert = true - }); - } - } - - driveBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var driveResult = await _database.HostDrive().BulkWriteAsync(driveBulk, cancellationToken: cancellationToken); - - // volumes - - var volumeBulk = new List>(); - - if (drives is not null && drives.Any()) - { - 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(); - - if (drive.Volumes is not null && drive.Volumes.Any()) - { - foreach (var volume in drive.Volumes) - { - var volumeFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Drive, driveId), - Builders.Filter.Eq(x => x.Index, volume.Index) - }); - - var volumeUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Drive, driveId) - .SetOnInsert(p => p.Index, volume.Index) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch) - .Set(p => p.Name, volume.Name) - .Set(p => p.Label, volume.Id) - .Set(p => p.Serial, volume.SerialNumber) - .Set(p => p.Size, volume.Size) - .Set(p => p.FreeSpace, volume.FreeSpace) - .Set(p => p.Type, volume.Type) - .Set(p => p.FileSystem, volume.FileSystem) - .Set(p => p.Compressed, volume.Compressed) - .Set(p => p.Bootable, volume.Bootable) - .Set(p => p.Primary, volume.PrimaryPartition) - .Set(p => p.Boot, volume.Bootable) - .Set(p => p.BlockSize, volume.BlockSize) - .Set(p => p.Blocks, volume.NumberOfBlocks) - .Set(p => p.StartingOffset, volume.StartingOffset) - .Set(p => p.Provider, volume.ProviderName); - - volumeBulk.Add(new UpdateOneModel(volumeFilter, volumeUpdate) - { - IsUpsert = true - }); - } - } - } - } - - volumeBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var volumeResult = await _database.HostVolume().BulkWriteAsync(volumeBulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/EventHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/EventHandler.cs deleted file mode 100644 index 1bd5c22..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/EventHandler.cs +++ /dev/null @@ -1,266 +0,0 @@ -using Insight.Agent.Enums; -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; -using static Insight.Agent.Messages.Event; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class EventHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public EventHandler(IMongoDatabase database, ILogger logger) - { - _database = database; - _logger = logger; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is Event @event) - { - await OnEventAsync(sender, @event, cancellationToken); - } - } - - private async ValueTask OnEventAsync(AgentSession session, Event @event, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - if (FilterEventId(@event)) return; - - var hostLog = await InsertHostLogAsync(hostEntity, @event, cancellationToken); - - if (hostLog is null || FilterMonitoringHostLog(hostLog)) return; - - await _database.HostLogMonitoring() - .InsertOneAsync(new HostLogMonitoringEntity - { - Host = hostEntity.Id, - Insert = hostLog.Insert, - Timestamp = hostLog.Timestamp, - Category = hostLog.Category, - Status = hostLog.Status, - Message = hostLog.Message, - Dispatch = DispatchEnum.Pending.ToString() - }, cancellationToken: cancellationToken); - } - - private async ValueTask InsertAgentLogAsync(AgentSession session, Event @event, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent() - .Aggregate() - .Match(Builders.Filter.Eq(p => p.Id, session.Id)) - .Lookup(_database.Host(), p => p.Serial, p => p.Agent, p => p.Hosts) - .FirstOrDefaultAsync(cancellationToken); - - if (agentEntity is null) return null; - - StatusEnum? status = @event.Status switch - { - StatusType.Information => StatusEnum.Information, - StatusType.Warning => StatusEnum.Warning, - StatusType.Error => StatusEnum.Error, - _ => null - }; - - CategoryEnum? category = @event.Category.ToLower() switch - { - "network" => CategoryEnum.Network, - "application" => CategoryEnum.Application, - "security" => CategoryEnum.Security, - "system" => CategoryEnum.System, - _ => null - }; - - var date = DateTime.Now; - - var log = new AgentLogEntity - { - Insert = date, - Agent = agentEntity.Id, - Timestamp = @event.Timestamp, - EventId = @event.EventId.ToString(), - Source = @event.Source, - Status = status.ToString(), - Category = category.ToString(), - Message = @event.Message - }; - - await _database.AgentLog().InsertOneAsync(log, cancellationToken: cancellationToken); - return log; - } - - private async ValueTask InsertHostLogAsync(HostEntity hostEntity, Event @event, CancellationToken cancellationToken) - { - StatusEnum? status = @event.Status switch - { - StatusType.Information => StatusEnum.Information, - StatusType.Warning => StatusEnum.Warning, - StatusType.Error => StatusEnum.Error, - StatusType.Critical => StatusEnum.Error, - StatusType.Unknown => null, - _ => null - }; - - CategoryEnum? category = 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 bool FilterEventId(Event @event) - { - var filter = new List - { - 0, - 3, - 4, - 16, - 37, - 900, - 902, - 903, - 1001, - 1003, - 1008, - 1023, - 1066, - 4624, - 4625, - 4634, - 4648, - 4672, - 4776, - 4798, - 4799, - 5058, - 5059, - 5061, - 5379, - 5612, - 5781, - //7036, - 8014, - 8016, - 8019, - 8194, - 8224, - 9009, - 9027, - 10001, - 10016, - 16384, - 16394, - 36874, - 36888, - }; - - if (filter.Any(p => p == @event.EventId)) return true; - return false; - } - - private bool FilterMonitoringHostLog(HostLogEntity hostLog) - { - //_logger.LogDebug($"try filter event: {hostLog.Category}.{hostLog.Source}.{hostLog.Status}"); - - if (Enum.TryParse(hostLog.Status, out StatusType status) is false) return true; - - if (hostLog.Category == CategoryEnum.System.ToString()) - { - if (hostLog.Source == "Service Control Manager" && status < StatusType.Warning) return true; - } - - if (hostLog.Category == CategoryEnum.Task.ToString()) - { - if (status < StatusType.Error) return true; - } - - // skip rdp infos - if (hostLog.Category == CategoryEnum.RDP.ToString()) - { - if (hostLog.EventId == "261") return true; - } - - // skip smbclient (veeam errors) - if (hostLog.Category == CategoryEnum.Security.ToString()) - { - if (hostLog.Source == "Microsoft-Windows-SMBClient") return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/InterfaceHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/InterfaceHandler.cs deleted file mode 100644 index 7cfc212..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/InterfaceHandler.cs +++ /dev/null @@ -1,299 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class InterfaceHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public InterfaceHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is InterfaceList interfaces) - { - await OnInterfacesAsync(sender, interfaces, cancellationToken); - } - } - - private async ValueTask OnInterfacesAsync(AgentSession session, List interfaces, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - // interfaces - - if (interfaces is not null && interfaces.Any()) - { - var interfaceBulk = new List>(); - - foreach (var @interface in interfaces) - { - var interfaceFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Index, @interface.Index) - }); - - var interfaceUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Index, @interface.Index) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch) - - .Set(p => p.Mac, @interface?.Mac) - .Set(p => p.Name, @interface?.Name) - .Set(p => p.Description, @interface?.Description) - .Set(p => p.Physical, @interface?.Physical) - .Set(p => p.Status, @interface?.Status?.ToString()) - .Set(p => p.Suffix, @interface?.Suffix) - .Set(p => p.Speed, @interface?.Speed) - .Set(p => p.Ipv4Mtu, @interface?.Ipv4Mtu) - .Set(p => p.Ipv4Dhcp, @interface?.Ipv4Dhcp) - - .Set(p => p.Ipv4Forwarding, @interface?.Ipv4Forwarding) - .Set(p => p.Ipv6Mtu, @interface?.Ipv6Mtu) - .Set(p => p.Sent, @interface?.Sent) - .Set(p => p.Received, @interface?.Received) - .Set(p => p.IncomingPacketsDiscarded, @interface?.IncomingPacketsDiscarded) - .Set(p => p.IncomingPacketsWithErrors, @interface?.IncomingPacketsWithErrors) - .Set(p => p.IncomingUnknownProtocolPackets, @interface?.IncomingUnknownProtocolPackets) - .Set(p => p.OutgoingPacketsDiscarded, @interface?.OutgoingPacketsDiscarded) - .Set(p => p.OutgoingPacketsWithErrors, @interface?.OutgoingPacketsWithErrors); - - interfaceBulk.Add(new UpdateOneModel(interfaceFilter, interfaceUpdate) - { - IsUpsert = true - }); - } - - interfaceBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var interfaceResult = await _database.HostInterface().BulkWriteAsync(interfaceBulk, cancellationToken: cancellationToken); - } - - // addresses - - if (interfaces is not null && interfaces.Any()) - { - var addressBulk = new List>(); - - foreach (var @interface in interfaces) - { - var interfaceId = await _database.HostInterface() - .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) - .Project(p => p.Id) - .FirstOrDefaultAsync(); - - if (@interface.Addresses is not null && @interface.Addresses.Any()) - { - foreach (var address in @interface.Addresses) - { - var addressFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Interface, interfaceId), - Builders.Filter.Eq(x => x.Address, address?.IpAddress?.Address), - Builders.Filter.Eq(x => x.Mask, address?.Ipv4Mask?.Address) - }); - - var addressUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Interface, interfaceId) - .SetOnInsert(p => p.Address, address?.IpAddress?.Address) - .SetOnInsert(p => p.Mask, address?.Ipv4Mask?.Address) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch); - - addressBulk.Add(new UpdateOneModel(addressFilter, addressUpdate) - { - IsUpsert = true - }); - } - } - } - - addressBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var addressResult = await _database.HostInterfaceAddress().BulkWriteAsync(addressBulk, cancellationToken: cancellationToken); - } - - // gateways - - if (interfaces is not null && interfaces.Any()) - { - var gatewayBulk = new List>(); - - foreach (var @interface in interfaces) - { - var interfaceId = await _database.HostInterface() - .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) - .Project(p => p.Id) - .FirstOrDefaultAsync(); - - if (@interface.Gateways is not null && @interface.Gateways.Any()) - { - foreach (var gateway in @interface.Gateways) - { - var gatewayFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Interface, interfaceId), - Builders.Filter.Eq(x => x.Address, gateway?.Address) - }); - - var gatewayUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Interface, interfaceId) - .SetOnInsert(p => p.Address, gateway?.Address) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch); - - gatewayBulk.Add(new UpdateOneModel(gatewayFilter, gatewayUpdate) - { - IsUpsert = true - }); - } - } - } - - gatewayBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var gatewayResult = await _database.HostInterfaceGateway().BulkWriteAsync(gatewayBulk, cancellationToken: cancellationToken); - } - - // nameservers - - if (interfaces is not null && interfaces.Any()) - { - var nameserverBulk = new List>(); - - foreach (var @interface in interfaces) - { - var interfaceId = await _database.HostInterface() - .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) - .Project(p => p.Id) - .FirstOrDefaultAsync(); - - if (@interface.Dns is not null && @interface.Dns.Any()) - { - foreach (var nameserver in @interface.Dns) - { - var nameserverFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Interface, interfaceId), - Builders.Filter.Eq(x => x.Address, nameserver?.Address) - }); - - var nameserverUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Interface, interfaceId) - .SetOnInsert(p => p.Address, nameserver?.Address) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch); - - nameserverBulk.Add(new UpdateOneModel(nameserverFilter, nameserverUpdate) - { - IsUpsert = true - }); - } - } - } - - nameserverBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var nameserverResult = await _database.HostInterfaceNameserver().BulkWriteAsync(nameserverBulk, cancellationToken: cancellationToken); - } - - // routes - - if (interfaces is not null && interfaces.Any()) - { - var routeBulk = new List>(); - - foreach (var @interface in interfaces) - { - var interfaceId = await _database.HostInterface() - .Find(p => p.Host == hostEntity.Id && p.Index == @interface.Index) - .Project(p => p.Id) - .FirstOrDefaultAsync(); - - if (@interface.Routes is not null && @interface.Routes.Any()) - { - foreach (var route in @interface.Routes) - { - var routeFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Interface, interfaceId), - Builders.Filter.Eq(x => x.Destination, route?.Destination?.Address), - Builders.Filter.Eq(x => x.Gateway, route?.Gateway?.Address), - Builders.Filter.Eq(x => x.Mask, route?.Mask), - }); - - var routeUpdate = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Interface, interfaceId) - .SetOnInsert(p => p.Destination, route?.Destination?.Address) - .SetOnInsert(p => p.Gateway, route?.Gateway?.Address) - .SetOnInsert(p => p.Mask, route?.Mask) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch) - - .Set(p => p.Metric, route?.Metric); - - routeBulk.Add(new UpdateOneModel(routeFilter, routeUpdate) - { - IsUpsert = true - }); - } - } - } - - routeBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var routeResult = await _database.HostInterfaceRoute().BulkWriteAsync(routeBulk, cancellationToken: cancellationToken); - } - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/MemoryHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/MemoryHandler.cs deleted file mode 100644 index 99d78cd..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/MemoryHandler.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class MemoryHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public MemoryHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is MemoryList memory) - { - await OnMemoryAsync(sender, memory, cancellationToken); - } - } - - private async ValueTask OnMemoryAsync(AgentSession session, List memory, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (memory is not null && memory.Any()) - { - foreach (var mem in memory) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Index, mem.Index) - }); - - var updateDefinition = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Index, mem.Index) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch) - .Set(p => p.Company, mem.Manufacturer) - .Set(p => p.Name, mem.Model) - .Set(p => p.Tag, mem.Tag) - .Set(p => p.Location, mem.Location) - .Set(p => p.Serial, mem.Serial) - .Set(p => p.Capacity, mem.Capacity) - .Set(p => p.Clock, mem.Speed) - .Set(p => p.CurrentClock, mem.ConfiguredSpeed) - .Set(p => p.Voltage, mem.Voltage) - .Set(p => p.CurrentVoltage, mem.ConfiguredVoltage); - - bulk.Add(new UpdateOneModel(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostMemory().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/OperationSystemHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/OperationSystemHandler.cs deleted file mode 100644 index ca8743e..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/OperationSystemHandler.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class OperationSystemHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public OperationSystemHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is OperationSystem os) - { - await OnOperationSystemAsync(sender, os, cancellationToken); - } - } - - private async ValueTask OnOperationSystemAsync(AgentSession session, OperationSystem operatingSystem, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var date = DateTime.Now; - - await _database.HostOs().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .Set(p => p.Update, date) - .Set(p => p.Name, operatingSystem.Name) - .Set(p => p.Version, operatingSystem.Version) - .Set(p => p.Architecture, operatingSystem.Architecture.ToString()) - .Set(p => p.SerialNumber, operatingSystem.SerialNumber) - .Set(p => p.Virtual, operatingSystem.Virtual) - .Set(p => p.Installed, operatingSystem.InstallDate), new UpdateOptions - { - IsUpsert = true - }, cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/PrinterHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/PrinterHandler.cs deleted file mode 100644 index eed8b2c..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/PrinterHandler.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class PrinterHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public PrinterHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is PrinterList printers) - { - await OnPrintersAsync(sender, printers, cancellationToken); - } - } - - private async ValueTask OnPrintersAsync(AgentSession session, List printers, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (printers is not null && printers.Any()) - { - foreach (var printer in printers) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Name, printer.Name) - }); - - var updateDefinition = Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .SetOnInsert(p => p.Name, printer.Name) - .Set(p => p.Update, date) - .Set(p => p.Batch, batch) - .Set(p => p.Port, printer.Port) - .Set(p => p.Location, printer.Location) - .Set(p => p.Comment, printer.Comment); - - bulk.Add(new UpdateOneModel(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostPrinter().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/ProcessorHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/ProcessorHandler.cs deleted file mode 100644 index a4f9c5d..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/ProcessorHandler.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class ProcessorHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public ProcessorHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is ProcessorList processors) - { - await OnProcessorsAsync(sender, processors, cancellationToken); - } - } - - private async ValueTask OnProcessorsAsync(AgentSession session, List processors, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (processors is not null && processors.Any()) - { - foreach (var processor in processors) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Index, processor.Index) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostProcessor().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/ServiceHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/ServiceHandler.cs deleted file mode 100644 index 1108236..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/ServiceHandler.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class ServiceHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public ServiceHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is ServiceList services) - { - await OnServicesAsync(sender, services, cancellationToken); - } - } - - private async ValueTask OnServicesAsync(AgentSession session, List services, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (services is not null && services.Any()) - { - foreach (var service in services) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Name, service.Name) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostService().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/SessionHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/SessionHandler.cs deleted file mode 100644 index ffad33d..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/SessionHandler.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class SessionHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public SessionHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is SessionList sessions) - { - await OnSessionsAsync(sender, sessions, cancellationToken); - } - } - - private async ValueTask OnSessionsAsync(AgentSession session, List sessions, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (sessions is not null && sessions.Any()) - { - foreach (var sess in sessions) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Sid, sess.Sid) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostSession().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/SoftwareHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/SoftwareHandler.cs deleted file mode 100644 index 1e986fa..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/SoftwareHandler.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class SoftwareHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public SoftwareHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is ApplicationList applications) - { - await OnApplicationsAsync(sender, applications, cancellationToken); - } - } - - private async ValueTask OnApplicationsAsync(AgentSession session, List applications, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (applications is not null && applications.Any()) - { - foreach (var app in applications) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Name, app.Name), - Builders.Filter.Eq(x => x.Architecture, app.Architecture?.ToString()) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostApplication().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/StoragePoolHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/StoragePoolHandler.cs deleted file mode 100644 index 7b00c9c..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/StoragePoolHandler.cs +++ /dev/null @@ -1,253 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class StoragePoolHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public StoragePoolHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is StoragePoolList storagePools) - { - await OnStoragePoolsAsync(sender, storagePools, cancellationToken); - } - } - - private async ValueTask OnStoragePoolsAsync(AgentSession session, List? storagePools, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - // storagepools - - if (storagePools is not null && storagePools.Any()) - { - var storagepoolBulk = new List>(); - - foreach (var storagePool in storagePools) - { - var storagePoolFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.UniqueId, storagePool.UniqueId) - }); - - List? states = null; - - if (storagePool.States is not null) - { - states = new List(); - - foreach (var state in storagePool.States) - { - states.Add(state.ToString()); - } - } - - var storagePoolUpdate = Builders.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(storagePoolFilter, storagePoolUpdate) - { - IsUpsert = true - }); - } - - storagepoolBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var storagePoolResult = await _database.HostStoragePool().BulkWriteAsync(storagepoolBulk, cancellationToken: cancellationToken); - } - - // physicaldisks - - if (storagePools is not null && storagePools.Any()) - { - var physicalDiskBulk = new List>(); - - 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(); - - if (storagePool.PhysicalDisks is not null && storagePool.PhysicalDisks.Any()) - { - foreach (var physicalDisk in storagePool.PhysicalDisks) - { - var physicalDiskFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.StoragePool, storagePoolId), - Builders.Filter.Eq(x => x.UniqueId, physicalDisk.UniqueId) - }); - - List? states = null; - - if (physicalDisk.States is not null) - { - states = new List(); - - foreach (var state in physicalDisk.States) - { - states.Add(state.ToString()); - } - } - - var physicalDiskUpdate = Builders.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(physicalDiskFilter, physicalDiskUpdate) - { - IsUpsert = true - }); - } - } - } - - physicalDiskBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var physicalDiskResult = await _database.HostStoragePoolPhysicalDisk().BulkWriteAsync(physicalDiskBulk, cancellationToken: cancellationToken); - } - - // virtual disks - - if (storagePools is not null && storagePools.Any()) - { - var virtualDiskBulk = new List>(); - - foreach (var storagePool in storagePools) - { - if (storagePool.VirtualDisks is not null && storagePool.VirtualDisks.Any()) - { - 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(); - - var virtualDiskFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.StoragePool, storagePoolId), - Builders.Filter.Eq(x => x.UniqueId, virtualDisk.UniqueId) - }); - - List? states = null; - - if (virtualDisk.States is not null) - { - states = new List(); - - foreach (var state in virtualDisk.States) - { - states.Add(state.ToString()); - } - } - - var virtualDiskUpdate = Builders.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(virtualDiskFilter, virtualDiskUpdate) - { - IsUpsert = true - }); - } - } - } - - virtualDiskBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var virtualDiskResult = await _database.HostStoragePoolVirtualDisk().BulkWriteAsync(virtualDiskBulk, cancellationToken: cancellationToken); - } - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/SystemInfoHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/SystemInfoHandler.cs deleted file mode 100644 index 7abab05..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/SystemInfoHandler.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class SystemInfoHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public SystemInfoHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is SystemInfo systemInfo) - { - await OnSystemInfoAsync(sender, systemInfo, cancellationToken); - } - } - - private async ValueTask OnSystemInfoAsync(AgentSession session, SystemInfo? system, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var date = DateTime.Now; - - await _database.HostSystem().UpdateOneAsync(p => p.Host == hostEntity.Id, Builders.Update - .SetOnInsert(p => p.Insert, date) - .SetOnInsert(p => p.Host, hostEntity.Id) - .Set(p => p.Update, date) - .Set(p => p.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); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/TrapHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/TrapHandler.cs deleted file mode 100644 index 9d8b817..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/TrapHandler.cs +++ /dev/null @@ -1,289 +0,0 @@ -using Insight.Agent.Enums; -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -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.Handlers.Agent -{ - public class TrapHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public TrapHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is Trap trap) - { - await OnTrapAsync(sender, trap, cancellationToken); - } - } - - private async ValueTask OnTrapAsync(AgentSession session, Trap? trap, CancellationToken cancellationToken) - { - if (trap is null) return; - - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - if (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.Any() is false) return false; - - monitoring = new MonitorMessage - { - Community = packet.Community, - Category = CategoryEnum.Monitoring, - Endpoint = packet.Endpoint, - Timestamp = packet.Timestamp - }; - - if (Enum.TryParse(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; - } - - private static bool ParseAcronis(List> 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 parsed = false; - - try - { - var trim = data[1].Value.Split(new string[] { ":" }, StringSplitOptions.None); - task = trim[1].Split(new string[] { "'" }, StringSplitOptions.None)[1].Split("'")[0].Trim(); - message = trim[1].Split(new string[] { "' " }, StringSplitOptions.None)[1].Trim(); - - parsed = true; - } - catch (Exception) - { - // skipped for base64 parse - } - - if (parsed) return true; - - try - { - var content = Regex.Replace(data[1].Value, @"\s+", ""); - 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); - - var trim = content.Split(new string[] { ":" }, StringSplitOptions.None); - task = trim[1].Split(new string[] { "'" }, StringSplitOptions.None)[1].Split("'")[0].Trim(); - message = trim[1].Split(new string[] { "' " }, StringSplitOptions.None)[1].Trim(); - - parsed = true; - } - catch (Exception ex) - { - //_logger.LogError("{ex}", ex); - } - - if (parsed) return true; - return false; - } - - private static bool ParseVeeam(List> 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> 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 - { - { "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 ex) - { - //_logger.LogError("{ex}", ex); - } - - if (parsed) return true; - return false; - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/UpdateHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/UpdateHandler.cs deleted file mode 100644 index f1fe72d..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/UpdateHandler.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class UpdateHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public UpdateHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is UpdateList updates) - { - await OnUpdatesAsync(sender, updates, cancellationToken); - } - } - - private async ValueTask OnUpdatesAsync(AgentSession session, UpdateList updates, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (updates is not null) - { - if (updates.Installed is not null && updates.Installed.Any()) - { - foreach (var update in updates.Installed) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Serial, update.Id) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - - if (updates.Pending is not null && updates.Pending.Any()) - { - foreach (var update in updates.Pending) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Serial, update.Id) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - } - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostUpdate().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/UserHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/UserHandler.cs deleted file mode 100644 index 5e09d66..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/UserHandler.cs +++ /dev/null @@ -1,188 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class UserHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public UserHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is UserList users) - { - await OnUsersAsync(sender, users, cancellationToken); - } - } - - private async ValueTask OnUsersAsync(AgentSession session, List? users, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - // users - - if (users is not null && users.Any()) - { - var userBulk = new List>(); - - foreach (var user in users) - { - var userFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Domain, user?.Domain), - Builders.Filter.Eq(x => x.Name, user?.Name) - }); - - var userUpdate = Builders.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(userFilter, userUpdate) - { - IsUpsert = true - }); - } - - userBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var userResult = await _database.HostSystemUser().BulkWriteAsync(userBulk, cancellationToken: cancellationToken); - } - - // groups - - if (users is not null && users.Any()) - { - var groupBulk = new List>(); - - 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.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Domain, group?.Domain), - Builders.Filter.Eq(x => x.Name, group?.Name) - }); - - var groupUpdate = Builders.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(groupFilter, groupUpdate) - { - IsUpsert = true - }); - } - - groupBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var groupResult = await _database.HostSystemGroup().BulkWriteAsync(groupBulk, cancellationToken: cancellationToken); - } - - // relations - - if (users is not null && users.Any()) - { - var relationBulk = new List>(); - - 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(); - - if (user.Groups is not null && user.Groups.Any()) - { - 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(); - - var relationFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.User, userId), - Builders.Filter.Eq(x => x.Group, groupId) - }); - - var relationUpdate = Builders.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(relationFilter, relationUpdate) - { - IsUpsert = true - }); - } - } - } - - relationBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var relationResult = await _database.HostSystemUserSystemGroup().BulkWriteAsync(relationBulk, cancellationToken: cancellationToken); - } - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/VideocardHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/VideocardHandler.cs deleted file mode 100644 index bf8e648..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/VideocardHandler.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class VideocardHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public VideocardHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is VideocardList videocards) - { - await OnVideocardsAsync(sender, videocards, cancellationToken); - } - } - - private async ValueTask OnVideocardsAsync(AgentSession session, List? videocards, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - var bulk = new List>(); - - if (videocards is not null && videocards.Any()) - { - foreach (var videocard in videocards) - { - var filterDefinition = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.Name, videocard.Model) - }); - - var updateDefinition = Builders.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(filterDefinition, updateDefinition) - { - IsUpsert = true - }); - } - - bulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var result = await _database.HostVideocard().BulkWriteAsync(bulk, cancellationToken: cancellationToken); - } - } - } -} diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/VirtualMaschineHandler.cs b/src/Server/Insight.Server/Network/Handlers/Agent/VirtualMaschineHandler.cs deleted file mode 100644 index a49e08d..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/VirtualMaschineHandler.cs +++ /dev/null @@ -1,173 +0,0 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace Insight.Server.Network.Handlers.Agent -{ - public class VirtualMaschineHandler : IAgentMessageHandler - { - private readonly IMongoDatabase _database; - - public VirtualMaschineHandler(IMongoDatabase database) - { - _database = database; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage - { - if (message is VirtualMaschineList virtualMaschines) - { - await OnVirtualMaschinesAsync(sender, virtualMaschines, cancellationToken); - } - } - - private async ValueTask OnVirtualMaschinesAsync(AgentSession session, List? virtualMaschines, CancellationToken cancellationToken) - { - var agentEntity = await _database.Agent().Find(Builders.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken); - if (agentEntity is null) return; - - var hostEntity = await _database.Host().Find(Builders.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken); - if (hostEntity is null) return; - - var batch = ObjectId.GenerateNewId().ToString(); - var date = DateTime.Now; - - // virtual maschines - if (virtualMaschines is not null && virtualMaschines.Any()) - { - var virtualMaschineBulk = new List>(); - - foreach (var virtualMaschine in virtualMaschines) - { - var virtualMaschineFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.UniqueId, virtualMaschine.Id.ToString()) - }); - - var virtualMaschineUpdate = Builders.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(virtualMaschineFilter, virtualMaschineUpdate) - { - IsUpsert = true - }); - } - - virtualMaschineBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var virtualMaschineResult = await _database.HostHypervisorVirtualMaschine().BulkWriteAsync(virtualMaschineBulk, cancellationToken: cancellationToken); - } - - // virtual maschine configurations - - if (virtualMaschines is not null && virtualMaschines.Any()) - { - var configurationBulk = new List>(); - - 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(); - - if (virtualmaschine.Configurations is not null && virtualmaschine.Configurations.Any()) - { - foreach (var config in virtualmaschine.Configurations) - { - var configFilter = Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Eq(x => x.VirtualMaschine, virtualMaschineId), - Builders.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; - - var configUpdate = Builders.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(configFilter, configUpdate) - { - IsUpsert = true - }); - } - } - } - - configurationBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> - { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) - }))); - - var configurationResult = await _database.HostVirtualMaschineConfig().BulkWriteAsync(configurationBulk, cancellationToken: cancellationToken); - } - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Handlers/Web/ConsoleProxyHandler.cs b/src/Server/Insight.Server/Network/Handlers/Web/ConsoleProxyHandler.cs deleted file mode 100644 index e7cc5c9..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Web/ConsoleProxyHandler.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Insight.Agent.Messages; -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using Insight.Web.Interfaces; -using Insight.Web.Messages; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; -using Vaitr.MemoryBus; -using Vaitr.Network; - -namespace Insight.Server.Network.Handlers.Web -{ - public class ConsoleProxyHandler : IWebMessageHandler - { - private readonly List _subscriptions = new(); - - private readonly TcpSessionPool _agentPool; - private readonly TcpSessionPool _webPool; - private readonly IMongoDatabase _database; - private readonly IMemoryBus _bus; - private readonly ILogger _logger; - - public ConsoleProxyHandler( - TcpSessionPool agentPool, - TcpSessionPool webPool, - IMongoDatabase database, - IMemoryBus bus, - ILogger logger) - { - _agentPool = agentPool; - _webPool = webPool; - _database = database; - _bus = bus; - _logger = logger; - - _subscriptions.Add(_bus.SubscribeAsync(OnConsoleQueryAsync, null)); - } - - public async ValueTask HandleAsync(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IWebMessage - { - if (message is ConsoleQueryProxyRequest consoleRequest) - { - await OnConsoleQueryRequestAsync(sender, consoleRequest, cancellationToken); - } - } - - private async ValueTask OnConsoleQueryRequestAsync(WebSession session, ConsoleQueryProxyRequest request, CancellationToken cancellationToken) - { - // get host - var hostEntity = await _database.Host() - .Find(Builders - .Filter - .Eq(p => p.Id, request.HostId)) - .FirstOrDefaultAsync(cancellationToken); - - if (hostEntity is null) - { - _logger.LogWarning("hostEntity is null"); - return; - } - - // get agent - var agentEntity = await _database.Agent() - .Find(Builders - .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; - - // send "real" packet to agent - await agent.SendAsync(new ConsoleQueryRequest - { - Id = request.Id, - HostId = request.HostId, - Query = request.Query - }, cancellationToken); - } - - private async ValueTask OnConsoleQueryAsync(ConsoleQuery query, CancellationToken cancellationToken) - { - // check if web online - if (_webPool.FirstOrDefault().Value is not WebSession web) return; - - await web.SendAsync(new ConsoleQueryProxy - { - Id = query.Id, - HostId = query.HostId, - Query = query.Query, - Data = query.Data, - Errors = query.Errors, - HadErrors = query.HadErrors - }, cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Remote/Handlers/RemoteHandler.cs b/src/Server/Insight.Server/Network/Remote/Handlers/RemoteHandler.cs new file mode 100644 index 0000000..a09c4bf --- /dev/null +++ b/src/Server/Insight.Server/Network/Remote/Handlers/RemoteHandler.cs @@ -0,0 +1,111 @@ +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 : IMessageHandler +{ + private readonly Bus _bus; + private readonly ISessionPool _remotePool; + private readonly ILogger _logger; + + public RemoteHandler(Bus bus, ISessionPool remotePool, ILogger logger) + { + _bus = bus; + _remotePool = remotePool; + _logger = logger; + } + + public async ValueTask HandleAsync(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.Id} => SessionRequest"); + + 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.Id} => ClipboardData"); + + await _bus.PublishAsync(clipboardChanged, cancellationToken); + } + + private async Task OnAudioData(RemoteSession session, CastAudio audioSample, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {session.Id} => AudioData"); + + await _bus.PublishAsync(audioSample, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Remote/RemoteSession.cs b/src/Server/Insight.Server/Network/Remote/RemoteSession.cs new file mode 100644 index 0000000..19d6a66 --- /dev/null +++ b/src/Server/Insight.Server/Network/Remote/RemoteSession.cs @@ -0,0 +1,92 @@ +using Insight.Domain.Enums; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Server.Network.Web; +using Microsoft.Extensions.Logging; +using Vaitr.Bus; +using Vaitr.Network; + +namespace Insight.Server.Network.Remote; + +public class RemoteSession : TcpSession +{ + public string Id { get; } + public RemoteControlMode Mode { get; set; } + + private readonly Bus _bus; + private readonly ISessionPool _webPool; + private readonly IEnumerable> _handlers; + + public RemoteSession( + ISessionPool webPool, + IEnumerable> handlers, + ISerializer serializer, + ILogger logger) : base(serializer, logger) + { + Id = GenerateRandomId(); + + _webPool = webPool; + _handlers = handlers; + } + + public async ValueTask ProxyAsync(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 + { + Message = message + }, cancellationToken); + } + + protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) connected", RemoteEndPoint); + + if (_webPool.FirstOrDefault().Value is not WebSession webSession) return; + } + + protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) disconnected", RemoteEndPoint); + } + + protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) + { + await base.OnSentAsync(context, cancellationToken); + } + + protected override async ValueTask OnReceivedAsync(IPacketContext 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 async ValueTask OnHeartbeatAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) Heartbeat", RemoteEndPoint); + } + + 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; + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Shared/ProxyHandler.cs b/src/Server/Insight.Server/Network/Shared/ProxyHandler.cs new file mode 100644 index 0000000..1d305bb --- /dev/null +++ b/src/Server/Insight.Server/Network/Shared/ProxyHandler.cs @@ -0,0 +1,94 @@ +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 : IMessageHandler, IMessageHandler +{ + private readonly ISessionPool _agentPool; + private readonly ISessionPool _webPool; + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public ProxyHandler( + ISessionPool agentPool, + ISessionPool webPool, + IMongoDatabase database, + ILogger logger) + { + _agentPool = agentPool; + _webPool = webPool; + _database = database; + _logger = logger; + } + + public async ValueTask HandleAsync(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Proxy proxyRequest: + await OnProxyRequestAsync(sender, proxyRequest, cancellationToken); + break; + } + } + + public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + switch (message) + { + case Proxy proxyResponse: + await OnProxyResponseAsync(sender, proxyResponse, cancellationToken); + break; + } + } + + private async ValueTask OnProxyRequestAsync(WebSession session, Proxy request, CancellationToken cancellationToken) + { + // get host + var hostEntity = await _database.Host() + .Find(Builders + .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 + .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, CancellationToken cancellationToken) + { + // check if web online + if (_webPool.FirstOrDefault().Value is not WebSession web) return; + + await web.SendAsync(response, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/Web/WebSession.cs b/src/Server/Insight.Server/Network/Web/WebSession.cs new file mode 100644 index 0000000..9625bb0 --- /dev/null +++ b/src/Server/Insight.Server/Network/Web/WebSession.cs @@ -0,0 +1,55 @@ +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Microsoft.Extensions.Logging; +using Vaitr.Network; + +namespace Insight.Server.Network.Web; + +public class WebSession : TcpSession +{ + public string? Id { get; set; } + + private readonly IEnumerable> _handlers; + + public WebSession(IEnumerable> handlers, ISerializer serializer, ILogger logger) : base(serializer, logger) + { + _handlers = handlers; + } + + protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Web ({ep?}) connected", RemoteEndPoint); + } + + protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Web ({ep?}) disconnected", RemoteEndPoint); + } + + protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) + { + await base.OnSentAsync(context, cancellationToken); + } + + protected override async ValueTask OnReceivedAsync(IPacketContext 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 async ValueTask OnHeartbeatAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Web ({ep?}) Heartbeat", RemoteEndPoint); + } +} \ No newline at end of file diff --git a/src/Server/Insight.Server/Network/WebSession.cs b/src/Server/Insight.Server/Network/WebSession.cs deleted file mode 100644 index 1a949ac..0000000 --- a/src/Server/Insight.Server/Network/WebSession.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Insight.Web.Interfaces; -using Insight.Web.Messages; -using Microsoft.Extensions.Logging; -using System.Net.Sockets; -using Vaitr.Network; - -namespace Insight.Server.Network; - -public class WebSession(IEnumerable> handlers, Socket socket, Stream stream, TcpConnectionOptions options, MemPackSerializer serializer, ILogger logger) - : TcpSession(socket, stream, options, serializer, logger) -{ - public string? Id { get; set; } - - private readonly IEnumerable> _handlers = handlers; - - protected override async ValueTask OnReceivedAsync(PacketContext context, CancellationToken cancellationToken) - { - foreach (var handler in _handlers) - { - try - { - await handler.HandleAsync(this, context.Data, cancellationToken); - } - catch (Exception ex) - { - _logger.LogWarning("Web ({ep?}) {ex}", RemoteEndPoint, ex.ToString()); - } - } - } -} \ No newline at end of file diff --git a/src/Server/Insight.Server/Program.cs b/src/Server/Insight.Server/Program.cs index 1ee789d..fc2a392 100644 --- a/src/Server/Insight.Server/Program.cs +++ b/src/Server/Insight.Server/Program.cs @@ -1,142 +1,151 @@ -using Insight.Agent.Interfaces; -using Insight.Agent.Messages; -using Insight.Domain.Constants; +using Insight.Domain.Constants; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; using Insight.Infrastructure; using Insight.Server.Extensions; -using Insight.Server.Network; -using Insight.Server.Network.Handlers.Agent; -using Insight.Server.Network.Handlers.Web; +using Insight.Server.Network.Agent; +using Insight.Server.Network.Agent.Handlers; +using Insight.Server.Network.Globals; +using Insight.Server.Network.Web; using Insight.Server.Services; -using Insight.Web.Interfaces; -using Insight.Web.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Net; -using Vaitr.MemoryBus.Hosting; +using Vaitr.Bus; using Vaitr.Network; +using Vaitr.Network.Hosting; -namespace Insight.Server +namespace Insight.Server; + +internal class Program { - internal class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) + var builder = Host.CreateDefaultBuilder(args); + builder.UseWindowsService(); + builder.UseSystemd(); + + builder.ConfigureAppConfiguration(options => { - var builder = Host.CreateDefaultBuilder(args); - builder.UseWindowsService(); - builder.UseSystemd(); + options.Defaults(); + }); - builder.ConfigureAppConfiguration(options => + builder.ConfigureLogging(options => + { + options.ClearProviders(); + options.SetMinimumLevel(LogLevel.Trace); + + options.AddSimpleConsole(options => { - options.Defaults(); + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; }); - builder.ConfigureLogging(options => + options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/server_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); + }); + + builder.ConfigureServices((host, services) => + { + // AGENT + services.AddAgentServices(host.Configuration); + + // WEB + services.AddWebServices(host.Configuration); + + // INFRASTRUCTURE + services.AddDatabase(host.Configuration); + + // BACKGROUND SERVICES + services.AddHostedService(); + services.AddHostedService(); + + // GLOBAL DEPENDENCIES + services.AddSingleton(); + services.AddTransient(provider => new HttpClient(new HttpClientHandler { - options.ClearProviders(); - options.SetMinimumLevel(LogLevel.Trace); + ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true + })); + }); - options.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; - }); + var host = builder.Build(); + await host.RunAsync().ConfigureAwait(false); + } +} - options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/server_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); - }); +internal static class ServiceExtensions +{ + internal static IServiceCollection AddAgentServices(this IServiceCollection services, IConfiguration configuration) + { + // AGENT SERVER + services.UseHostedServer(options => + { + options.Address = IPAddress.Any; + options.Port = configuration.GetValue(Appsettings.AgentServerPort) ?? throw new Exception($"{Appsettings.AgentServerPort} value not set (appsettings)"); + options.Keepalive = 10000; + options.Timeout = 30000; + options.Backlog = 128; - builder.ConfigureServices((host, services) => - { - //var databaseLoggerFactory = LoggerFactory.Create(b => - //{ - // b.AddSimpleConsole(); - // b.SetMinimumLevel(LogLevel.Debug); - //}); + options.Compression = true; + options.Encryption = Encryption.Tls12; + options.Certificate = configuration.GetValue(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)"); + options.CertificatePassword = configuration.GetValue(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)"); - // INFRASTRUCTURE - services.AddDatabase(host.Configuration); + options.UseSerializer>(); + }); - // AGENT SERVER - services.UseHostedTcpServer(options => - { - options.Address = IPAddress.Any; - options.Port = host.Configuration.GetValue(Appsettings.AgentServerPort) ?? throw new Exception($"{Appsettings.AgentServerPort} value not set (appsettings)"); - options.KeepAlive = 10000; - options.Timeout = 30000; - options.Backlog = 128; - options.Buffer = 1024 * 1024; - options.Parallelism = 0; - options.Certificate = host.Configuration.GetValue(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)"); - options.CertificatePassword = host.Configuration.GetValue(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)"); - options.Compression = Compression.None; - options.Encryption = Encryption.Tls12; - options.SslPolicy = SslPolicy.None; + // HANDLER + services.AddSingleton(); + services.AddSingleton, CustomHandler>(); + services.AddSingleton, ProxyHandler>(); + services.AddSingleton, AgentHandler>(); + services.AddSingleton, DriveHandler>(); + services.AddSingleton, Network.Agent.Handlers.EventHandler>(); + services.AddSingleton, InterfaceHandler>(); + services.AddSingleton, MainboardHandler>(); + services.AddSingleton, MemoryHandler>(); + services.AddSingleton, OperationSystemHandler>(); + services.AddSingleton, PrinterHandler>(); + services.AddSingleton, ProcessorHandler>(); + services.AddSingleton, ServiceHandler>(); + services.AddSingleton, SessionHandler>(); + services.AddSingleton, SoftwareHandler>(); + services.AddSingleton, StoragePoolHandler>(); + services.AddSingleton, SystemInfoHandler>(); + services.AddSingleton, TrapHandler>(); + services.AddSingleton, UpdateHandler>(); + services.AddSingleton, UserHandler>(); + services.AddSingleton, VideocardHandler>(); + services.AddSingleton, VirtualMaschineHandler>(); + + return services; + } - //options.InputRateLimit = 1024 * 1024; - //options.OutputRateLimit = 1024 * 1024; - }); + internal static IServiceCollection AddWebServices(this IServiceCollection services, IConfiguration configuration) + { + // SERVER + services.UseHostedServer(options => + { + options.Address = IPAddress.Any; + options.Port = configuration.GetValue(Appsettings.WebServerPort) ?? throw new Exception($"{Appsettings.WebServerPort} value not set (appsettings)"); + options.Keepalive = 10000; + options.Timeout = 30000; + options.Backlog = 128; + options.Encryption = Encryption.Tls12; + options.Compression = true; - services.AddSingleton(); - services.AddSingleton, AgentHandler>(); - services.AddSingleton, DriveHandler>(); - services.AddSingleton, Network.Handlers.Agent.EventHandler>(); - services.AddSingleton, InterfaceHandler>(); - services.AddSingleton, MainboardHandler>(); - services.AddSingleton, MemoryHandler>(); - services.AddSingleton, OperationSystemHandler>(); - services.AddSingleton, PrinterHandler>(); - services.AddSingleton, ProcessorHandler>(); - services.AddSingleton, ServiceHandler>(); - services.AddSingleton, SessionHandler>(); - services.AddSingleton, SoftwareHandler>(); - services.AddSingleton, StoragePoolHandler>(); - services.AddSingleton, SystemInfoHandler>(); - services.AddSingleton, TrapHandler>(); - services.AddSingleton, UpdateHandler>(); - services.AddSingleton, UserHandler>(); - services.AddSingleton, VideocardHandler>(); - services.AddSingleton, VirtualMaschineHandler>(); - services.AddSingleton, ConsoleHandler>(); + options.Certificate = configuration.GetValue(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)"); + options.CertificatePassword = configuration.GetValue(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)"); - // WEB (FRONTEND-PROXY) SERVER - services.UseHostedTcpServer(options => - { - options.Address = IPAddress.Any; - options.Port = host.Configuration.GetValue(Appsettings.WebServerPort) ?? throw new Exception($"{Appsettings.WebServerPort} value not set (appsettings)"); - options.KeepAlive = 10000; - options.Timeout = 30000; - options.Backlog = 128; - options.Buffer = 1024 * 1024; - options.Parallelism = 0; - options.Certificate = host.Configuration.GetValue(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)"); - options.CertificatePassword = host.Configuration.GetValue(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)"); - options.Compression = Compression.None; - options.Encryption = Encryption.Tls12; - options.SslPolicy = SslPolicy.None; + options.UseSerializer>(); + }); - //options.InputRateLimit = 1024 * 1024; - //options.OutputRateLimit = 1024 * 1024; - }); + // HANDLER + services.AddSingleton, ProxyHandler>(); - services.AddSingleton, ConsoleProxyHandler>(); - - // DISPATCH - services.AddHostedService(); - services.AddHostedService(); - - // GLOBAL DEPENDENCIES - services.AddMemoryBus(); - services.AddTransient(provider => new HttpClient(new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true - })); - }); - - var host = builder.Build(); - await host.RunAsync().ConfigureAwait(false); - } + return services; } } \ No newline at end of file diff --git a/src/Server/Insight.Server/Services/DispatchService.cs b/src/Server/Insight.Server/Services/DispatchService.cs index 7d3b6c4..23b7a25 100644 --- a/src/Server/Insight.Server/Services/DispatchService.cs +++ b/src/Server/Insight.Server/Services/DispatchService.cs @@ -1,4 +1,4 @@ -using Insight.Agent.Enums; +using Insight.Domain.Enums; using Insight.Infrastructure; using Insight.Infrastructure.Entities; using Microsoft.Extensions.Configuration; @@ -6,153 +6,152 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Server.Services +namespace Insight.Server.Services; + +internal class DispatchService : BackgroundService { - internal class DispatchService : BackgroundService + private readonly HttpClient _httpClient; + private readonly IMongoDatabase _database; + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + + public DispatchService(HttpClient httpClient, IMongoDatabase database, IConfiguration configuration, ILogger logger) { - private readonly HttpClient _httpClient; - private readonly IMongoDatabase _database; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; + _httpClient = httpClient; + _database = database; + _configuration = configuration; + _logger = logger; + } - public DispatchService(HttpClient httpClient, IMongoDatabase database, IConfiguration configuration, ILogger logger) + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); + + var enabled = _configuration.GetValue(Appsettings.DispatchWebmatic) ?? throw new Exception($"{Appsettings.DispatchWebmatic} value not set (appsettings)"); + if (enabled is false) return; + + try { - _httpClient = httpClient; - _database = database; - _configuration = configuration; - _logger = logger; + while (cancellationToken.IsCancellationRequested is false) + { + await DispatchAsync(cancellationToken); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + } } + catch (OperationCanceledException) { } + catch (Exception) { } + } - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + private async ValueTask DispatchAsync(CancellationToken cancellationToken) + { + _logger.LogTrace($"DispatchAsync"); + + var pendings = await _database.HostLogMonitoring() + .Find(Builders + .Filter.Eq(p => p.Dispatch, DispatchEnum.Pending.ToString())) + .Limit(10) + .ToListAsync(cancellationToken); + + if (pendings is null || pendings.Any() is false) return; + + foreach (var entity in pendings) { - _logger.LogTrace("ExecuteAsync"); - - var enabled = _configuration.GetValue(Appsettings.DispatchWebmatic) ?? throw new Exception($"{Appsettings.DispatchWebmatic} 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); - } + var result = await SendAsync(entity, default); + + await _database.HostLogMonitoring() + .UpdateOneAsync(Builders.Filter + .Eq(p => p.Id, entity.Id), Builders + .Update + .Set(p => p.Dispatch, result.ToString()), cancellationToken: default); } - catch (OperationCanceledException) { } - catch (Exception) { } - } - - private async ValueTask DispatchAsync(CancellationToken cancellationToken) - { - _logger.LogTrace($"DispatchAsync"); - - var pendings = await _database.HostLogMonitoring() - .Find(Builders - .Filter.Eq(p => p.Dispatch, DispatchEnum.Pending.ToString())) - .Limit(10) - .ToListAsync(cancellationToken); - - if (pendings is null || pendings.Any() is false) return; - - foreach (var entity in pendings) + catch (Exception ex) { - try - { - var result = await SendAsync(entity, default); - - await _database.HostLogMonitoring() - .UpdateOneAsync(Builders.Filter - .Eq(p => p.Id, entity.Id), Builders - .Update - .Set(p => p.Dispatch, result.ToString()), cancellationToken: default); - } - catch (Exception ex) - { - _logger.LogError(ex.Message); - _logger.LogTrace(ex.StackTrace); - } - finally - { - // webmatic safety offset - await Task.Delay(TimeSpan.FromSeconds(1), default); - } + _logger.LogError(ex.Message); + _logger.LogTrace(ex.StackTrace); } - } - - private async ValueTask SendAsync(HostLogMonitoringEntity monitoring, CancellationToken cancellationToken) - { - _logger.LogTrace($"SendAsync ({monitoring})"); - - var monitoringApi = Monitoring.LogUri; - var monitoringContent = new List>(); - var monitoringResult = new List>(); - - // adjust by category - if (Enum.TryParse(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("category", monitoringCategory.ToString())); - - // host resolve - var hostEntity = await _database.Host().Find(Builders.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.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("computer_name", monitoring.Hostname.ToLower())); - - // insert converted status (api styled) - if (Enum.TryParse(monitoring.Status, true, out var monitoringStatus) is false) return DispatchEnum.Failure; - - monitoringContent.Add(monitoringStatus switch + finally { - StatusEnum.Information => new KeyValuePair("status", monitoringApi == Monitoring.StatusUri ? "erfolgreich" : "info"), - StatusEnum.Warning => new KeyValuePair("status", monitoringApi == Monitoring.StatusUri ? "Interaktion" : "warning"), - StatusEnum.Error => new KeyValuePair("status", monitoringApi == Monitoring.StatusUri ? "fehlgeschlagen" : "error"), - _ => throw new NotImplementedException(nameof(monitoringStatus)) - }); - - // insert task, timestamp, message, - monitoringContent.Add(new KeyValuePair("task", monitoring.Task)); - - if (monitoring.Timestamp is not null) - { - monitoringContent.Add(new KeyValuePair("timestamp", monitoring.Timestamp.Value.ToLocalTime().ToString())); + // webmatic safety offset + await Task.Delay(TimeSpan.FromSeconds(1), default); } - - if (string.IsNullOrWhiteSpace(monitoring.Message) is false) - { - monitoringContent.Add(new KeyValuePair("message", monitoring.Message)); - } - - // send message - var result = await _httpClient.PostAsync(monitoringApi, new FormUrlEncodedContent(monitoringContent), default); - - monitoringResult.Add(new KeyValuePair("HttpStatusCode", result.StatusCode.ToString())); - monitoringResult.Add(new KeyValuePair("HttpResponseMessage", await result.Content.ReadAsStringAsync(default))); - - // if content != "OK" - if (result is null || result.IsSuccessStatusCode == false) return DispatchEnum.Failure; - - // success - return DispatchEnum.Success; } } + + private async ValueTask SendAsync(HostLogMonitoringEntity monitoring, CancellationToken cancellationToken) + { + _logger.LogTrace($"SendAsync ({monitoring})"); + + var monitoringApi = Monitoring.LogUri; + var monitoringContent = new List>(); + var monitoringResult = new List>(); + + // adjust by category + if (Enum.TryParse(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("category", monitoringCategory.ToString())); + + // host resolve + var hostEntity = await _database.Host().Find(Builders.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.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("computer_name", monitoring.Hostname.ToLower())); + + // insert converted status (api styled) + if (Enum.TryParse(monitoring.Status, true, out var monitoringStatus) is false) return DispatchEnum.Failure; + + monitoringContent.Add(monitoringStatus switch + { + StatusEnum.Information => new KeyValuePair("status", monitoringApi == Monitoring.StatusUri ? "erfolgreich" : "info"), + StatusEnum.Warning => new KeyValuePair("status", monitoringApi == Monitoring.StatusUri ? "Interaktion" : "warning"), + StatusEnum.Error => new KeyValuePair("status", monitoringApi == Monitoring.StatusUri ? "fehlgeschlagen" : "error"), + _ => throw new NotImplementedException(nameof(monitoringStatus)) + }); + + // insert task, timestamp, message, + monitoringContent.Add(new KeyValuePair("task", monitoring.Task)); + + if (monitoring.Timestamp is not null) + { + monitoringContent.Add(new KeyValuePair("timestamp", monitoring.Timestamp.Value.ToLocalTime().ToString())); + } + + if (string.IsNullOrWhiteSpace(monitoring.Message) is false) + { + monitoringContent.Add(new KeyValuePair("message", monitoring.Message)); + } + + // send message + var result = await _httpClient.PostAsync(monitoringApi, new FormUrlEncodedContent(monitoringContent), default); + + monitoringResult.Add(new KeyValuePair("HttpStatusCode", result.StatusCode.ToString())); + monitoringResult.Add(new KeyValuePair("HttpResponseMessage", await result.Content.ReadAsStringAsync(default))); + + // if content != "OK" + if (result is null || result.IsSuccessStatusCode == false) return DispatchEnum.Failure; + + // success + return DispatchEnum.Success; + } } \ No newline at end of file diff --git a/src/Server/Insight.Server/Services/JobService.cs b/src/Server/Insight.Server/Services/JobService.cs index d692ed4..1619007 100644 --- a/src/Server/Insight.Server/Services/JobService.cs +++ b/src/Server/Insight.Server/Services/JobService.cs @@ -1,81 +1,113 @@ -using Insight.Agent.Messages; -using Insight.Infrastructure; +using Amazon.Runtime.Internal; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using Insight.Server.Extensions; -using Insight.Server.Network; +using Insight.Server.Network.Agent; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MongoDB.Driver; using Vaitr.Network; -namespace Insight.Server.Services +namespace Insight.Server.Services; + +internal class JobService : BackgroundService { - internal class JobService : BackgroundService + private readonly ISessionPool _agentPool; + private readonly IMongoDatabase _database; + private readonly ILogger _logger; + + public JobService(ISessionPool agentPool, IMongoDatabase database, ILogger logger) { - private readonly TcpSessionPool _agentPool; - private readonly IMongoDatabase _database; - private readonly ILogger _logger; + _agentPool = agentPool; + _database = database; + _logger = logger; + } - public JobService(TcpSessionPool agentPool, IMongoDatabase database, ILogger logger) + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); + + var jobs = new List { - _agentPool = agentPool; - _database = database; - _logger = logger; + RunInventoryAsync(cancellationToken), + //RunCustomsAsync(cancellationToken) + }; + + try + { + await Task.WhenAll(jobs).ConfigureAwait(false); } + catch (OperationCanceledException) { } + catch (Exception) { } + } - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + private async Task RunInventoryAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("RunInventoryAsync"); + + while (cancellationToken.IsCancellationRequested is false) { - _logger.LogTrace("ExecuteAsync"); - - await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); - - var jobs = new List - { - Task.Run(async () => - { - while (cancellationToken.IsCancellationRequested is false) - { - try - { - foreach (var agent in await GetAssignedAgentsAsync(cancellationToken)) - { - await agent.SendAsync(new GetInventory(), cancellationToken); - } - } - catch (OperationCanceledException) { } - catch (Exception) { } - finally - { - await Task.Delay(TimeSpan.FromHours(1), cancellationToken); - } - } - }, default) - }; - try { - await Task.WhenAll(jobs).ConfigureAwait(false); + foreach (var agent in await GetAssignedAgentsAsync(cancellationToken)) + { + await agent.SendAsync(new InventoryRequest(), cancellationToken); + } } catch (OperationCanceledException) { } catch (Exception) { } - } - - private async ValueTask> GetAssignedAgentsAsync(CancellationToken cancellationToken) - { - var valid = new List(); - - await Async.ParallelForEach(_agentPool.Where(p => p.Value.Id is not null), async x => + finally { - var host = await _database.Host() - .Find(Builders.Filter.Eq(p => p.Agent, x.Value.Id)) - .FirstOrDefaultAsync(cancellationToken) - .ConfigureAwait(false); - - if (host is null) return; - valid.Add(x.Value); - }); - - return valid; + await Task.Delay(TimeSpan.FromHours(1), cancellationToken); + } } } + + private async Task RunCustomsAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("RunCustomsAsync"); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + // check if agent online + if (_agentPool.FirstOrDefault().Value is not AgentSession agent) continue; + + // proxy-send request packet to agent + await agent.SendAsync(new Request + { + RequestId = "test", + RequestData = "Get-Date" + }, cancellationToken); + } + catch (OperationCanceledException) { } + catch (Exception) { } + finally + { + await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); + } + } + + + } + + private async ValueTask> GetAssignedAgentsAsync(CancellationToken cancellationToken) + { + var valid = new List(); + + await Async.ParallelForEach(_agentPool.Where(p => p.Value.Id is not null), async x => + { + var host = await _database.Host() + .Find(Builders.Filter.Eq(p => p.Agent, x.Value.Id)) + .FirstOrDefaultAsync(cancellationToken) + .ConfigureAwait(false); + + if (host is null) return; + valid.Add(x.Value); + }); + + return valid; + } } \ No newline at end of file diff --git a/src/Server/Insight.Server/appsettings.Development.json b/src/Server/Insight.Server/appsettings.Development.json index 17abbae..859a335 100644 --- a/src/Server/Insight.Server/appsettings.Development.json +++ b/src/Server/Insight.Server/appsettings.Development.json @@ -1,5 +1,10 @@ { - "database": "mongodb://10.22.70.40:32768", + "database": "mongodb://db.insight.local:27017", + + "remote.server.port": 3003, + "remote.server.certificate": "localhost.pfx", + "remote.server.certificate.password": "Webmatic12", + "agent.server.port": 3002, "agent.server.certificate": "localhost.pfx", "agent.server.certificate.password": "Webmatic12", diff --git a/src/Server/Insight.Server/appsettings.json b/src/Server/Insight.Server/appsettings.json index b0ee8b6..c43acdf 100644 --- a/src/Server/Insight.Server/appsettings.json +++ b/src/Server/Insight.Server/appsettings.json @@ -1,5 +1,6 @@ { "database": "mongodb://127.0.0.1:27017", + "agent.server.port": 3002, "agent.server.certificate": "localhost.pfx", "agent.server.certificate.password": "Webmatic12", diff --git a/src/Setup/Insight.Setup.Windows/Constants/Deploy.cs b/src/Setup/Insight.Setup.Windows/Constants/Deploy.cs index 724759b..5240b5e 100644 --- a/src/Setup/Insight.Setup.Windows/Constants/Deploy.cs +++ b/src/Setup/Insight.Setup.Windows/Constants/Deploy.cs @@ -1,49 +1,48 @@ -namespace Insight.Setup.Constants +namespace Insight.Setup.Constants; + +public static class Deploy { - public static class Deploy + public static class Runtime { - public static class Runtime + public static class Core { - public static class Core - { - public const string Version = "7.0.2"; - public const string Download = "https://download.visualstudio.microsoft.com/download/pr/df7da01f-1f17-4728-92b7-778e9607da8f/7c18246830f8c78591f02f25aa368dcf/dotnet-runtime-7.0.2-win-x64.exe"; + public const string Version = "7.0.2"; + public const string Download = "https://download.visualstudio.microsoft.com/download/pr/df7da01f-1f17-4728-92b7-778e9607da8f/7c18246830f8c78591f02f25aa368dcf/dotnet-runtime-7.0.2-win-x64.exe"; - public static DirectoryInfo Directory => new DirectoryInfo($@"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)}/dotnet/shared/Microsoft.NETCore.App"); - } - - public static class Asp - { - public const string Version = "7.0.2"; - public const string Download = "https://download.visualstudio.microsoft.com/download/pr/3ecad4f7-1342-4688-ae4a-38908c61f4a2/391a9010acad2e312e3d1e766bedfac7/aspnetcore-runtime-7.0.2-win-x64.exe"; - - public static DirectoryInfo Directory => new DirectoryInfo($@"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)}/dotnet/shared/Microsoft.AspNetCore.App"); - } + public static DirectoryInfo Directory => new DirectoryInfo($@"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)}/dotnet/shared/Microsoft.NETCore.App"); } - public static class Updater + public static class Asp { - public const string Name = "Updater"; - public const string ServiceName = "insight_updater"; - public const string Description = "Insight Updater"; + public const string Version = "7.0.2"; + public const string Download = "https://download.visualstudio.microsoft.com/download/pr/3ecad4f7-1342-4688-ae4a-38908c61f4a2/391a9010acad2e312e3d1e766bedfac7/aspnetcore-runtime-7.0.2-win-x64.exe"; - public static Uri UpdateHref(Uri api) => new($"{api.AbsoluteUri}/{Name}/windows"); + public static DirectoryInfo Directory => new DirectoryInfo($@"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)}/dotnet/shared/Microsoft.AspNetCore.App"); } - - public static class Agent - { - public const string Name = "Agent"; - public const string ServiceName = "insight_agent"; - public const string Description = "Insight Agent"; - } - - public static DirectoryInfo GetAppDirectory(string appName) - => new($"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}/Webmatic/Insight/{appName}"); - - public static FileInfo GetAppExecutable(string appName) - => new($"{GetAppDirectory(appName).FullName}/{appName.ToLower()}.exe"); - - public static Uri GetUpdateHref(Uri api, string appName) - => new($"{api.AbsoluteUri}/update/{appName.ToLower()}/windows"); } + + public static class Updater + { + public const string Name = "Updater"; + public const string ServiceName = "insight_updater"; + public const string Description = "Insight Updater"; + + public static Uri UpdateHref(Uri api) => new($"{api.AbsoluteUri}/{Name}/windows"); + } + + public static class Agent + { + public const string Name = "Agent"; + public const string ServiceName = "insight_agent"; + public const string Description = "Insight Agent"; + } + + public static DirectoryInfo GetAppDirectory(string appName) + => new($"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}/Webmatic/Insight/{appName}"); + + public static FileInfo GetAppExecutable(string appName) + => new($"{GetAppDirectory(appName).FullName}/{appName.ToLower()}.exe"); + + public static Uri GetUpdateHref(Uri api, string appName) + => new($"{api.AbsoluteUri}/update/{appName.ToLower()}/windows"); } \ No newline at end of file diff --git a/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj b/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj index 907d9aa..377172e 100644 --- a/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj +++ b/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj @@ -8,10 +8,7 @@ setup Insight.Setup Insight - 2025.2.24.0 - 2025.2.24.0 - none - true + 2023.12.14.0 diff --git a/src/Setup/Insight.Setup.Windows/Program.cs b/src/Setup/Insight.Setup.Windows/Program.cs index 8c488d7..4450a9a 100644 --- a/src/Setup/Insight.Setup.Windows/Program.cs +++ b/src/Setup/Insight.Setup.Windows/Program.cs @@ -5,54 +5,53 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Runtime.Versioning; -namespace Insight.Setup.Windows +namespace Insight.Setup.Windows; + +[SupportedOSPlatform("windows")] +internal class Program { - [SupportedOSPlatform("windows")] - public class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) - { - await Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(options => + await Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(options => + { + options.Sources.Clear(); + options.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + options.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); + + options.AddCommandLine(args, new Dictionary() { - options.Sources.Clear(); - options.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); - options.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); + { "-deploy", "deploy" }, + { "--deploy", "deploy" } + }); + }) + .ConfigureLogging(options => + { + options.ClearProviders(); + options.SetMinimumLevel(LogLevel.Trace); - options.AddCommandLine(args, new Dictionary() - { - { "-deploy", "deploy" }, - { "--deploy", "deploy" } - }); - }) - .ConfigureLogging(options => + options.AddSimpleConsole(options => { - options.ClearProviders(); - options.SetMinimumLevel(LogLevel.Trace); + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; + }); - options.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; - }); + options.AddFilter("Microsoft", LogLevel.Warning); + }) + .ConfigureServices((host, services) => + { + // SERVICES + services.AddHostedService(); - options.AddFilter("Microsoft", LogLevel.Warning); - }) - .ConfigureServices((host, services) => + // GLOBALS + services.AddTransient(provider => new HttpClient(new HttpClientHandler { - // SERVICES - services.AddHostedService(); - - // GLOBALS - services.AddTransient(provider => new HttpClient(new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true - })); - }) - .Build() - .RunAsync(); - } + ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true + })); + }) + .Build() + .RunAsync(); } } \ No newline at end of file diff --git a/src/Setup/Insight.Setup.Windows/Services/Deployment.cs b/src/Setup/Insight.Setup.Windows/Services/Deployment.cs index 57a8047..cfddfa9 100644 --- a/src/Setup/Insight.Setup.Windows/Services/Deployment.cs +++ b/src/Setup/Insight.Setup.Windows/Services/Deployment.cs @@ -6,472 +6,471 @@ using System.Runtime.Versioning; using System.ServiceProcess; using System.Text.Json; -namespace Insight.Setup.Services +namespace Insight.Setup.Services; + +public static class Deployment { - public static class Deployment + [SupportedOSPlatform("windows")] + public static class Windows { - [SupportedOSPlatform("windows")] - public static class Windows + public static class Service { - public static class Service + private static bool ServiceExistence(string serviceName) { - private static bool ServiceExistence(string serviceName) + try { - try - { - if (ServiceController.GetServices().Any(s => s.ServiceName.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase))) return true; - return false; - } - catch (Exception) { } - + if (ServiceController.GetServices().Any(s => s.ServiceName.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase))) return true; return false; } + catch (Exception) { } - private static bool SetServiceState(string app, ServiceControllerStatus status, TimeSpan timeout) + return false; + } + + private static bool SetServiceState(string app, ServiceControllerStatus status, TimeSpan timeout) + { + try { - try + using var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName.Equals(app, StringComparison.InvariantCultureIgnoreCase)); + if (sc is null) return false; + + if (sc.Status != status) { - using var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName.Equals(app, StringComparison.InvariantCultureIgnoreCase)); - if (sc is null) return false; - - if (sc.Status != status) + switch (status) { - switch (status) - { - case ServiceControllerStatus.Running: - sc.Start(); - break; + case ServiceControllerStatus.Running: + sc.Start(); + break; - case ServiceControllerStatus.Stopped: - sc.Stop(); - break; - } - - sc.WaitForStatus(status, timeout); + case ServiceControllerStatus.Stopped: + sc.Stop(); + break; } - return true; + sc.WaitForStatus(status, timeout); } - catch (Exception) { } + return true; + } + catch (Exception) { } + + return false; + } + + private static async ValueTask InstallAsync(FileInfo bin, string serviceName, string displayName, string description, bool autorun) + { + if (bin.Exists is false) return false; + if (ServiceExistence(serviceName)) return false; + + var args = @$"/C sc create {serviceName} binPath= ""{bin.FullName}"" DisplayName= ""{displayName}"""; + args += $@" && sc description {serviceName} ""{description}"""; + args += $@" && sc config {serviceName} start= {(autorun ? "auto" : "demand")}"; + args += $@" && sc start {serviceName}"; + + try + { + using var process = new System.Diagnostics.Process() + { + StartInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Normal, + FileName = "cmd.exe", + Arguments = args, + Verb = "runas", + RedirectStandardOutput = true, + UseShellExecute = false + } + }; + + process.Start(); + + var output = await process.StandardOutput.ReadToEndAsync(); + + if (Directory.GetParent(bin.FullName) is not DirectoryInfo serviceDir) return false; + + return ServiceExistence(serviceName); // may return output as return model if existence false + } + catch (Exception) + { return false; } - - private static async ValueTask InstallAsync(FileInfo bin, string serviceName, string displayName, string description, bool autorun) - { - if (bin.Exists is false) return false; - if (ServiceExistence(serviceName)) return false; - - var args = @$"/C sc create {serviceName} binPath= ""{bin.FullName}"" DisplayName= ""{displayName}"""; - args += $@" && sc description {serviceName} ""{description}"""; - args += $@" && sc config {serviceName} start= {(autorun ? "auto" : "demand")}"; - args += $@" && sc start {serviceName}"; - - try - { - using var process = new System.Diagnostics.Process() - { - StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Normal, - FileName = "cmd.exe", - Arguments = args, - Verb = "runas", - RedirectStandardOutput = true, - UseShellExecute = false - } - }; - - process.Start(); - - var output = await process.StandardOutput.ReadToEndAsync(); - - if (Directory.GetParent(bin.FullName) is not DirectoryInfo serviceDir) return false; - - return ServiceExistence(serviceName); // may return output as return model if existence false - } - catch (Exception) - { - return false; - } - } - - public static async ValueTask InstallAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, string serviceDisplayName, string serviceDescription, bool autorun, CancellationToken cancellationToken) - { - var result = new InstallResult - { - Api = api?.ToString(), - SourceDirectory = bin.Directory?.FullName, - App = bin.Name, - ServiceName = serviceName, - Autorun = autorun - }; - - try - { - if (ServiceExistence(serviceName)) - { - result.Errors.Add("Service already installed"); - return result; - } - - var response = await httpClient.GetFromJsonAsync(api, cancellationToken); - if (response is null) - { - result.ApiErrors.Add("not available / response null"); - return result; - } - - // get update file (bytes) to memory - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) - { - result.ApiErrors.Add("update source not available"); - return result; - } - - result.ApiAvailable = true; - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // install service with windows api - if (await InstallAsync(bin, serviceName, serviceDisplayName, serviceDescription, autorun) is false) - { - result.Errors.Add("installation failed"); - return result; - } - - result.Success = true; - } - catch (Exception ex) - { - result.Errors.Add(ex.Message); - } - - return result; - } - - public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, CancellationToken cancellationToken) - { - var result = new UpdateResult - { - Api = api?.ToString(), - SourceDirectory = bin.Directory?.FullName, - App = bin.Name, - ServiceName = serviceName - }; - - try - { - // check if service exists - if (ServiceExistence(serviceName) is false) - { - result.UpdateErrors.Add("service not found"); - return result; - } - - // get service update details - var response = await httpClient.GetFromJsonAsync(api, cancellationToken); - if (response is null) - { - result.ApiErrors.Add("not available / response null"); - return result; - } - - result.ApiAvailable = true; - - // check if local binary exists - if (bin is null) - { - result.UpdateErrors.Add("source binary not found"); - return result; - } - - // get local file binary version - if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) - { - result.UpdateErrors.Add("source binary fileversion not valid"); - return result; - } - - // compare local against update version, skip lower or equal update version - var actualVersion = Version.Parse(binVersionString); - if (actualVersion >= response.Version) - { - result.Success = true; - return result; - } - else - { - result.UpdateAvailable = true; - } - - // get update file (bytes) to memory - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) - { - result.ApiErrors.Add("update source not available"); - return result; - } - - // stop service - if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)) is false) - { - result.UpdateErrors.Add("service control failed / failed to stop service"); - return result; - } - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // start updateds service - if (SetServiceState(serviceName, ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)) is false) - { - result.UpdateErrors.Add("service control failed / failed to start service"); - return result; - } - - result.Success = true; - } - catch (Exception ex) - { - result.UpdateErrors.Add(ex.Message); - } - - return result; - } - - public static async ValueTask UninstallAsync(string serviceName) - { - var result = new UninstallResult - { - ServiceName = serviceName - }; - - try - { - if (ServiceExistence(serviceName) is false) - { - result.Errors.Add("service not found"); - return result; - } - - if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(60)) is false) - { - result.Errors.Add("service control failed / failed to stop service"); - return result; - } - - using var process = new System.Diagnostics.Process() - { - StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Normal, - FileName = "cmd.exe", - Arguments = $"/C sc delete {serviceName}", - Verb = "runas", - RedirectStandardOutput = true, - UseShellExecute = false - } - }; - - process.Start(); - - var output = await process.StandardOutput.ReadToEndAsync(); - - // may return output as return model if existence true - - if (ServiceExistence(serviceName)) - { - result.Errors.Add("service still existing"); - } - - result.Success = true; - } - catch (Exception ex) - { - result.Errors.Add(ex.Message); - } - - return result; - } } - public static class Process + public static async ValueTask InstallAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, string serviceDisplayName, string serviceDescription, bool autorun, CancellationToken cancellationToken) { - public static bool IsRunning(FileInfo bin) + var result = new InstallResult { - if (bin.Exists is false) return false; + Api = api?.ToString(), + SourceDirectory = bin.Directory?.FullName, + App = bin.Name, + ServiceName = serviceName, + Autorun = autorun + }; - var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); - - if (matched is null || matched.Any() is false) return false; - - if (matched.Any(p => - p.MainModule is not null && - p.MainModule.FileName is not null && - p.MainModule.FileName.Equals(bin.FullName, - StringComparison.InvariantCultureIgnoreCase))) return true; - - return false; - } - - public static bool Start(FileInfo binary) + try { - try + if (ServiceExistence(serviceName)) { - if (IsRunning(binary) is false) return false; - - using var process = System.Diagnostics.Process.Start(binary.FullName); - return true; + result.Errors.Add("Service already installed"); + return result; } - catch (Exception) { } - return false; + var response = await httpClient.GetFromJsonAsync(api, cancellationToken); + if (response is null) + { + result.ApiErrors.Add("not available / response null"); + return result; + } + + // get update file (bytes) to memory + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) + { + result.ApiErrors.Add("update source not available"); + return result; + } + + result.ApiAvailable = true; + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // install service with windows api + if (await InstallAsync(bin, serviceName, serviceDisplayName, serviceDescription, autorun) is false) + { + result.Errors.Add("installation failed"); + return result; + } + + result.Success = true; + } + catch (Exception ex) + { + result.Errors.Add(ex.Message); } - public static bool Stop(FileInfo bin, TimeSpan timeout) + return result; + } + + public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, CancellationToken cancellationToken) + { + var result = new UpdateResult { - try + Api = api?.ToString(), + SourceDirectory = bin.Directory?.FullName, + App = bin.Name, + ServiceName = serviceName + }; + + try + { + // check if service exists + if (ServiceExistence(serviceName) is false) { - if (IsRunning(bin) is false) return false; + result.UpdateErrors.Add("service not found"); + return result; + } - var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + // get service update details + var response = await httpClient.GetFromJsonAsync(api, cancellationToken); + if (response is null) + { + result.ApiErrors.Add("not available / response null"); + return result; + } - if (matched is null || matched.Any() is false) return true; + result.ApiAvailable = true; - foreach (var procsInfo in matched.Where(p => - p.MainModule is not null && - p.MainModule.FileName is not null && - p.MainModule.FileName.Equals(bin.FullName, StringComparison.InvariantCultureIgnoreCase))) + // check if local binary exists + if (bin is null) + { + result.UpdateErrors.Add("source binary not found"); + return result; + } + + // get local file binary version + if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) + { + result.UpdateErrors.Add("source binary fileversion not valid"); + return result; + } + + // compare local against update version, skip lower or equal update version + var actualVersion = Version.Parse(binVersionString); + if (actualVersion >= response.Version) + { + result.Success = true; + return result; + } + else + { + result.UpdateAvailable = true; + } + + // get update file (bytes) to memory + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) + { + result.ApiErrors.Add("update source not available"); + return result; + } + + // stop service + if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)) is false) + { + result.UpdateErrors.Add("service control failed / failed to stop service"); + return result; + } + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // start updateds service + if (SetServiceState(serviceName, ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)) is false) + { + result.UpdateErrors.Add("service control failed / failed to start service"); + return result; + } + + result.Success = true; + } + catch (Exception ex) + { + result.UpdateErrors.Add(ex.Message); + } + + return result; + } + + public static async ValueTask UninstallAsync(string serviceName) + { + var result = new UninstallResult + { + ServiceName = serviceName + }; + + try + { + if (ServiceExistence(serviceName) is false) + { + result.Errors.Add("service not found"); + return result; + } + + if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(60)) is false) + { + result.Errors.Add("service control failed / failed to stop service"); + return result; + } + + using var process = new System.Diagnostics.Process() + { + StartInfo = new ProcessStartInfo { - if (procsInfo.CloseMainWindow()) procsInfo.WaitForExit((int)timeout.TotalMilliseconds); - if (procsInfo.HasExited is false) procsInfo.Kill(true); + WindowStyle = ProcessWindowStyle.Normal, + FileName = "cmd.exe", + Arguments = $"/C sc delete {serviceName}", + Verb = "runas", + RedirectStandardOutput = true, + UseShellExecute = false } + }; - return true; + process.Start(); + + var output = await process.StandardOutput.ReadToEndAsync(); + + // may return output as return model if existence true + + if (ServiceExistence(serviceName)) + { + result.Errors.Add("service still existing"); } - catch (Exception) { } - return false; + result.Success = true; + } + catch (Exception ex) + { + result.Errors.Add(ex.Message); } - public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, CancellationToken cancellationToken) + return result; + } + } + + public static class Process + { + public static bool IsRunning(FileInfo bin) + { + if (bin.Exists is false) return false; + + var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + + if (matched is null || matched.Any() is false) return false; + + if (matched.Any(p => + p.MainModule is not null && + p.MainModule.FileName is not null && + p.MainModule.FileName.Equals(bin.FullName, + StringComparison.InvariantCultureIgnoreCase))) return true; + + return false; + } + + public static bool Start(FileInfo binary) + { + try + { + if (IsRunning(binary) is false) return false; + + using var process = System.Diagnostics.Process.Start(binary.FullName); + return true; + } + catch (Exception) { } + + return false; + } + + public static bool Stop(FileInfo bin, TimeSpan timeout) + { + try { if (IsRunning(bin) is false) return false; - var response = await httpClient.GetFromJsonAsync(api.AbsoluteUri, new JsonSerializerOptions + var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + + if (matched is null || matched.Any() is false) return true; + + foreach (var procsInfo in matched.Where(p => + p.MainModule is not null && + p.MainModule.FileName is not null && + p.MainModule.FileName.Equals(bin.FullName, StringComparison.InvariantCultureIgnoreCase))) { - IncludeFields = true - }, cancellationToken); - - if (response is null) return false; - - Version actualVersion; - - if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) return false; - - try - { - actualVersion = Version.Parse(binVersionString); - if (actualVersion >= response.Version) return false; - - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) return false; - - Stop(bin, TimeSpan.FromSeconds(60)); - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // rewrite with options to start user session process - //Start(app, directory, TimeSpan.FromSeconds(60)); - - return true; + if (procsInfo.CloseMainWindow()) procsInfo.WaitForExit((int)timeout.TotalMilliseconds); + if (procsInfo.HasExited is false) procsInfo.Kill(true); } - catch (Exception) { } - return false; + return true; } + catch (Exception) { } - public static bool Delete(FileInfo bin, TimeSpan timeout) + return false; + } + + public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, CancellationToken cancellationToken) + { + if (IsRunning(bin) is false) return false; + + var response = await httpClient.GetFromJsonAsync(api.AbsoluteUri, new JsonSerializerOptions { - try - { - Stop(bin, timeout); - bin.Delete(); + IncludeFields = true + }, cancellationToken); - return true; - } - catch (Exception) { } + if (response is null) return false; - return false; + Version actualVersion; + + if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) return false; + + try + { + actualVersion = Version.Parse(binVersionString); + if (actualVersion >= response.Version) return false; + + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) return false; + + Stop(bin, TimeSpan.FromSeconds(60)); + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // rewrite with options to start user session process + //Start(app, directory, TimeSpan.FromSeconds(60)); + + return true; } + catch (Exception) { } + + return false; + } + + public static bool Delete(FileInfo bin, TimeSpan timeout) + { + try + { + Stop(bin, timeout); + bin.Delete(); + + return true; + } + catch (Exception) { } + + return false; } } + } - public class InstallResult - { - public string? Api { get; set; } - public string? SourceDirectory { get; set; } - public string? App { get; set; } - public string? ServiceName { get; set; } - public bool Autorun { get; set; } + public class InstallResult + { + public string? Api { get; set; } + public string? SourceDirectory { get; set; } + public string? App { get; set; } + public string? ServiceName { get; set; } + public bool Autorun { get; set; } - public bool ApiAvailable { get; set; } = false; - public bool Success { get; set; } = false; - public List ApiErrors { get; } = new(); - public List Errors { get; } = new(); - } + public bool ApiAvailable { get; set; } = false; + public bool Success { get; set; } = false; + public List ApiErrors { get; } = new(); + public List Errors { get; } = new(); + } - public class UpdateResult - { - public string? Api { get; set; } - public string? SourceDirectory { get; set; } - public string? App { get; set; } - public string? ServiceName { get; set; } + public class UpdateResult + { + public string? Api { get; set; } + public string? SourceDirectory { get; set; } + public string? App { get; set; } + public string? ServiceName { get; set; } - public bool ApiAvailable { get; set; } = false; - public bool UpdateAvailable { get; set; } = false; - public bool Success { get; set; } = false; - public List ApiErrors { get; } = new(); - public List UpdateErrors { get; } = new(); - } + public bool ApiAvailable { get; set; } = false; + public bool UpdateAvailable { get; set; } = false; + public bool Success { get; set; } = false; + public List ApiErrors { get; } = new(); + public List UpdateErrors { get; } = new(); + } - public class UninstallResult - { - public string? ServiceName { get; set; } + public class UninstallResult + { + public string? ServiceName { get; set; } - public bool Success { get; set; } = false; - public List Errors { get; } = new(); - } + public bool Success { get; set; } = false; + public List Errors { get; } = new(); } } \ No newline at end of file diff --git a/src/Setup/Insight.Setup.Windows/Services/SetupService.cs b/src/Setup/Insight.Setup.Windows/Services/SetupService.cs index 54df73b..547bec9 100644 --- a/src/Setup/Insight.Setup.Windows/Services/SetupService.cs +++ b/src/Setup/Insight.Setup.Windows/Services/SetupService.cs @@ -5,243 +5,242 @@ using Microsoft.Extensions.Logging; using System.Diagnostics; using System.Runtime.Versioning; -namespace Insight.Setup.Services +namespace Insight.Setup.Services; + +[SupportedOSPlatform("windows")] +internal class SetupService : BackgroundService { - [SupportedOSPlatform("windows")] - internal class SetupService : BackgroundService + private readonly Uri _uri; + + private readonly HttpClient _httpClient; + private readonly IHostApplicationLifetime _lifetime; + private readonly ILogger _logger; + + public SetupService(HttpClient httpClient, IHostApplicationLifetime lifetime, IConfiguration configuration, ILogger logger) { - private readonly Uri _uri; + _httpClient = httpClient; + _lifetime = lifetime; + _uri = configuration.GetValue("api") ?? new Uri("https://insight.webmatic.de/api"); //throw new Exception($"api value not set (appsettings)"); + _logger = logger; + } - private readonly HttpClient _httpClient; - private readonly IHostApplicationLifetime _lifetime; - private readonly ILogger _logger; + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); - public SetupService(HttpClient httpClient, IHostApplicationLifetime lifetime, IConfiguration configuration, ILogger logger) + Console.WriteLine("1: Install"); + Console.WriteLine("2: Update"); + Console.WriteLine("3: Uninstall"); + + Console.WriteLine("\n"); + + var key = Console.ReadKey(); + + Console.Clear(); + + try { - _httpClient = httpClient; - _lifetime = lifetime; - _uri = configuration.GetValue("api") ?? new Uri("https://insight.webmatic.de/api"); //throw new Exception($"api value not set (appsettings)"); - _logger = logger; - } - - protected override async Task ExecuteAsync(CancellationToken cancellationToken) - { - _logger.LogTrace("ExecuteAsync"); - - Console.WriteLine("1: Install"); - Console.WriteLine("2: Update"); - Console.WriteLine("3: Uninstall"); - - Console.WriteLine("\n"); - - var key = Console.ReadKey(); - - Console.Clear(); - - try + switch (key.Key) { - switch (key.Key) - { - case ConsoleKey.NumPad1: - case ConsoleKey.D1: - { - await InstallAsync(cancellationToken); - break; - } - case ConsoleKey.NumPad2: - case ConsoleKey.D2: - { - await UpdateAsync(cancellationToken); - break; - } - case ConsoleKey.NumPad3: - case ConsoleKey.D3: - { - await UninstallAsync(); - break; - } - default: - { - Console.WriteLine("invalid selection"); - break; - } - } - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - finally - { - _lifetime.StopApplication(); - } - } - - private async ValueTask InstallAsync(CancellationToken cancellationToken) - { - Console.WriteLine("Install Runtime"); - await InstallRuntimeAsync(cancellationToken); - - Console.WriteLine("Install ASP Runtime"); - await InstallAspRuntimeAsync(cancellationToken); - - // UPDATER - Console.WriteLine("Install Updater"); - - var updaterResult = await Deployment.Windows.Service.InstallAsync( - _httpClient, - Deploy.GetUpdateHref(_uri, Deploy.Updater.Name), - Deploy.GetAppExecutable(Deploy.Updater.Name), - Deploy.Updater.ServiceName, - Deploy.Updater.Description, - Deploy.Updater.Description, - true, - cancellationToken); - - Console.WriteLine($"Updater: {updaterResult.Success}"); - Console.WriteLine($"Updater: {string.Concat(updaterResult.Errors)}"); - - // AGENT - Console.WriteLine("Install Agent"); - - var agentResult = await Deployment.Windows.Service.InstallAsync( - _httpClient, - Deploy.GetUpdateHref(_uri, Deploy.Agent.Name), - Deploy.GetAppExecutable(Deploy.Agent.Name), - Deploy.Agent.ServiceName, - Deploy.Agent.Description, - Deploy.Agent.Description, - true, - cancellationToken); - - Console.WriteLine($"Agent: {agentResult}"); - Console.WriteLine($"Agent: {string.Concat(agentResult.Errors)}"); - } - - private async ValueTask UpdateAsync(CancellationToken cancellationToken) - { - // UPDATER - Console.WriteLine("Update Updater"); - - var updateResult = await Deployment.Windows.Service.UpdateAsync( - _httpClient, - Deploy.GetUpdateHref(_uri, Deploy.Updater.Name), - Deploy.GetAppExecutable(Deploy.Updater.Name), - Deploy.Updater.ServiceName, - cancellationToken); - - Console.WriteLine($"Result: {updateResult}"); - - // AGENT - Console.WriteLine("Update Agent"); - - var agentResult = await Deployment.Windows.Service.UpdateAsync( - _httpClient, - Deploy.GetUpdateHref(_uri, Deploy.Agent.Name), - Deploy.GetAppExecutable(Deploy.Agent.Name), - Deploy.Agent.ServiceName, - cancellationToken); - - Console.WriteLine($"Result: {agentResult}"); - } - - private static async ValueTask UninstallAsync() - { - // UPDATER - Console.WriteLine("Uninstall Updater"); - - var updaterResult = await Deployment.Windows.Service.UninstallAsync(Deploy.Updater.ServiceName); - Console.WriteLine($"Result: {updaterResult}"); - - // AGENT - Console.WriteLine("Uninstall Agent"); - - var agentResult = await Deployment.Windows.Service.UninstallAsync(Deploy.Agent.ServiceName); - Console.WriteLine($"Result: {agentResult}"); - } - - private async ValueTask InstallRuntimeAsync(CancellationToken cancellationToken) - { - // try get dotnet folders - var coreDirectory = Deploy.Runtime.Core.Directory; - - // Runtime Installation Check... - if (coreDirectory.Exists && coreDirectory.EnumerateDirectories().Any(x => x.Name == Deploy.Runtime.Core.Version)) return; - - // Downloading Runtime... - var queryResult = await _httpClient.GetAsync(Deploy.Runtime.Core.Download, cancellationToken); - - var tempDir = Directory.CreateTempSubdirectory(); - - await File.WriteAllBytesAsync( - $@"{tempDir.FullName}/runtime.exe", - await queryResult.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // Installing Runtime... - var runtimeFile = new FileInfo($@"{tempDir.FullName}/runtime.exe"); - - using var process = new Process() - { - StartInfo = new ProcessStartInfo - { - FileName = runtimeFile.FullName, - ArgumentList = + case ConsoleKey.NumPad1: + case ConsoleKey.D1: { - @"/install", - @"/quiet", - @"/norestart" - }, - UseShellExecute = false, - RedirectStandardOutput = true - } - }; - - process.Start(); - await process.WaitForExitAsync(cancellationToken); - - if (runtimeFile.Exists) runtimeFile.Delete(); - } - - private async ValueTask InstallAspRuntimeAsync(CancellationToken cancellationToken) - { - // try get dotnet folders - var aspDirectory = Deploy.Runtime.Asp.Directory; - - // Runtime Installation Check... - if (aspDirectory.Exists && aspDirectory.EnumerateDirectories().Any(x => x.Name == Deploy.Runtime.Asp.Version)) return; - - // Downloading Runtime... - var queryResult = await _httpClient.GetAsync(Deploy.Runtime.Asp.Download, cancellationToken); - - var tempDir = Directory.CreateTempSubdirectory(); - - await File.WriteAllBytesAsync( - $@"{tempDir.FullName}/runtime.exe", - await queryResult.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // Installing Runtime... - var runtimeFile = new FileInfo($@"{tempDir.FullName}/runtime.exe"); - - using var process = new Process() - { - StartInfo = new ProcessStartInfo - { - FileName = runtimeFile.FullName, - ArgumentList = + await InstallAsync(cancellationToken); + break; + } + case ConsoleKey.NumPad2: + case ConsoleKey.D2: { - @"/install", - @"/quiet", - @"/norestart" - }, - UseShellExecute = false, - RedirectStandardOutput = true - } - }; - - process.Start(); - await process.WaitForExitAsync(cancellationToken); - - if (runtimeFile.Exists) runtimeFile.Delete(); + await UpdateAsync(cancellationToken); + break; + } + case ConsoleKey.NumPad3: + case ConsoleKey.D3: + { + await UninstallAsync(); + break; + } + default: + { + Console.WriteLine("invalid selection"); + break; + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + finally + { + _lifetime.StopApplication(); } } + + private async ValueTask InstallAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Install Runtime"); + await InstallRuntimeAsync(cancellationToken); + + Console.WriteLine("Install ASP Runtime"); + await InstallAspRuntimeAsync(cancellationToken); + + // UPDATER + Console.WriteLine("Install Updater"); + + var updaterResult = await Deployment.Windows.Service.InstallAsync( + _httpClient, + Deploy.GetUpdateHref(_uri, Deploy.Updater.Name), + Deploy.GetAppExecutable(Deploy.Updater.Name), + Deploy.Updater.ServiceName, + Deploy.Updater.Description, + Deploy.Updater.Description, + true, + cancellationToken); + + Console.WriteLine($"Updater: {updaterResult.Success}"); + Console.WriteLine($"Updater: {string.Concat(updaterResult.Errors)}"); + + // AGENT + Console.WriteLine("Install Agent"); + + var agentResult = await Deployment.Windows.Service.InstallAsync( + _httpClient, + Deploy.GetUpdateHref(_uri, Deploy.Agent.Name), + Deploy.GetAppExecutable(Deploy.Agent.Name), + Deploy.Agent.ServiceName, + Deploy.Agent.Description, + Deploy.Agent.Description, + true, + cancellationToken); + + Console.WriteLine($"Agent: {agentResult}"); + Console.WriteLine($"Agent: {string.Concat(agentResult.Errors)}"); + } + + private async ValueTask UpdateAsync(CancellationToken cancellationToken) + { + // UPDATER + Console.WriteLine("Update Updater"); + + var updateResult = await Deployment.Windows.Service.UpdateAsync( + _httpClient, + Deploy.GetUpdateHref(_uri, Deploy.Updater.Name), + Deploy.GetAppExecutable(Deploy.Updater.Name), + Deploy.Updater.ServiceName, + cancellationToken); + + Console.WriteLine($"Result: {updateResult}"); + + // AGENT + Console.WriteLine("Update Agent"); + + var agentResult = await Deployment.Windows.Service.UpdateAsync( + _httpClient, + Deploy.GetUpdateHref(_uri, Deploy.Agent.Name), + Deploy.GetAppExecutable(Deploy.Agent.Name), + Deploy.Agent.ServiceName, + cancellationToken); + + Console.WriteLine($"Result: {agentResult}"); + } + + private static async ValueTask UninstallAsync() + { + // UPDATER + Console.WriteLine("Uninstall Updater"); + + var updaterResult = await Deployment.Windows.Service.UninstallAsync(Deploy.Updater.ServiceName); + Console.WriteLine($"Result: {updaterResult}"); + + // AGENT + Console.WriteLine("Uninstall Agent"); + + var agentResult = await Deployment.Windows.Service.UninstallAsync(Deploy.Agent.ServiceName); + Console.WriteLine($"Result: {agentResult}"); + } + + private async ValueTask InstallRuntimeAsync(CancellationToken cancellationToken) + { + // try get dotnet folders + var coreDirectory = Deploy.Runtime.Core.Directory; + + // Runtime Installation Check... + if (coreDirectory.Exists && coreDirectory.EnumerateDirectories().Any(x => x.Name == Deploy.Runtime.Core.Version)) return; + + // Downloading Runtime... + var queryResult = await _httpClient.GetAsync(Deploy.Runtime.Core.Download, cancellationToken); + + var tempDir = Directory.CreateTempSubdirectory(); + + await File.WriteAllBytesAsync( + $@"{tempDir.FullName}/runtime.exe", + await queryResult.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // Installing Runtime... + var runtimeFile = new FileInfo($@"{tempDir.FullName}/runtime.exe"); + + using var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = runtimeFile.FullName, + ArgumentList = + { + @"/install", + @"/quiet", + @"/norestart" + }, + UseShellExecute = false, + RedirectStandardOutput = true + } + }; + + process.Start(); + await process.WaitForExitAsync(cancellationToken); + + if (runtimeFile.Exists) runtimeFile.Delete(); + } + + private async ValueTask InstallAspRuntimeAsync(CancellationToken cancellationToken) + { + // try get dotnet folders + var aspDirectory = Deploy.Runtime.Asp.Directory; + + // Runtime Installation Check... + if (aspDirectory.Exists && aspDirectory.EnumerateDirectories().Any(x => x.Name == Deploy.Runtime.Asp.Version)) return; + + // Downloading Runtime... + var queryResult = await _httpClient.GetAsync(Deploy.Runtime.Asp.Download, cancellationToken); + + var tempDir = Directory.CreateTempSubdirectory(); + + await File.WriteAllBytesAsync( + $@"{tempDir.FullName}/runtime.exe", + await queryResult.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // Installing Runtime... + var runtimeFile = new FileInfo($@"{tempDir.FullName}/runtime.exe"); + + using var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = runtimeFile.FullName, + ArgumentList = + { + @"/install", + @"/quiet", + @"/norestart" + }, + UseShellExecute = false, + RedirectStandardOutput = true + } + }; + + process.Start(); + await process.WaitForExitAsync(cancellationToken); + + if (runtimeFile.Exists) runtimeFile.Delete(); + } } \ No newline at end of file diff --git a/src/Updater/Insight.Updater/Constants/Deploy.cs b/src/Updater/Insight.Updater/Constants/Deploy.cs index b285b98..86d9a6d 100644 --- a/src/Updater/Insight.Updater/Constants/Deploy.cs +++ b/src/Updater/Insight.Updater/Constants/Deploy.cs @@ -1,21 +1,20 @@ -namespace Insight.Updater.Constants +namespace Insight.Updater.Constants; + +public static class Deploy { - public static class Deploy + public static class Agent { - public static class Agent - { - public const string Name = "Agent"; - public const string ServiceName = "insight_agent"; - public const string Description = "Insight Agent"; - } - - public static DirectoryInfo GetAppDirectory(string appName) - => new($"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}/Webmatic/Insight/{appName}"); - - public static FileInfo GetAppExecutable(string appName) - => new($"{GetAppDirectory(appName).FullName}/{appName.ToLower()}.exe"); - - public static Uri GetUpdateHref(Uri api, string appName) - => new($"{api.AbsoluteUri}/update/{appName.ToLower()}/windows"); + public const string Name = "Agent"; + public const string ServiceName = "insight_agent"; + public const string Description = "Insight Agent"; } + + public static DirectoryInfo GetAppDirectory(string appName) + => new($"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}/Webmatic/Insight/{appName}"); + + public static FileInfo GetAppExecutable(string appName) + => new($"{GetAppDirectory(appName).FullName}/{appName.ToLower()}.exe"); + + public static Uri GetUpdateHref(Uri api, string appName) + => new($"{api.AbsoluteUri}/update/{appName.ToLower()}/windows"); } \ No newline at end of file diff --git a/src/Updater/Insight.Updater/Insight.Updater.csproj b/src/Updater/Insight.Updater/Insight.Updater.csproj index 88c3b73..c4d2f53 100644 --- a/src/Updater/Insight.Updater/Insight.Updater.csproj +++ b/src/Updater/Insight.Updater/Insight.Updater.csproj @@ -6,12 +6,9 @@ Insight Insight.Updater updater - 2025.2.24.0 - 2025.2.24.0 + 2023.12.14.0 enable enable - none - true diff --git a/src/Updater/Insight.Updater/Program.cs b/src/Updater/Insight.Updater/Program.cs index 892f807..55d2eab 100644 --- a/src/Updater/Insight.Updater/Program.cs +++ b/src/Updater/Insight.Updater/Program.cs @@ -5,53 +5,52 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace Insight.Updater.Windows +namespace Insight.Updater.Windows; + +internal class Program { - internal class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) + var builder = Host.CreateDefaultBuilder(args); + builder.UseWindowsService(); + builder.UseSystemd(); + + builder.ConfigureAppConfiguration(options => { - var builder = Host.CreateDefaultBuilder(args); - builder.UseWindowsService(); - builder.UseSystemd(); + options.Sources.Clear(); + options.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + options.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); + }); - builder.ConfigureAppConfiguration(options => + builder.ConfigureLogging(options => + { + options.ClearProviders(); + options.SetMinimumLevel(LogLevel.Trace); + + options.AddSimpleConsole(options => { - options.Sources.Clear(); - options.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); - options.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; }); - builder.ConfigureLogging(options => + options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/updater_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); + }); + + builder.ConfigureServices((host, services) => + { + // SERVICES + services.AddHostedService(); + + // GLOBALS + services.AddTransient(provider => new HttpClient(new HttpClientHandler { - options.ClearProviders(); - options.SetMinimumLevel(LogLevel.Trace); + ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true + })); + }); - options.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; - }); - - options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/updater_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); - }); - - builder.ConfigureServices((host, services) => - { - // SERVICES - services.AddHostedService(); - - // GLOBALS - services.AddTransient(provider => new HttpClient(new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true - })); - }); - - var host = builder.Build(); - await host.RunAsync().ConfigureAwait(false); - } + var host = builder.Build(); + await host.RunAsync().ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Updater/Insight.Updater/Services/UpdateService.cs b/src/Updater/Insight.Updater/Services/UpdateService.cs index e88c598..c2935b7 100644 --- a/src/Updater/Insight.Updater/Services/UpdateService.cs +++ b/src/Updater/Insight.Updater/Services/UpdateService.cs @@ -10,351 +10,350 @@ using System.Runtime.Versioning; using System.ServiceProcess; using System.Text.Json; -namespace Insight.Updater.Services +namespace Insight.Updater.Services; + +public class UpdateService : BackgroundService { - public class UpdateService : BackgroundService + private readonly Uri _uri; + + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + public UpdateService(HttpClient httpClient, IConfiguration configuration, ILogger logger) { - private readonly Uri _uri; + _httpClient = httpClient; + _uri = configuration.GetValue("api") ?? throw new Exception($"api value not set (appsettings)"); + _logger = logger; + } - private readonly HttpClient _httpClient; - private readonly ILogger _logger; + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("ExecuteAsync"); - public UpdateService(HttpClient httpClient, IConfiguration configuration, ILogger logger) + while (cancellationToken.IsCancellationRequested is false) { - _httpClient = httpClient; - _uri = configuration.GetValue("api") ?? throw new Exception($"api value not set (appsettings)"); - _logger = logger; + try + { + UpdateResult? result = null; + + if (OperatingSystem.IsWindows()) result = await WindowsUpdateAsync(cancellationToken); + if (OperatingSystem.IsLinux()) result = await LinuxUpdateAsync(cancellationToken); + + _logger.LogInformation("Update Result: {result}", result?.Success); + if (result?.UpdateErrors is not null) + { + _logger.LogError("Update Errors: {errors}", string.Concat(result?.UpdateErrors)); + } + } + catch (OperationCanceledException) { } + catch (Exception ex) // may inform via client / api about errors + { + _logger.LogError("{ex}", ex.Message); + } + finally + { + await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); + } } + } - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + [SupportedOSPlatform("windows")] + private async ValueTask WindowsUpdateAsync(CancellationToken cancellationToken) + { + return await Windows.Service.UpdateAsync( + _httpClient, + Deploy.GetUpdateHref(_uri, Deploy.Agent.Name), + Deploy.GetAppExecutable(Deploy.Agent.Name), + Deploy.Agent.ServiceName, + cancellationToken); + } + + [SupportedOSPlatform("linux")] + private ValueTask LinuxUpdateAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + [SupportedOSPlatform("windows")] + private static class Windows + { + public static class Service { - _logger.LogTrace("ExecuteAsync"); - - while (cancellationToken.IsCancellationRequested is false) + private static bool ServiceExistence(string serviceName) { try { - UpdateResult? result = null; + if (ServiceController.GetServices().Any(s => s.ServiceName.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase))) return true; + return false; + } + catch (Exception) { } - if (OperatingSystem.IsWindows()) result = await WindowsUpdateAsync(cancellationToken); - if (OperatingSystem.IsLinux()) result = await LinuxUpdateAsync(cancellationToken); - - _logger.LogInformation("Update Result: {result}", result?.Success); - if (result?.UpdateErrors is not null) - { - _logger.LogError("Update Errors: {errors}", string.Concat(result?.UpdateErrors)); - } - } - catch (OperationCanceledException) { } - catch (Exception ex) // may inform via client / api about errors - { - _logger.LogError("{ex}", ex.Message); - } - finally - { - await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); - } + return false; } - } - [SupportedOSPlatform("windows")] - private async ValueTask WindowsUpdateAsync(CancellationToken cancellationToken) - { - return await Windows.Service.UpdateAsync( - _httpClient, - Deploy.GetUpdateHref(_uri, Deploy.Agent.Name), - Deploy.GetAppExecutable(Deploy.Agent.Name), - Deploy.Agent.ServiceName, - cancellationToken); - } - - [SupportedOSPlatform("linux")] - private ValueTask LinuxUpdateAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - [SupportedOSPlatform("windows")] - private static class Windows - { - public static class Service + private static bool SetServiceState(string app, ServiceControllerStatus status, TimeSpan timeout) { - private static bool ServiceExistence(string serviceName) + try { - try + using var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName.Equals(app, StringComparison.InvariantCultureIgnoreCase)); + if (sc is null) return false; + + if (sc.Status != status) { - if (ServiceController.GetServices().Any(s => s.ServiceName.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase))) return true; - return false; + switch (status) + { + case ServiceControllerStatus.Running: + sc.Start(); + break; + + case ServiceControllerStatus.Stopped: + sc.Stop(); + break; + } + + sc.WaitForStatus(status, timeout); } - catch (Exception) { } - return false; + return true; } + catch (Exception) { } - private static bool SetServiceState(string app, ServiceControllerStatus status, TimeSpan timeout) + return false; + } + + public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, CancellationToken cancellationToken) + { + var result = new UpdateResult { - try + Api = api?.ToString(), + SourceDirectory = bin.Directory?.FullName, + App = bin.Name, + ServiceName = serviceName + }; + + try + { + // check if service exists + if (ServiceExistence(serviceName) is false) { - using var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName.Equals(app, StringComparison.InvariantCultureIgnoreCase)); - if (sc is null) return false; - - if (sc.Status != status) - { - switch (status) - { - case ServiceControllerStatus.Running: - sc.Start(); - break; - - case ServiceControllerStatus.Stopped: - sc.Stop(); - break; - } - - sc.WaitForStatus(status, timeout); - } - - return true; + result.UpdateErrors.Add("service not found"); + return result; } - catch (Exception) { } - return false; - } - - public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, string serviceName, CancellationToken cancellationToken) - { - var result = new UpdateResult + // get service update details + var response = await httpClient.GetFromJsonAsync(api, cancellationToken); + if (response is null) { - Api = api?.ToString(), - SourceDirectory = bin.Directory?.FullName, - App = bin.Name, - ServiceName = serviceName - }; + result.ApiErrors.Add("not available / response null"); + return result; + } - try + result.ApiAvailable = true; + + // check if local binary exists + if (bin is null) { - // check if service exists - if (ServiceExistence(serviceName) is false) - { - result.UpdateErrors.Add("service not found"); - return result; - } + result.UpdateErrors.Add("source binary not found"); + return result; + } - // get service update details - var response = await httpClient.GetFromJsonAsync(api, cancellationToken); - if (response is null) - { - result.ApiErrors.Add("not available / response null"); - return result; - } - - result.ApiAvailable = true; - - // check if local binary exists - if (bin is null) - { - result.UpdateErrors.Add("source binary not found"); - return result; - } - - // get local file binary version - if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) - { - result.UpdateErrors.Add("source binary fileversion not valid"); - return result; - } - - // compare local against update version, skip lower or equal update version - var actualVersion = Version.Parse(binVersionString); - if (actualVersion >= response.Version) - { - result.Success = true; - return result; - } - else - { - result.UpdateAvailable = true; - } - - // get update file (bytes) to memory - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) - { - result.ApiErrors.Add("update source not available"); - return result; - } - - // stop service - if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)) is false) - { - result.UpdateErrors.Add("service control failed / failed to stop service"); - return result; - } - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // start updateds service - if (SetServiceState(serviceName, ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)) is false) - { - result.UpdateErrors.Add("service control failed / failed to start service"); - return result; - } + // get local file binary version + if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) + { + result.UpdateErrors.Add("source binary fileversion not valid"); + return result; + } + // compare local against update version, skip lower or equal update version + var actualVersion = Version.Parse(binVersionString); + if (actualVersion >= response.Version) + { result.Success = true; + return result; } - catch (Exception ex) + else { - result.UpdateErrors.Add(ex.Message); + result.UpdateAvailable = true; } - return result; + // get update file (bytes) to memory + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) + { + result.ApiErrors.Add("update source not available"); + return result; + } + + // stop service + if (SetServiceState(serviceName, ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)) is false) + { + result.UpdateErrors.Add("service control failed / failed to stop service"); + return result; + } + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // start updateds service + if (SetServiceState(serviceName, ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)) is false) + { + result.UpdateErrors.Add("service control failed / failed to start service"); + return result; + } + + result.Success = true; } + catch (Exception ex) + { + result.UpdateErrors.Add(ex.Message); + } + + return result; + } + } + + public static class Process + { + public static bool IsRunning(FileInfo bin) + { + if (bin.Exists is false) return false; + + var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + + if (matched is null || matched.Any() is false) return false; + + if (matched.Any(p => + p.MainModule is not null && + p.MainModule.FileName is not null && + p.MainModule.FileName.Equals(bin.FullName, + StringComparison.InvariantCultureIgnoreCase))) return true; + + return false; } - public static class Process + public static bool Start(FileInfo binary) { - public static bool IsRunning(FileInfo bin) + try { - if (bin.Exists is false) return false; + if (IsRunning(binary) is false) return false; - var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); - - if (matched is null || matched.Any() is false) return false; - - if (matched.Any(p => - p.MainModule is not null && - p.MainModule.FileName is not null && - p.MainModule.FileName.Equals(bin.FullName, - StringComparison.InvariantCultureIgnoreCase))) return true; - - return false; + using var process = System.Diagnostics.Process.Start(binary.FullName); + return true; } + catch (Exception) { } - public static bool Start(FileInfo binary) - { - try - { - if (IsRunning(binary) is false) return false; + return false; + } - using var process = System.Diagnostics.Process.Start(binary.FullName); - return true; - } - catch (Exception) { } - - return false; - } - - public static bool Stop(FileInfo bin, TimeSpan timeout) - { - try - { - if (IsRunning(bin) is false) return false; - - var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); - - if (matched is null || matched.Any() is false) return true; - - foreach (var procsInfo in matched.Where(p => - p.MainModule is not null && - p.MainModule.FileName is not null && - p.MainModule.FileName.Equals(bin.FullName, StringComparison.InvariantCultureIgnoreCase))) - { - if (procsInfo.CloseMainWindow()) procsInfo.WaitForExit((int)timeout.TotalMilliseconds); - if (procsInfo.HasExited is false) procsInfo.Kill(true); - } - - return true; - } - catch (Exception) { } - - return false; - } - - public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, CancellationToken cancellationToken) + public static bool Stop(FileInfo bin, TimeSpan timeout) + { + try { if (IsRunning(bin) is false) return false; - var response = await httpClient.GetFromJsonAsync(api.AbsoluteUri, new JsonSerializerOptions + var matched = System.Diagnostics.Process.GetProcessesByName(bin.FullName); + + if (matched is null || matched.Any() is false) return true; + + foreach (var procsInfo in matched.Where(p => + p.MainModule is not null && + p.MainModule.FileName is not null && + p.MainModule.FileName.Equals(bin.FullName, StringComparison.InvariantCultureIgnoreCase))) { - IncludeFields = true - }, cancellationToken); - - if (response is null) return false; - - Version actualVersion; - - if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) return false; - - try - { - actualVersion = Version.Parse(binVersionString); - if (actualVersion >= response.Version) return false; - - using var update = await httpClient.GetAsync(response.Uri, cancellationToken); - if (update is null) return false; - - Stop(bin, TimeSpan.FromSeconds(60)); - - // read update archive to temp (overwrite) - var temp = Directory.CreateTempSubdirectory(); - var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); - - await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); - - // extract update archive from temp to app dir (overwrite) - ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); - - // delete temp folder - if (temp.Exists) temp.Delete(true); - - // rewrite with options to start user session process - //Start(app, directory, TimeSpan.FromSeconds(60)); - - return true; + if (procsInfo.CloseMainWindow()) procsInfo.WaitForExit((int)timeout.TotalMilliseconds); + if (procsInfo.HasExited is false) procsInfo.Kill(true); } - catch (Exception) { } - return false; + return true; } + catch (Exception) { } - public static bool Delete(FileInfo bin, TimeSpan timeout) + return false; + } + + public static async ValueTask UpdateAsync(HttpClient httpClient, Uri api, FileInfo bin, CancellationToken cancellationToken) + { + if (IsRunning(bin) is false) return false; + + var response = await httpClient.GetFromJsonAsync(api.AbsoluteUri, new JsonSerializerOptions { - try - { - Stop(bin, timeout); - bin.Delete(); + IncludeFields = true + }, cancellationToken); - return true; - } - catch (Exception) { } + if (response is null) return false; - return false; + Version actualVersion; + + if (FileVersionInfo.GetVersionInfo(bin.FullName).FileVersion is not string binVersionString) return false; + + try + { + actualVersion = Version.Parse(binVersionString); + if (actualVersion >= response.Version) return false; + + using var update = await httpClient.GetAsync(response.Uri, cancellationToken); + if (update is null) return false; + + Stop(bin, TimeSpan.FromSeconds(60)); + + // read update archive to temp (overwrite) + var temp = Directory.CreateTempSubdirectory(); + var updateFile = new FileInfo($@"{temp.FullName}/{bin.Name}.zip"); + + await File.WriteAllBytesAsync(updateFile.FullName, await update.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken); + + // extract update archive from temp to app dir (overwrite) + ZipFile.ExtractToDirectory(updateFile.FullName, bin.Directory?.FullName, true); + + // delete temp folder + if (temp.Exists) temp.Delete(true); + + // rewrite with options to start user session process + //Start(app, directory, TimeSpan.FromSeconds(60)); + + return true; } + catch (Exception) { } + + return false; + } + + public static bool Delete(FileInfo bin, TimeSpan timeout) + { + try + { + Stop(bin, timeout); + bin.Delete(); + + return true; + } + catch (Exception) { } + + return false; } } + } - public class UpdateResult - { - public string? Api { get; set; } - public string? SourceDirectory { get; set; } - public string? App { get; set; } - public string? ServiceName { get; set; } + public class UpdateResult + { + public string? Api { get; set; } + public string? SourceDirectory { get; set; } + public string? App { get; set; } + public string? ServiceName { get; set; } - public bool ApiAvailable { get; set; } = false; - public bool UpdateAvailable { get; set; } = false; - public bool Success { get; set; } = false; - public List ApiErrors { get; } = new(); - public List UpdateErrors { get; } = new(); - } + public bool ApiAvailable { get; set; } = false; + public bool UpdateAvailable { get; set; } = false; + public bool Success { get; set; } = false; + public List ApiErrors { get; } = new(); + public List UpdateErrors { get; } = new(); } } \ No newline at end of file diff --git a/src/Web/Insight.Web.Assets/Insight.Web.Assets.csproj b/src/Web/Insight.Web.Assets/Insight.Web.Assets.csproj deleted file mode 100644 index 4fecff5..0000000 --- a/src/Web/Insight.Web.Assets/Insight.Web.Assets.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net7.0 - true - enable - Insight.Web.Assets - Insight.Web - Insight - 2025.2.24.0 - 2025.2.24.0 - none - true - - - - - - - - diff --git a/src/Web/Insight.Web.Assets/Interfaces/IWebMessageHandler.cs b/src/Web/Insight.Web.Assets/Interfaces/IWebMessageHandler.cs deleted file mode 100644 index 2609347..0000000 --- a/src/Web/Insight.Web.Assets/Interfaces/IWebMessageHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Insight.Web.Messages; - -namespace Insight.Web.Interfaces -{ - public partial interface IWebMessageHandler - { - ValueTask HandleAsync(TSender sender, TMessage message, CancellationToken cancellationToken) where TMessage : IWebMessage; - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web.Assets/Messages/Host/ConsoleProxy.cs b/src/Web/Insight.Web.Assets/Messages/Host/ConsoleProxy.cs deleted file mode 100644 index 0a5783f..0000000 --- a/src/Web/Insight.Web.Assets/Messages/Host/ConsoleProxy.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MemoryPack; - -namespace Insight.Web.Messages -{ - [MemoryPackUnion(1, typeof(ConsoleQueryProxy))] - [MemoryPackUnion(2, typeof(ConsoleQueryProxyRequest))] - public partial interface IWebMessage { } - - [MemoryPackable] - public partial class ConsoleQueryProxy : IWebMessage - { - [MemoryPackOrder(0)] - public string? Id { get; set; } - - [MemoryPackOrder(1)] - public string? HostId { get; set; } - - [MemoryPackOrder(2)] - public string? Query { get; set; } - - [MemoryPackOrder(3)] - public string? Data { get; set; } - - [MemoryPackOrder(4)] - public string? Errors { get; set; } - - [MemoryPackOrder(7)] - public bool HadErrors { get; set; } - } - - [MemoryPackable] - public partial class ConsoleQueryProxyRequest : IWebMessage - { - [MemoryPackOrder(0)] - public string? Id { get; set; } - - [MemoryPackOrder(1)] - public string? HostId { get; set; } - - [MemoryPackOrder(2)] - public string? Query { get; set; } - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web.Assets/Messages/IWebMessage.cs b/src/Web/Insight.Web.Assets/Messages/IWebMessage.cs deleted file mode 100644 index 6ac0a66..0000000 --- a/src/Web/Insight.Web.Assets/Messages/IWebMessage.cs +++ /dev/null @@ -1,7 +0,0 @@ -using MemoryPack; - -namespace Insight.Web.Messages -{ - [MemoryPackable] - public partial interface IWebMessage { } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Cards/InfoCard.razor b/src/Web/Insight.Web/Components/Cards/InfoCard.razor index 207c6c9..9b0c1d9 100644 --- a/src/Web/Insight.Web/Components/Cards/InfoCard.razor +++ b/src/Web/Insight.Web/Components/Cards/InfoCard.razor @@ -1,4 +1,7 @@ - +@inherits ComponentBase +@implements IDisposable + + @@ -21,27 +24,35 @@ @code { - [Parameter] - public string? Href { get; set; } + [Parameter] public string? Href { get; set; } + [Parameter] public EventCallback OnClick { get; set; } + [Parameter] public string? Icon { get; set; } + [Parameter] public Color? IconColor { get; set; } + [Parameter] public string? Key { get; set; } + [Parameter] public Color? KeyColor { get; set; } + [Parameter] public string? Text { get; set; } + [Parameter] public int Lines { get; set; } = 1; - [Parameter] - public EventCallback OnClick { get; set; } + private bool _disposed; - [Parameter] - public string? Icon { get; set; } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - [Parameter] - public Color? IconColor { get; set; } + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; - [Parameter] - public string? Key { get; set; } + try + { - [Parameter] - public Color? KeyColor { get; set; } - - [Parameter] - public string? Text { get; set; } - - [Parameter] - public int Lines { get; set; } = 1; + } + finally + { + _disposed = true; + } + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Cards/KeyValueCard.razor b/src/Web/Insight.Web/Components/Cards/KeyValueCard.razor index 01a4527..70e4120 100644 --- a/src/Web/Insight.Web/Components/Cards/KeyValueCard.razor +++ b/src/Web/Insight.Web/Components/Cards/KeyValueCard.razor @@ -1,5 +1,8 @@ @typeparam T +@inherits ComponentBase +@implements IDisposable + @@ -31,27 +34,35 @@ @code{ - [Parameter] - public string? Href { get; set; } + [Parameter] public string? Href { get; set; } + [Parameter] public EventCallback OnClick { get; set; } + [Parameter] public string? Icon { get; set; } + [Parameter] public Color? IconColor { get; set; } + [Parameter] public string? Key { get; set; } + [Parameter] public Color? KeyColor { get; set; } + [Parameter] public T? Value { get; set; } + [Parameter] public Color? ValueColor { get; set; } - [Parameter] - public EventCallback OnClick { get; set; } + private bool _disposed; - [Parameter] - public string? Icon { get; set; } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - [Parameter] - public Color? IconColor { get; set; } + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; - [Parameter] - public string? Key { get; set; } + try + { - [Parameter] - public Color? KeyColor { get; set; } - - [Parameter] - public T? Value { get; set; } - - [Parameter] - public Color? ValueColor { get; set; } + } + finally + { + _disposed = true; + } + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Containers/BaseContainer.razor b/src/Web/Insight.Web/Components/Containers/BaseContainer.razor index 5d504ad..eb4a679 100644 --- a/src/Web/Insight.Web/Components/Containers/BaseContainer.razor +++ b/src/Web/Insight.Web/Components/Containers/BaseContainer.razor @@ -1,28 +1,11 @@ -@Title +@inherits ComponentBase +@implements IDisposable -@*@if (Loading) -{ - +@Title -
- -
- - return; -}*@ - -
-
+
+
-@* - - - - @item.Text - - - - *@ @@ -43,9 +26,75 @@
-
+
@if (Content is not null) { @Content } -
\ No newline at end of file +
+ +@code{ + [Parameter] public string Title { get; set; } = Global.Name; + [Parameter] public List? Breadcrumbs { get; set; } + [Parameter] public RenderFragment? BreadcrumbAction { get; set; } + [Parameter] public RenderFragment? Content { get; set; } + [Parameter] public Func? LoadData { get; set; } + [Parameter] public bool DisableReload { get; set; } + + private bool _disposed; + + private bool _loading; + private bool Loading + { + get => _loading; + set + { + if (value != _loading) + { + _loading = value; + StateHasChanged(); + } + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) await OnRefreshAsync(); + } + + private async Task OnRefreshAsync() + { + if (LoadData is null || Loading) return; + + try + { + Loading = true; + await LoadData(); + } + finally + { + Loading = false; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Containers/BaseContainer.razor.cs b/src/Web/Insight.Web/Components/Containers/BaseContainer.razor.cs deleted file mode 100644 index 8f5ab44..0000000 --- a/src/Web/Insight.Web/Components/Containers/BaseContainer.razor.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Insight.Web.Constants; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace Insight.Web.Components.Containers; - -public partial class BaseContainer -{ - [Parameter] - public string Title { get; set; } = Global.Name; - - [Parameter] - public List? Breadcrumbs { get; set; } - - [Parameter] - public RenderFragment? BreadcrumbAction { get; set; } - - [Parameter] - public RenderFragment? Content { get; set; } - - [Parameter] - public Func? LoadData { get; set; } - - [Parameter] - public bool DisableReload { get; set; } - - - private bool _loading; - private bool Loading - { - get - { - return _loading; - } - set - { - if (value != _loading) - { - _loading = value; - StateHasChanged(); - } - } - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await OnRefreshAsync(); - } - } - - private async Task OnRefreshAsync() - { - if (LoadData is null || Loading) return; - - Loading = true; - StateHasChanged(); - - //var start = Stopwatch.GetTimestamp(); - - await LoadData(); - - //var time = Stopwatch.GetElapsedTime(start); - - //if (time <= TimeSpan.FromSeconds(1)) - //{ - // await Task.Delay(TimeSpan.FromSeconds(1).Subtract(time)); - //} - - Loading = false; - StateHasChanged(); - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Containers/TableContainer.razor b/src/Web/Insight.Web/Components/Containers/TableContainer.razor index a7d06ff..faed0f2 100644 --- a/src/Web/Insight.Web/Components/Containers/TableContainer.razor +++ b/src/Web/Insight.Web/Components/Containers/TableContainer.razor @@ -1,17 +1,24 @@ -@typeparam T +@using Insight.Web.Extensions + +@typeparam T + +@inherits ComponentBase +@implements IDisposable + +@inject NavigationManager NavigationManager @Title -
+
@if (OnAdd.HasDelegate) { - @if (AddBreadcrumbs is not null) + @if (_optionalBreadcrumbs is not null) { - + } @@ -25,7 +32,7 @@
- + @if (Header is not null) { @@ -36,7 +43,7 @@ Filter - + Refresh @@ -63,4 +70,137 @@ - \ No newline at end of file + + +@code{ + [Parameter] public string Title { get; set; } = Global.Name; + [Parameter] public List? Breadcrumbs { get; set; } + [Parameter] public Func>>? Data { get; set; } + [Parameter] public RenderFragment? Header { get; set; } + [Parameter] public RenderFragment? RowTemplate { get; set; } + [Parameter] public RenderFragment? ActionTemplate { get; set; } + [Parameter] public bool DisableAction { get; set; } + [Parameter] public bool DisableRefresh { get; set; } + [Parameter] public string? Search + { + get => _search; + set + { + if (_search == value) return; + _search = value; + + SearchChanged.InvokeAsync(value); + } + } + [Parameter] public bool Filtered { get; set; } + [Parameter] public EventCallback SearchChanged { get; set; } + [Parameter] public EventCallback OnFilter { get; set; } + [Parameter] public EventCallback OnAdd { get; set; } + + private List? _optionalBreadcrumbs; + private MudTable? _table; + private string? _search; + private bool _loading; + private bool _disposed; + + private bool Loading + { + get => _loading; + set + { + if (value != _loading) + { + _loading = value; + StateHasChanged(); + } + } + } + + protected override void OnInitialized() + { + if (OnAdd.HasDelegate) + { + _optionalBreadcrumbs = new List() + { + new("", "#", true), + new("", "#", true) + }; + } + + if (NavigationManager.GetQueryString().TryGetValue("search", out var search)) + { + Search = search.ToString(); + } + } + + private async Task> OnLoadDataAsync(TableState state) + { + if (Data is null) throw new MissingMethodException(nameof(Data)); + + try + { + Loading = true; + return await Data(state); + } + finally + { + Loading = false; + } + } + + public async Task RefreshAsync() + { + if (Loading || _table is null) return; + + try + { + Loading = true; + await _table.ReloadServerData(); + } + finally + { + Loading = false; + } + } + + private async Task SearchAsync(string? text) + { + Search = text; + + if (Loading) return; + await RefreshAsync(); + } + + private void OnSearchReleased() => NavigationManager.ChangeQueryStringValue("search", Search); + + private async Task OnFilterClick() + { + if (OnFilter.HasDelegate) await OnFilter.InvokeAsync(this); + } + + private async Task OnAddClick() + { + if (OnAdd.HasDelegate) await OnAdd.InvokeAsync(this); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Containers/TableContainer.razor.cs b/src/Web/Insight.Web/Components/Containers/TableContainer.razor.cs deleted file mode 100644 index bf75e8f..0000000 --- a/src/Web/Insight.Web/Components/Containers/TableContainer.razor.cs +++ /dev/null @@ -1,194 +0,0 @@ -using Insight.Web.Constants; -using Insight.Web.Extensions; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace Insight.Web.Components.Containers; - -public partial class TableContainer -{ - [Inject] private NavigationManager NavigationManager { get; init; } = default!; - - - [Parameter] - public string Title { get; set; } = Global.Name; - - [Parameter] - public List? Breadcrumbs { get; set; } - - [Parameter] - public Func>>? Data { get; set; } - - [Parameter] - public RenderFragment? Header { get; set; } - - [Parameter] - public RenderFragment? RowTemplate { get; set; } - - [Parameter] - public RenderFragment? ActionTemplate { get; set; } - - [Parameter] - public bool DisableAction { get; set; } - - - private List? AddBreadcrumbs { get; set; } - private MudTable? Table { get; set; } - private bool Loading { get; set; } - - private int CurrentPage { get; set; } - private int CurrentSize { get; set; } - - protected override void OnInitialized() - { - if (OnAdd.HasDelegate) - { - AddBreadcrumbs = new List() - { - new BreadcrumbItem("", "#", true), - new BreadcrumbItem("", "#", true) - }; - } - - if (NavigationManager.GetQueryString().TryGetValue("search", out var search)) - { - Search = search.ToString(); - } - - //if (NavigationManager.GetQueryString().TryGetValue("page", out var pageRaw)) - //{ - // if (int.TryParse(pageRaw, out var page)) - // { - // CurrentPage = page; - // } - //} - - //if (NavigationManager.GetQueryString().TryGetValue("size", out var sizeRaw)) - //{ - // if (int.TryParse(sizeRaw, out var size)) - // { - // CurrentSize = size == 0 ? 100 : size; - // } - //} - } - - private async Task> OnLoadDataAsync(TableState state) - { - if (Data is null) - { - throw new MissingMethodException(nameof(Data)); - } - - try - { - Loading = true; - - //state.Page = CurrentPage; - //Table.SetRowsPerPage(CurrentSize); - - var data = await Data(state); - - //CurrentPage = state.Page; - //CurrentSize = state.PageSize; - - //NavigationManager.ChangeQueryStringValue("page", state.Page.ToString()); - //NavigationManager.ChangeQueryStringValue("size", state.PageSize.ToString()); - - //if (state.SortLabel is not null) NavigationManager.ChangeQueryStringValue("sort", state.SortLabel.ToString()); - //if (state.SortDirection) NavigationManager.ChangeQueryStringValue("direction", state.SortDirection.ToString()); - - return data; - } - finally - { - Loading = false; - } - } - - // - - [Parameter] - public bool DisableRefresh { get; set; } - - private string? _search; - - [Parameter] - public string? Search - { - get => _search; - set - { - if (_search == value) return; - _search = value; - - SearchChanged.InvokeAsync(value); - } - } - - [Parameter] - public EventCallback SearchChanged { get; set; } - - private async Task SearchAsync(string? text) - { - Search = text; - - if (Loading) return; - await RefreshAsync(); - } - - public async Task RefreshAsync() - { - if (Loading || Table is null) return; - - Loading = true; - StateHasChanged(); - - //var start = Stopwatch.GetTimestamp(); - - await Table.ReloadServerData(); - - //var time = Stopwatch.GetElapsedTime(start); - - //if (time <= TimeSpan.FromSeconds(1)) - //{ - // await Task.Delay(TimeSpan.FromSeconds(1).Subtract(time)); - //} - - Loading = false; - StateHasChanged(); - } - - private void OnSearchReleased() - { - NavigationManager.ChangeQueryStringValue("search", Search); - } - - // - - [Parameter] public EventCallback OnFilter { get; set; } - - - [Parameter] - public bool Filtered { get; set; } - - private async Task OnFilterClick() - { - if (OnFilter.HasDelegate) - { - await OnFilter.InvokeAsync(this); - } - } - - // - - [Parameter] - public EventCallback OnAdd { get; set; } - - private async Task OnAddClick() - { - if (OnAdd.HasDelegate) - { - await OnAdd.InvokeAsync(this); - } - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor b/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor index 660fea5..773d039 100644 --- a/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor +++ b/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor @@ -1,4 +1,7 @@ -@if (@Actions is not null) +@inherits ComponentBase +@implements IDisposable + +@if (@Actions is not null) { @@ -15,11 +18,59 @@ } else { - + @Content +} + +@code{ + [CascadingParameter] public MudDialogInstance MudDialog { get; set; } = default!; + + [Parameter] public bool Visible + { + get => _visible; + set + { + if (_visible == value) return; + _visible = value; + + VisibleChanged.InvokeAsync(value); + } + } + [Parameter] public EventCallback VisibleChanged { get; set; } + [Parameter] public RenderFragment? Content { get; set; } + [Parameter] public RenderFragment? Actions { get; set; } + + private bool _visible; + private bool _disposed; + + protected override void OnAfterRender(bool firstRender) + { + if (firstRender is false) return; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor.cs b/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor.cs deleted file mode 100644 index f4734ab..0000000 --- a/src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Components; - -namespace Insight.Web.Components.Dialogs; - -public partial class ActionDialog -{ - private bool _visible; - - [Parameter] - public bool Visible - { - get => _visible; - set - { - if (_visible == value) return; - _visible = value; - - VisibleChanged.InvokeAsync(value); - } - } - - [Parameter] - public EventCallback VisibleChanged { get; set; } - - [Parameter] - public RenderFragment? Content { get; set; } - - [Parameter] - public RenderFragment? Actions { get; set; } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor b/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor index 5aeab56..b974ab4 100644 --- a/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor +++ b/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor @@ -11,11 +11,6 @@ - @* - # - contacts - *@ - @foreach (var user in ChatService.Users.Keys.Where(p => p.Uid != SessionHandler.State.Uid).OrderByDescending(p => p.Online)) diff --git a/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor.cs b/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor.cs index b05c974..c336369 100644 --- a/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor.cs +++ b/src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor.cs @@ -8,14 +8,14 @@ using Microsoft.AspNetCore.SignalR.Client; using Microsoft.JSInterop; using MongoDB.Driver; using MudBlazor; -using Vaitr.MemoryBus; +using Vaitr.Bus; using static Insight.Web.Constants.Events.Chat; namespace Insight.Web.Components.Dialogs; -public partial class ChatDialog +public partial class ChatDialog : IDisposable { - [Inject] private IMemoryBus Bus { get; init; } = default!; + [Inject] private Bus Bus { get; init; } = default!; [Inject] private ChatService ChatService { get; init; } = default!; [Inject] private SessionPool SessionCache { get; init; } = default!; [Inject] private SessionHandler SessionHandler { get; init; } = default!; @@ -45,10 +45,10 @@ public partial class ChatDialog private enum Content { Contacts, Chat } private Content _content = Content.Contacts; - private ChatUser? _currentUser; private ChatSession? _currentSession; private string? _currentMessage; + private bool _disposed; private readonly List _subscriptions = new(); @@ -194,4 +194,25 @@ public partial class ChatDialog await InvokeAsync(StateHasChanged); } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Layouts/LoginLayout.razor b/src/Web/Insight.Web/Components/Layouts/LoginLayout.razor index 4f71777..35f1c0b 100644 --- a/src/Web/Insight.Web/Components/Layouts/LoginLayout.razor +++ b/src/Web/Insight.Web/Components/Layouts/LoginLayout.razor @@ -1,8 +1,34 @@ @inherits LayoutComponentBase +@implements IDisposable + - @Body - \ No newline at end of file + + +@code{ + private bool _disposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Layouts/MainLayout.razor b/src/Web/Insight.Web/Components/Layouts/MainLayout.razor index fa4a84f..ea2781f 100644 --- a/src/Web/Insight.Web/Components/Layouts/MainLayout.razor +++ b/src/Web/Insight.Web/Components/Layouts/MainLayout.razor @@ -1,10 +1,19 @@ -@using Vaitr.MemoryBus; +@using Vaitr.Bus; +@using Blazored.LocalStorage; +@using Microsoft.AspNetCore.Identity; +@using MongoDB.Driver; +@using Insight.Infrastructure @inherits LayoutComponentBase @implements IDisposable -@inject IMemoryBus Bus +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject IServiceScopeFactory ServiceScopeFactory +@inject ILocalStorageService LocalStorageService +@inject IMongoDatabase Database +@inject Bus Bus + @@ -19,8 +28,11 @@ + @if (_darkModeSwitch is false) + { + + } - @@ -40,7 +52,10 @@ @code{ public IReadOnlyDictionary? RouteValues { get; set; } + private MudTheme _currentTheme = Themes.Default(); private DrawerProvider? _drawer; + private bool _darkMode; + private bool _darkModeSwitch; private bool _disposed; protected override void OnParametersSet() @@ -50,6 +65,11 @@ base.OnParametersSet(); } + protected override async Task OnInitializedAsync() + { + await LoadDarkModeAsync(); + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -60,22 +80,85 @@ await Bus.PublishAsync(Events.Layout.Rendered).ConfigureAwait(false); } + private async Task LoadDarkModeAsync() + { + // local storage + var storageDarkMode = await LocalStorageService.GetItemAsync("darkmode"); + if (storageDarkMode is bool darkmodeValue) _darkMode = darkmodeValue; + + // database override + var state = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + + if (state?.User.Identity is not null && state.User.Identity.IsAuthenticated) + { + using var scope = ServiceScopeFactory.CreateScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + + if (await userManager.FindByNameAsync(state.User.Identity.Name) is not InsightUser user) return; + + var userPrefs = await Database.UserPreference() + .Find(p => p.User == user.Id.ToString()) + .FirstOrDefaultAsync(); + + if (userPrefs is null) return; + + _darkMode = userPrefs.DarkMode; + + await LocalStorageService.SetItemAsync("darkmode", _darkMode); + } + } + + private async Task OnDarkModeToggleAsync() + { + // update current + _darkMode = !_darkMode; + + // update local storage + await LocalStorageService.SetItemAsync("darkmode", _darkMode); + + // update database + var state = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + + if (state?.User.Identity is not null && state.User.Identity.IsAuthenticated) + { + using var scope = ServiceScopeFactory.CreateScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + + if (await userManager.FindByNameAsync(state.User.Identity.Name) is not InsightUser user) return; + + var date = DateTime.Now; + + var userPrefs = await Database.UserPreference() + .UpdateOneAsync(p => p.User == user.Id.ToString(), Builders.Update + .SetOnInsert(p => p.User, user.Id.ToString()) + .SetOnInsert(p => p.Insert, date) + .Set(p => p.Update, date) + .Set(p => p.DarkMode, _darkMode), new UpdateOptions + { + IsUpsert = true + }); + } + + await InvokeAsync(StateHasChanged); + } + public void Dispose() { - Dispose(disposing: true); + Dispose(true); GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { - // auto disposed when starved circuit timeouts (disconnects) - if (_disposed is false) + if (_disposed is false) return; + if (disposing is false) return; + + try { - if (disposing) - { - - } + } + finally + { _disposed = true; } } diff --git a/src/Web/Insight.Web/Components/Navbars/Account.razor b/src/Web/Insight.Web/Components/Navbars/Account.razor index 088bc59..92fca5e 100644 --- a/src/Web/Insight.Web/Components/Navbars/Account.razor +++ b/src/Web/Insight.Web/Components/Navbars/Account.razor @@ -1,7 +1,36 @@ @inherits ComponentBase +@implements IDisposable Account -Profile \ No newline at end of file +Profile + +@code{ + [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } + + private string _title = "Account"; + private bool _disposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Account.razor.cs b/src/Web/Insight.Web/Components/Navbars/Account.razor.cs deleted file mode 100644 index 0a34573..0000000 --- a/src/Web/Insight.Web/Components/Navbars/Account.razor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; - -namespace Insight.Web.Components.Navbars; - -public partial class Account -{ - [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } - - [Inject] private NavigationManager NavigationManager { get; init; } = default!; - [Inject] private AuthenticationStateProvider AuthenticationStateProvider { get; init; } = default!; - - private string Uri { get; set; } = string.Empty; - private string Title { get; set; } = "Account"; - - protected override async Task OnInitializedAsync() - { - var cp = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; - //Title = $"Account: {cp?.Identity?.Name}"; - - Uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); - } - - protected override void OnAfterRender(bool firstRender) - { - Uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Customer.razor b/src/Web/Insight.Web/Components/Navbars/Customer.razor index 5e1ece7..37a6525 100644 --- a/src/Web/Insight.Web/Components/Navbars/Customer.razor +++ b/src/Web/Insight.Web/Components/Navbars/Customer.razor @@ -1,13 +1,70 @@ -@inherits ComponentBase +@using MongoDB.Driver +@using Vaitr.Bus -@* - @Title -*@ +@inherits ComponentBase +@implements IDisposable - - @Title +@inject IMongoDatabase Database +@inject Bus Bus + + + @_title
- Hosts -
\ No newline at end of file + Hosts +
+ +@code{ + [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } + + private string? _id; + private string _title = "Customer"; + private IDisposable? _token; + private bool _disposed; + + protected override void OnInitialized() + { + _token = Bus.Subscribe(OnRefresh, p => p == Events.Layout.Rendered); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (RouteValues is not null && _id is null) + { + if (RouteValues.TryGetValue("customerId", out object? rawId) == false) return; + _id = rawId?.ToString(); + + var entity = await Database.Customer() + .Find(p => p.Id == _id) + .FirstOrDefaultAsync(default); + + _title = $"{entity?.Name}"; + + await InvokeAsync(() => StateHasChanged()); + } + } + + public void OnRefresh(string? message) => InvokeAsync(StateHasChanged); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) return; + if (disposing is false) return; + + try + { + _token?.Dispose(); + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs b/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs deleted file mode 100644 index 23cf3ec..0000000 --- a/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Insight.Infrastructure; -using Insight.Web.Constants; -using Microsoft.AspNetCore.Components; -using MongoDB.Driver; -using Vaitr.MemoryBus; - -namespace Insight.Web.Components.Navbars; - -public partial class Customer -{ - [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } - - [Inject] private IMongoDatabase Database { get; set; } = default!; - [Inject] private IMemoryBus Bus { get; init; } = default!; - - private string Title { get; set; } = "Customer"; - private string? Id { get; set; } - - private IDisposable? Token { get; set; } - public bool Disposed { get; set; } = false; - - protected override void OnInitialized() - { - Token = Bus.Subscribe(OnRefresh, p => p == Events.Layout.Rendered); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (RouteValues is not null && Id is null) - { - if (RouteValues.TryGetValue("customerId", out object? rawId) == false) return; - Id = rawId?.ToString(); - - var entity = await Database.Customer() - .Find(p => p.Id == Id) - .FirstOrDefaultAsync(default); - - Title = $"{entity?.Name}"; - - await InvokeAsync(() => StateHasChanged()); - } - } - - public void OnRefresh(string? message) - { - InvokeAsync(() => StateHasChanged()); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (Disposed) return; - if (disposing is false) return; - - Token?.Dispose(); - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Host.razor b/src/Web/Insight.Web/Components/Navbars/Host.razor index 5c394d7..bdf3950 100644 --- a/src/Web/Insight.Web/Components/Navbars/Host.razor +++ b/src/Web/Insight.Web/Components/Navbars/Host.razor @@ -1,60 +1,127 @@ -@inherits ComponentBase +@using MongoDB.Driver +@using Vaitr.Bus -@*@if (CustomerEntity is not null) +@inherits ComponentBase +@implements IDisposable + +@inject IMongoDatabase Database +@inject Bus Bus + +@*@if (_customer is not null) { - @CustomerEntity.Name + @_customer.Name }*@ -@if (HostEntity is not null) +@if (_host is not null) { - - @HostEntity.Name + + @_host.Name }
- Console + Console
- Os + Os - Installed - Pending + Installed + Pending - Sessions - Software - Services - Printers - Volumes - Users - Groups - Storage Pools - Virtual Maschines + Sessions + Software + Services + Printers + Volumes + Users + Groups + Storage Pools + Virtual Maschines - Interfaces - Addresses - Nameservers - Gateways - Routes + Interfaces + Addresses + Nameservers + Gateways + Routes - Mainboard - Processors - Memory - Drives - Videocards + Mainboard + Processors + Memory + Drives + Videocards
- Logs -
\ No newline at end of file + Logs +
+ +@code{ + [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } + + private string? _id; + private CustomerEntity? _customer; + private HostEntity? _host; + private IDisposable? _token; + private bool _disposed; + + protected override void OnInitialized() + { + _token = Bus?.Subscribe(OnRefresh, p => p == Events.Layout.Rendered); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (RouteValues is not null && _id is null) + { + if (RouteValues.TryGetValue("hostId", out object? rawId) == false) return; + _id = rawId?.ToString(); + + _host = await Database.Host() + .Find(p => p.Id == _id) + .FirstOrDefaultAsync(default); + + if (_host is not null) + { + _customer = await Database.Customer() + .Find(p => p.Id == _host.Customer) + .FirstOrDefaultAsync(default); + } + + await InvokeAsync(() => StateHasChanged()); + } + } + + public void OnRefresh(string? message) => InvokeAsync(StateHasChanged); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) return; + if (disposing is false) return; + + try + { + _token?.Dispose(); + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Host.razor.cs b/src/Web/Insight.Web/Components/Navbars/Host.razor.cs deleted file mode 100644 index ef0b21e..0000000 --- a/src/Web/Insight.Web/Components/Navbars/Host.razor.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; -using Insight.Web.Constants; -using Microsoft.AspNetCore.Components; -using MongoDB.Driver; -using Vaitr.MemoryBus; - -namespace Insight.Web.Components.Navbars; - -public partial class Host : IDisposable -{ - [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } - - [Inject] private IMongoDatabase Database { get; init; } = default!; - [Inject] private IMemoryBus Bus { get; init; } = default!; - - private CustomerEntity? CustomerEntity { get; set; } - private HostEntity HostEntity { get; set; } - private string? Id { get; set; } - - private IDisposable? Token { get; set; } - public bool Disposed { get; set; } = false; - - protected override void OnInitialized() - { - Token = Bus?.Subscribe(OnRefresh, p => p == Events.Layout.Rendered); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (RouteValues is not null && Id is null) - { - if (RouteValues.TryGetValue("hostId", out object? rawId) == false) return; - Id = rawId?.ToString(); - - HostEntity = await Database.Host() - .Find(p => p.Id == Id) - .FirstOrDefaultAsync(default); - - if (HostEntity is not null) - { - CustomerEntity = await Database.Customer() - .Find(p => p.Id == HostEntity.Customer) - .FirstOrDefaultAsync(default); - } - - await InvokeAsync(() => StateHasChanged()); - } - } - - public void OnRefresh(string? message) - { - InvokeAsync(() => StateHasChanged()); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (Disposed) return; - if (disposing is false) return; - - Token?.Dispose(); - } -} diff --git a/src/Web/Insight.Web/Components/Navbars/Main.razor b/src/Web/Insight.Web/Components/Navbars/Main.razor index 3c61848..7281e33 100644 --- a/src/Web/Insight.Web/Components/Navbars/Main.razor +++ b/src/Web/Insight.Web/Components/Navbars/Main.razor @@ -1,6 +1,14 @@ -@inherits ComponentBase +@using MongoDB.Driver +@using Vaitr.Bus - +@inherits ComponentBase +@implements IDisposable + +@inject NavigationManager NavigationManager +@inject IMongoDatabase Database +@inject Bus Bus + + @* Dashboard *@ @@ -12,7 +20,7 @@ *@ - + Os @@ -84,10 +92,7 @@ - - - Overview - + Accounts @@ -97,16 +102,58 @@ Hosts - @* + Host Groups - *@ + Agents + + Scheduler + -@* - - Chat - -*@ \ No newline at end of file +@code{ + private bool ExpandMonitoring => _uri.StartsWith(Navigation.Monitoring.Index); + private bool ExpandManagement => _uri.StartsWith(Navigation.Management.Index); + private bool ExpandInventory => _uri.StartsWith(Navigation.Inventory.Index); + private bool ExpandInventorySystem => _uri.StartsWith(Navigation.Inventory.Systems.Index); + private bool ExpandInventoryNetwork => _uri.StartsWith(Navigation.Inventory.Network.Index); + private bool ExpandInventoryHardware => _uri.StartsWith(Navigation.Inventory.Hardware.Index); + private string _uri = string.Empty; + private IDisposable? _token; + private bool _disposed; + + protected override void OnInitialized() + { + _token = Bus?.Subscribe(OnRefresh, p => p == Events.Layout.Rendered); + OnRefresh(); + } + + public void OnRefresh(string? message = null) + { + _uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + InvokeAsync(() => StateHasChanged()); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + _token?.Dispose(); + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Main.razor.cs b/src/Web/Insight.Web/Components/Navbars/Main.razor.cs deleted file mode 100644 index 74374ff..0000000 --- a/src/Web/Insight.Web/Components/Navbars/Main.razor.cs +++ /dev/null @@ -1,136 +0,0 @@ -using Insight.Web.Constants; -using Microsoft.AspNetCore.Components; -using Vaitr.MemoryBus; - -namespace Insight.Web.Components.Navbars; - -public partial class Main : IDisposable -{ - [Inject] private IMemoryBus Bus { get; init; } = default!; - [Inject] private NavigationManager NavigationManager { get; init; } = default!; - - private string Uri { get; set; } = string.Empty; - private IDisposable? Token { get; set; } - public bool Disposed { get; set; } = false; - - protected override void OnInitialized() - { - Token = Bus?.Subscribe(OnRefresh, p => p == Events.Layout.Rendered); - OnRefresh(); - } - - public void OnRefresh(string? message = null) - { - Uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); - InvokeAsync(() => StateHasChanged()); - } - - private bool ExpandMonitoringGroup - { - get - { - if (Uri.StartsWith(Navigation.Monitoring.Index)) return true; - return false; - } - } - - private bool ExpandManagementGroup - { - get - { - if (Uri.StartsWith(Navigation.Management.Overview.Index)) return true; - if (Uri.StartsWith(Navigation.Management.Accounts.Index)) return true; - if (Uri.StartsWith(Navigation.Management.Customers.Index)) return true; - if (Uri.StartsWith(Navigation.Management.Hosts.Index)) return true; - if (Uri.StartsWith(Navigation.Management.HostGroups.Index)) return true; - if (Uri.StartsWith(Navigation.Management.Agents.Index)) return true; - return false; - } - } - - private bool ExpandInventoryGroup - { - get - { - if (ExpandInventorySystem) return true; - if (ExpandInventoryNetwork) return true; - if (ExpandInventoryHardware) return true; - return false; - } - } - - private bool ExpandInventorySystem - { - get - { - if (Uri.StartsWith(Navigation.Inventory.Systems.Os.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Os.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Os.Guests)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Updates.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Updates.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Software.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Software.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Services.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Services.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Sessions.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Printers.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Volumes.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Users.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Users.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Groups.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.Groups.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.StoragePools.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Systems.VirtualMaschines.Index)) return true; - return false; - } - } - - private bool ExpandInventoryNetwork - { - get - { - if (Uri.StartsWith(Navigation.Inventory.Network.Interfaces.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Addresses.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Addresses.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Nameservers.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Nameservers.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Gateways.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Gateways.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Routes.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Network.Routes.Hosts)) return true; - return false; - } - } - - private bool ExpandInventoryHardware - { - get - { - if (Uri.StartsWith(Navigation.Inventory.Hardware.Mainboards.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Mainboards.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Processors.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Processors.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Memory.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Memory.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Drives.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Drives.Hosts)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Videocards.Index)) return true; - if (Uri.StartsWith(Navigation.Inventory.Hardware.Videocards.Hosts)) return true; - return false; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (Disposed) return; - if (disposing is false) return; - - Token?.Dispose(); - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/NavSwitch.razor b/src/Web/Insight.Web/Components/Navbars/NavSwitch.razor deleted file mode 100644 index 9eb23b4..0000000 --- a/src/Web/Insight.Web/Components/Navbars/NavSwitch.razor +++ /dev/null @@ -1,29 +0,0 @@ -@using System.Reflection; -@inherits ComponentBase - -
- - - @if (_content == Content.Account) - { - - } - else if (_content == Content.Host) - { - - } - else if (_content == Content.Customer) - { - - } - else - { -
- } - - -
- - - @(Assembly.GetEntryAssembly()?.GetName().Version) - \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/NavSwitch.razor.cs b/src/Web/Insight.Web/Components/Navbars/NavSwitch.razor.cs deleted file mode 100644 index bbcc4ab..0000000 --- a/src/Web/Insight.Web/Components/Navbars/NavSwitch.razor.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Insight.Web.Constants; -using Microsoft.AspNetCore.Components; -using Vaitr.MemoryBus; - -namespace Insight.Web.Components.Navbars; - -public partial class NavSwitch : IDisposable -{ - [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } - - [Inject] private NavigationManager NavigationManager { get; init; } = default!; - [Inject] private IMemoryBus Bus { get; init; } = default!; - - public string Url { get; set; } = string.Empty; - private IDisposable? Token { get; set; } - public bool Disposed { get; set; } = false; - - private enum Content { Main, Account, Customer, Host } - private Content _content = Content.Main; - - protected override void OnInitialized() - { - Token = Bus?.SubscribeAsync(OnRefreshAsync, p => p == Events.Layout.Rendered); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await OnRefreshAsync().ConfigureAwait(false); - } - } - - public async ValueTask OnRefreshAsync(string? message = null, CancellationToken cancellationToken = default) - { - Url = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); - - if (Url is null) return; - - if (Url.StartsWith($"{Navigation.Account.Index}/")) _content = Content.Account; - else if (Url.StartsWith($"{Navigation.Management.Customers.Index}/")) _content = Content.Customer; - else if (Url.StartsWith($"{Navigation.Management.Hosts.Index}/")) _content = Content.Host; - else _content = Content.Main; - - await InvokeAsync(StateHasChanged).ConfigureAwait(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (Disposed) return; - Token?.Dispose(); - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Navbars/Scheduler.razor b/src/Web/Insight.Web/Components/Navbars/Scheduler.razor new file mode 100644 index 0000000..024e016 --- /dev/null +++ b/src/Web/Insight.Web/Components/Navbars/Scheduler.razor @@ -0,0 +1,37 @@ +@inherits ComponentBase +@implements IDisposable + +@inject NavigationManager NavigationManager + + + Scheduler + + +Jobs +Tasks +Triggers + +@code{ + private bool _disposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Providers/ChatProvider.razor b/src/Web/Insight.Web/Components/Providers/ChatProvider.razor index b88218b..c6f231a 100644 --- a/src/Web/Insight.Web/Components/Providers/ChatProvider.razor +++ b/src/Web/Insight.Web/Components/Providers/ChatProvider.razor @@ -1,12 +1,13 @@ @using System.Collections.Concurrent; -@using Vaitr.MemoryBus; +@using Vaitr.Bus; @using static Insight.Web.Constants.Events.Chat; -@inject IMemoryBus Bus -@inject SessionHandler SessionHandler - +@inherits ComponentBase @implements IDisposable +@inject Bus Bus +@inject SessionHandler SessionHandler + @@ -20,7 +21,7 @@ private readonly ConcurrentBag _subscriptions = new(); - protected override async Task OnInitializedAsync() + protected override void OnInitialized() { _subscriptions.Add(Bus.SubscribeAsync(async (x, c) => await InvokeAsync(StateHasChanged), p => p == Events.Sessions.Changed)); _subscriptions.Add(Bus.SubscribeAsync(async (x, c) => await InvokeAsync(StateHasChanged), p => p.Message.SenderId != SessionHandler.State.Uid)); @@ -28,21 +29,24 @@ public void Dispose() { - Dispose(disposing: true); + Dispose(true); GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { // auto disposed when starved circuit timeouts (disconnects) - if (_disposed is false) - { - if (disposing) - { - foreach (var sub in _subscriptions) sub.Dispose(); - _subscriptions.Clear(); - } + if (_disposed is false) return; + if (disposing is false) return; + + try + { + foreach (var sub in _subscriptions) sub.Dispose(); + _subscriptions.Clear(); + } + finally + { _disposed = true; } } diff --git a/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor b/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor index 68a7295..117178d 100644 --- a/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor +++ b/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor @@ -1,8 +1,47 @@ - +@using System.Reflection; +@using MongoDB.Driver +@using Vaitr.Bus + +@inherits ComponentBase +@implements IDisposable + +@inject NavigationManager NavigationManager +@inject IMongoDatabase Database +@inject Bus Bus + + - +
+ + + @if (_content == Content.Account) + { + + } + else if (_content == Content.Customer) + { + + } + else if (_content == Content.Host) + { + + } + else if (_content == Content.Scheduler) + { + + } + else + { +
+ } + + +
+ + @(Assembly.GetEntryAssembly()?.GetName().Version) +
@@ -11,7 +50,57 @@ @code { [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } + private string _uri = string.Empty; + private enum Content { Main, Account, Customer, Host, Scheduler } + private Content _content = Content.Main; + private IDisposable? _token; private bool _open = true; + private bool _disposed; + + protected override void OnInitialized() + { + _token = Bus?.SubscribeAsync(OnRefreshAsync, p => p == Events.Layout.Rendered); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) await OnRefreshAsync(); + } public void Toggle() => _open = !_open; + + public async ValueTask OnRefreshAsync(string? message = null, CancellationToken cancellationToken = default) + { + _uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + if (_uri is null) return; + + if (_uri.StartsWith($"{Navigation.Account.Index}/")) _content = Content.Account; + else if (_uri.StartsWith($"{Navigation.Management.Customers.Index}/")) _content = Content.Customer; + else if (_uri.StartsWith($"{Navigation.Management.Hosts.Index}/")) _content = Content.Host; + else if (_uri.StartsWith($"{Navigation.Management.Scheduler.Index}/")) _content = Content.Scheduler; + else _content = Content.Main; + + await InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + _token?.Dispose(); + } + finally + { + _disposed = true; + } + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Providers/ProfileProvider.razor b/src/Web/Insight.Web/Components/Providers/ProfileProvider.razor index 2208e1e..5cf99d6 100644 --- a/src/Web/Insight.Web/Components/Providers/ProfileProvider.razor +++ b/src/Web/Insight.Web/Components/Providers/ProfileProvider.razor @@ -1,4 +1,7 @@ -@inject NavigationManager NavigationManager +@inherits ComponentBase +@implements IDisposable + +@inject NavigationManager NavigationManager @@ -49,6 +52,7 @@ @code{ private MudMenu? _menu; + private bool _disposed; private void OnLogin() { @@ -67,4 +71,25 @@ _menu?.CloseMenu(); NavigationManager.NavigateTo(Navigation.Account.Profile, false); } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try + { + + } + finally + { + _disposed = true; + } + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Providers/SessionProvider.razor b/src/Web/Insight.Web/Components/Providers/SessionProvider.razor index 69357d8..7cdbfd5 100644 --- a/src/Web/Insight.Web/Components/Providers/SessionProvider.razor +++ b/src/Web/Insight.Web/Components/Providers/SessionProvider.razor @@ -1,14 +1,37 @@ -@using Vaitr.MemoryBus; +@using Vaitr.Bus; + +@inherits ComponentBase +@implements IDisposable @inject SessionHandler SessionHandler -@inject IMemoryBus Bus +@inject Bus Bus @code { + private bool _disposed; + protected override async Task OnAfterRenderAsync(bool firstRender) { - if (firstRender) + if (firstRender) await SessionHandler.UpdateStateAsync(default); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed is false) return; + if (disposing is false) return; + + try { - await SessionHandler.UpdateStateAsync(default); + + } + finally + { + _disposed = true; } } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Components/Providers/ThemeProvider.razor b/src/Web/Insight.Web/Components/Providers/ThemeProvider.razor deleted file mode 100644 index f3549d7..0000000 --- a/src/Web/Insight.Web/Components/Providers/ThemeProvider.razor +++ /dev/null @@ -1,90 +0,0 @@ -@using Blazored.LocalStorage; -@using Microsoft.AspNetCore.Identity; -@using MongoDB.Driver; -@using Insight.Infrastructure - -@inject IServiceScopeFactory ServiceScopeFactory -@inject ILocalStorageService LocalStorageService -@inject IMongoDatabase Database -@inject AuthenticationStateProvider AuthenticationStateProvider - - - -@if (DisableIcon is false) -{ - -} - -@code { - [Parameter] public bool DisableIcon { get; set; } - - private MudTheme CurrentTheme { get; } = Themes.Default(); - private bool DarkMode { get; set; } - - protected override async Task OnInitializedAsync() - { - await LoadDarkModeAsync(); - } - - public async Task LoadDarkModeAsync() - { - // local storage - var storageDarkMode = await LocalStorageService.GetItemAsync("darkmode"); - if (storageDarkMode is bool darkmodeValue) DarkMode = darkmodeValue; - - // database override - var state = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - - if (state?.User.Identity is not null && state.User.Identity.IsAuthenticated) - { - using var scope = ServiceScopeFactory.CreateScope(); - var userManager = scope.ServiceProvider.GetRequiredService>(); - - if (await userManager.FindByNameAsync(state.User.Identity.Name) is not InsightUser user) return; - - var userPrefs = await Database.UserPreference() - .Find(p => p.User == user.Id.ToString()) - .FirstOrDefaultAsync(); - - if (userPrefs is null) return; - - DarkMode = userPrefs.DarkMode; - - await LocalStorageService.SetItemAsync("darkmode", DarkMode); - } - } - - private async Task OnDarkModeToggleAsync() - { - // update current - DarkMode = !DarkMode; - - // update local storage - await LocalStorageService.SetItemAsync("darkmode", DarkMode); - - // update database - var state = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - - if (state?.User.Identity is not null && state.User.Identity.IsAuthenticated) - { - using var scope = ServiceScopeFactory.CreateScope(); - var userManager = scope.ServiceProvider.GetRequiredService>(); - - if (await userManager.FindByNameAsync(state.User.Identity.Name) is not InsightUser user) return; - - var date = DateTime.Now; - - var userPrefs = await Database.UserPreference() - .UpdateOneAsync(p => p.User == user.Id.ToString(), Builders.Update - .SetOnInsert(p => p.User, user.Id.ToString()) - .SetOnInsert(p => p.Insert, date) - .Set(p => p.Update, date) - .Set(p => p.DarkMode, DarkMode), new UpdateOptions - { - IsUpsert = true - }); - } - - await InvokeAsync(StateHasChanged); - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Constants/Navigation.cs b/src/Web/Insight.Web/Constants/Navigation.cs index aadb16b..3fcdfd6 100644 --- a/src/Web/Insight.Web/Constants/Navigation.cs +++ b/src/Web/Insight.Web/Constants/Navigation.cs @@ -1,4 +1,6 @@ -namespace Insight.Web.Constants; +using static Insight.Web.Constants.Navigation.Monitoring; + +namespace Insight.Web.Constants; public static class Navigation { @@ -13,44 +15,22 @@ public static class Navigation public static class Account { public const string Index = "account"; - public const string Login = "account/login"; - public const string LoginTFA = "account/login/{key:guid}"; - public const string SignIn = "account/signin"; - public const string SignInTFA = "account/signin/2fa"; - public const string Logout = "account/logout"; - public const string Lockout = "account/lockout"; - public const string Profile = "account/profile"; - public const string ChangePassword = "account/changepassword"; + public const string Login = Index + "/login"; + public const string LoginTFA = Login + "/{key:guid}"; + public const string SignIn = Index + "/signin"; + public const string SignInTFA = SignIn + "/2fa"; + public const string Logout = Index + "/logout"; + public const string Lockout = Index + "/lockout"; + public const string Profile = Index + "/profile"; + public const string ChangePassword = Index + "/changepassword"; - public static string LoginHref(string redirect) - { - if (string.IsNullOrWhiteSpace(redirect)) - { - return Login; - } + public static string LoginHref(string redirect) => string.IsNullOrWhiteSpace(redirect) ? Login : $"{Login}?redirect={redirect}"; + public static string LoginTFAHref(Guid key) => LoginTFA.Replace("{key:guid}", key.ToString()); - return $"{Login}?redirect={redirect}"; - } + public static string SignInHref(Guid key) => $"{SignIn}?key={key}"; + public static string SignInTFAHref(Guid key) => $"{SignInTFA}?key={key}"; - public static string LoginTFAHref(Guid key) - { - return LoginTFA.Replace("{key:guid}", key.ToString()); - } - - public static string SignInHref(Guid key) - { - return $"{SignIn}?key={key}"; - } - - public static string SignInTFAHref(Guid key) - { - return $"{SignInTFA}?key={key}"; - } - - public static string ChangePasswordHref(Guid key) - { - return $"{ChangePassword}?key={key}"; - } + public static string ChangePasswordHref(Guid key) => $"{ChangePassword}?key={key}"; } public static class Monitoring @@ -59,36 +39,36 @@ public static class Navigation public static class Maintenance { - public const string Index = "monitoring/maintenance"; + public const string Index = Monitoring.Index + "/maintenance"; public static class Drives { - public const string Index = "monitoring/maintenance/drives"; + public const string Index = Monitoring.Index + "/drives"; } public static class StoragePools { - public const string Index = "monitoring/maintenance/storagepools"; + public const string Index = Monitoring.Index + "/storagepools"; } public static class Volumes { - public const string Index = "monitoring/maintenance/volumes"; + public const string Index = Monitoring.Index + "/volumes"; } public static class Guests { - public const string Index = "monitoring/maintenance/guests"; + public const string Index = Monitoring.Index + "/guests"; } public static class Snapshots { - public const string Index = "monitoring/maintenance/snapshots"; + public const string Index = Monitoring.Index + "/snapshots"; } public static class Updates { - public const string Index = "monitoring/maintenance/updates"; + public const string Index = Monitoring.Index + "/updates"; } } } @@ -97,106 +77,58 @@ public static class Navigation { public const string Index = "management"; - public static class Overview - { - public const string Index = "management/overview"; - } - public static class Accounts { - public const string Index = "management/accounts"; - public const string Details = "management/accounts/{accountId}"; - - public static string DetailsHref(string? accountId) - { - return Details.Replace("{accountId}", accountId); - } + public const string Index = Management.Index + "/accounts"; + public const string Details = Index + "/{accountId}"; + + public static string DetailsHref(string? accountId) => Details.Replace("{accountId}", accountId); } public static class Customers { - public const string Index = "management/customers"; - public const string Details = "management/customers/{customerId}"; - public const string Hosts = "management/customers/{customerId}/hosts"; - public const string HostsAssign = "management/customers/{customerId}/hosts/assign"; + public const string Index = Management.Index + "/customers"; + public const string Details = Index + "/{customerId}"; + public const string Hosts = Details + "/hosts"; + public const string HostsAssign = Hosts + "/assign"; - public static string DetailsHref(string? customerId) - { - return Details.Replace("{customerId}", customerId); - } - - public static string HostsHref(string? customerId) - { - return Hosts.Replace("{customerId}", customerId); - } - - public static string HostsAssignHref(string? customerId) - { - return HostsAssign.Replace("{customerId}", customerId); - } + public static string DetailsHref(string? customerId) => Details.Replace("{customerId}", customerId); + public static string HostsHref(string? customerId) => Hosts.Replace("{customerId}", customerId); + public static string HostsAssignHref(string? customerId) => HostsAssign.Replace("{customerId}", customerId); } public static class Agents { - public const string Index = "management/agents"; - public const string Details = "management/agents/{agentId}"; - public const string Logs = "management/agents/{agentId}/logs"; - public const string HostAssign = "management/agents/{agentId}/assign"; + public const string Index = Management.Index + "/agents"; + public const string Details = Index + "/{agentId}"; + public const string Logs = Details + "/logs"; + public const string HostAssign = Details + "/assign"; - public static string DetailsHref(string? agentId) - { - return Details.Replace("{agentId}", agentId); - } - - public static string LogsHref(string? agentId) - { - return Logs.Replace("{agentId}", agentId); - } - - public static string HostAssingHref(string? agentId) - { - return HostAssign.Replace("{agentId}", agentId); - } + public static string DetailsHref(string? agentId) => Details.Replace("{agentId}", agentId); + public static string LogsHref(string? agentId) => Logs.Replace("{agentId}", agentId); + public static string HostAssingHref(string? agentId) => HostAssign.Replace("{agentId}", agentId); } public static class Hosts { - public const string Index = "management/hosts"; - public const string Details = "management/hosts/{hostId}"; - public const string Logs = "management/hosts/{hostId}/logs"; - public const string CustomerAssign = "management/hosts/{hostId}/customer/assign"; - public const string AgentAssign = "management/hosts/{hostId}/agent/assign"; + public const string Index = Management.Index + "/hosts"; + public const string Details = Index + "/{hostId}"; + public const string Logs = Details + "/logs"; + public const string CustomerAssign = Details + "/customer/assign"; + public const string AgentAssign = Details + "/agent/assign"; - public static string DetailsHref(string? hostId) - { - return Details.Replace("{hostId}", hostId); - } - - public static string LogsHref(string? hostId) - { - return Logs.Replace("{hostId}", hostId); - } - - public static string CustomerAssingHref(string? hostId) - { - return CustomerAssign.Replace("{hostId}", hostId); - } - - public static string AgentAssingHref(string? hostId) - { - return AgentAssign.Replace("{hostId}", hostId); - } + public static string DetailsHref(string? hostId) => Details.Replace("{hostId}", hostId); + public static string LogsHref(string? hostId) => Logs.Replace("{hostId}", hostId); + public static string CustomerAssingHref(string? hostId) => CustomerAssign.Replace("{hostId}", hostId); + public static string AgentAssingHref(string? hostId) => AgentAssign.Replace("{hostId}", hostId); public static class Actions { public static class Console { - public const string Index = "management/hosts/{hostId}/console"; + public const string Index = Hosts.Details + "/console"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } } @@ -204,190 +136,121 @@ public static class Navigation { public static class Os { - public const string Details = "management/hosts/{hostId}/os"; + public const string Details = Hosts.Details + "/os"; - public static string DetailsHref(string? hostId) - { - return Details.Replace("{hostId}", hostId); - } + public static string DetailsHref(string? hostId) => Details.Replace("{hostId}", hostId); } public static class Updates { public static class Installed { - public const string Index = "management/hosts/{hostId}/updates/installed"; + public const string Index = Hosts.Details + "/updates/installed"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Pending { - public const string Index = "management/hosts/{hostId}/updates/pending"; + public const string Index = Hosts.Details + "/updates/pending"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } } public static class Sessions { - public const string Index = "management/hosts/{hostId}/sessions"; + public const string Index = Hosts.Details + "/sessions"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Software { - public const string Index = "management/hosts/{hostId}/software"; + public const string Index = Hosts.Details + "/software"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Services { - public const string Index = "management/hosts/{hostId}/services"; + public const string Index = Hosts.Details + "/services"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Printers { - public const string Index = "management/hosts/{hostId}/printers"; + public const string Index = Hosts.Details + "/printers"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Volumes { - public const string Index = "management/hosts/{hostId}/volumes"; - public const string Details = "management/hosts/{hostId}/volumes/{volumeId}"; + public const string Index = Hosts.Details + "/volumes"; + public const string Details = Index + "/{volumeId}"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } - - public static string DetailsHref(string? hostId, string? volumeId) - { - return Details.Replace("{hostId}", hostId).Replace("{volumeId}", volumeId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); + public static string DetailsHref(string? hostId, string? volumeId) => Details.Replace("{hostId}", hostId).Replace("{volumeId}", volumeId); } public static class Users { - public const string Index = "management/hosts/{hostId}/users"; - public const string Details = "management/hosts/{hostId}/users/{userId}"; + public const string Index = Hosts.Details + "/users"; + public const string Details = Index + "/{userId}"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } - - public static string DetailsHref(string? hostId, string? userId) - { - return Details.Replace("{hostId}", hostId).Replace("{userId}", userId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); + public static string DetailsHref(string? hostId, string? userId) => Details.Replace("{hostId}", hostId).Replace("{userId}", userId); } public static class Groups { - public const string Index = "management/hosts/{hostId}/groups"; + public const string Index = Hosts.Details + "/groups"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class StoragePools { - public const string Index = "management/hosts/{hostId}/storagepools"; - public const string Details = "management/hosts/{hostId}/storagepools/{storagePoolId}"; + public const string Index = Hosts.Details + "/storagepools"; + public const string Details = Index + "/{storagePoolId}"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } - - public static string DetailsHref(string? hostId, string? storagePoolId) - { - return Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); + public static string DetailsHref(string? hostId, string? storagePoolId) => Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId); public static class VirtualDisks { - public const string Index = "management/hosts/{hostId}/storagepools/{storagePoolId}/virtualdisks"; - public const string Details = "management/hosts/{hostId}/storagepools/{storagePoolId}/virtualdisks/{virtualDiskId}"; + public const string Index = StoragePools.Details + "/virtualdisks"; + public const string Details = Index + "/{virtualDiskId}"; - public static string IndexHref(string? hostId, string? storagePoolId) - { - return Index.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId); - } - - public static string DetailsHref(string? hostId, string? storagePoolId, string? virtualDiskId) - { - return Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId).Replace("{virtualDiskId}", virtualDiskId); - } + public static string IndexHref(string? hostId, string? storagePoolId) => Index.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId); + public static string DetailsHref(string? hostId, string? storagePoolId, string? virtualDiskId) => Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId).Replace("{virtualDiskId}", virtualDiskId); } public static class PhysicalDisks { - public const string Index = "management/hosts/{hostId}/storagepools/{storagePoolId}/physicaldisks"; - public const string Details = "management/hosts/{hostId}/storagepools/{storagePoolId}/physicaldisks/{physicalDiskId}"; + public const string Index = StoragePools.Details + "/physicaldisks"; + public const string Details = Index + "/{physicalDiskId}"; - public static string IndexHref(string? hostId, string? storagePoolId) - { - return Index.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId); - } - - public static string DetailsHref(string? hostId, string? storagePoolId, string? physicalDiskId) - { - return Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId).Replace("{physicalDiskId}", physicalDiskId); - } + public static string IndexHref(string? hostId, string? storagePoolId) => Index.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId); + public static string DetailsHref(string? hostId, string? storagePoolId, string? physicalDiskId) => Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId).Replace("{physicalDiskId}", physicalDiskId); } } public static class VirtualMaschines { - public const string Index = "management/hosts/{hostId}/virtualmaschines"; - public const string Details = "management/hosts/{hostId}/virtualmaschines/{virtualMaschineId}"; + public const string Index = Hosts.Details + "/virtualmaschines"; + public const string Details = Index + "/{virtualMaschineId}"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } - - public static string DetailsHref(string? hostId, string? virtualMaschineId) - { - return Details.Replace("{hostId}", hostId).Replace("{virtualMaschineId}", virtualMaschineId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); + public static string DetailsHref(string? hostId, string? virtualMaschineId) => Details.Replace("{hostId}", hostId).Replace("{virtualMaschineId}", virtualMaschineId); public static class Snapshots { - public const string Details = "management/hosts/{hostId}/virtualmaschines/{virtualMaschineId}/snapshots/{snapshotId}"; + public const string Details = VirtualMaschines.Details + "/snapshots/{snapshotId}"; - public static string DetailsHref(string? hostId, string? virtualMaschineId, string? snapshotId) - { - return Details.Replace("{hostId}", hostId).Replace("{virtualMaschineId}", virtualMaschineId).Replace("{snapshotId}", snapshotId); - } + public static string DetailsHref(string? hostId, string? virtualMaschineId, string? snapshotId) => Details.Replace("{hostId}", hostId).Replace("{virtualMaschineId}", virtualMaschineId).Replace("{snapshotId}", snapshotId); } } } @@ -396,63 +259,44 @@ public static class Navigation { public static class Interfaces { - public const string Index = "management/hosts/{hostId}/interfaces"; - public const string Details = "management/hosts/{hostId}/interfaces/{interfaceId}"; + public const string Index = Hosts.Details + "/interfaces"; + public const string Details = Index + "/{interfaceId}"; - public const string Addresses = "management/hosts/{hostId}/interfaces/{interfaceId}/addresses"; - public const string Nameservers = "management/hosts/{hostId}/interfaces/{interfaceId}/nameservers"; - public const string Gateways = "management/hosts/{hostId}/interfaces/{interfaceId}/gateways"; - public const string Routes = "management/hosts/{hostId}/interfaces/{interfaceId}/routes"; + public const string Addresses = Details + "/addresses"; + public const string Nameservers = Details + "/nameservers"; + public const string Gateways = Details + "/gateways"; + public const string Routes = Details + "/routes"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } - - public static string DetailsHref(string? hostId, string? interfaceId) - { - return Details.Replace("{hostId}", hostId).Replace("{interfaceId}", interfaceId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); + public static string DetailsHref(string? hostId, string? interfaceId) => Details.Replace("{hostId}", hostId).Replace("{interfaceId}", interfaceId); } public static class Addresses { - public const string Index = "management/hosts/{hostId}/addresses"; + public const string Index = Hosts.Details + "/addresses"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Gateways { - public const string Index = "management/hosts/{hostId}/gateways"; + public const string Index = Details + "/gateways"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Nameservers { - public const string Index = "management/hosts/{hostId}/nameservers"; + public const string Index = Hosts.Details + "/nameservers"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Routes { - public const string Index = "management/hosts/{hostId}/routes"; + public const string Index = Hosts.Details + "/routes"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } } @@ -460,288 +304,264 @@ public static class Navigation { public static class Mainboard { - public const string Details = "management/hosts/{hostId}/mainboard"; + public const string Details = Hosts.Details + "/mainboard"; - public static string DetailsHref(string? hostId) - { - return Details.Replace("{hostId}", hostId); - } + public static string DetailsHref(string? hostId) => Details.Replace("{hostId}", hostId); } public static class Processors { - public const string Details = "management/hosts/{hostId}/processors"; + public const string Details = Hosts.Details + "/processors"; - public static string DetailsHref(string? hostId) - { - return Details.Replace("{hostId}", hostId); - } + public static string DetailsHref(string? hostId) => Details.Replace("{hostId}", hostId); } public static class Memory { - public const string Index = "management/hosts/{hostId}/memory"; + public const string Index = Hosts.Details + "/memory"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Drives { - public const string Index = "management/hosts/{hostId}/drives"; + public const string Index = Hosts.Details + "/drives"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } public static class Videocards { - public const string Index = "management/hosts/{hostId}/videocards"; + public const string Index = Hosts.Details + "/videocards"; - public static string IndexHref(string? hostId) - { - return Index.Replace("{hostId}", hostId); - } + public static string IndexHref(string? hostId) => Index.Replace("{hostId}", hostId); } } } public static class HostGroups { - public const string Index = "management/hostgroups"; - public const string Details = "management/hostgroups/{groupId}"; + public const string Index = Management.Index + "/hostgroups"; + public const string Details = Index + "/{groupId}"; + public const string Hosts = Details + "/hosts"; + public const string HostsAssign = Details + "/assign"; - public static string DetailsHref(string? groupId) + public static string DetailsHref(string? groupId) => Details.Replace("{groupId}", groupId); + public static string HostsHref(string? groupId) => Hosts.Replace("{groupId}", groupId); + public static string HostsAssignHref(string? groupId) => HostsAssign.Replace("{groupId}", groupId); + } + + public static class Scheduler + { + public const string Index = Management.Index + "/scheduler"; + + public static class Jobs { - return Details.Replace("{groupId}", groupId); + public const string Index = Scheduler.Index + "/jobs"; + public const string Details = Index + "/{jobId}"; + public const string Tasks = Details + "/tasks"; + public const string TasksAssign = Tasks + "/assign"; + public const string Triggers = Details + "/triggers"; + public const string TriggersAssign = Triggers + "/assign"; + + public static string DetailsHref(string? jobId) => Details.Replace("{jobId}", jobId); + public static string TasksHref(string? jobId) => Tasks.Replace("{jobId}", jobId); + public static string TasksAssignHref(string? jobId) => TasksAssign.Replace("{jobId}", jobId); + public static string TriggersHref(string? jobId) => Triggers.Replace("{jobId}", jobId); + public static string TriggersAssignHref(string? jobId) => TriggersAssign.Replace("{jobId}", jobId); + } + + public static class Tasks + { + public const string Index = Scheduler.Index + "/tasks"; + public const string Details = Index + "/{taskId}"; + public const string Jobs = Details + "/jobs"; + + public static string DetailsHref(string? taskId) => Details.Replace("{taskId}", taskId); + public static string JobsHref(string? taskId) => Jobs.Replace("{taskId}", taskId); + } + + public static class Triggers + { + public const string Index = Scheduler.Index + "/triggers"; + public const string Details = Index + "/{triggerId}"; + public const string Jobs = Details + "/jobs"; + + public static string DetailsHref(string? triggerId) => Details.Replace("{triggerId}", triggerId); + public static string JobsHref(string? triggerId) => Jobs.Replace("{triggerId}", triggerId); } } } public static class Inventory { + public const string Index = "inventory"; + public static class Systems { + public const string Index = Inventory.Index + "/system"; + public static class Os { - public const string Index = "inventory/os"; - public const string Hosts = "inventory/os/{osName}/hosts"; - public const string Guests = "inventory/os/{osName}/guests"; + public const string Index = Systems.Index + "/os"; + public const string Hosts = Index + "/{osName}/hosts"; + public const string Guests = Index + "/{osName}/guests"; - public static string HostsHref(string? osName) - { - return Hosts.Replace("{osName}", osName); - } - - public static string GuestsHref(string? osName) - { - return Guests.Replace("{osName}", osName); - } + public static string HostsHref(string? osName) => Hosts.Replace("{osName}", osName); + public static string GuestsHref(string? osName) => Guests.Replace("{osName}", osName); } public static class Updates { - public const string Index = "inventory/updates"; - public const string Hosts = "inventory/updates/{updateName}/hosts"; + public const string Index = Systems.Index + "/updates"; + public const string Hosts = Index + "/{updateName}/hosts"; - public static string HostsHref(string? updateName) - { - return Hosts.Replace("{updateName}", updateName); - } + public static string HostsHref(string? updateName) => Hosts.Replace("{updateName}", updateName); } public static class Software { - public const string Index = "inventory/software"; - public const string Hosts = "inventory/software/{softwareName}/hosts"; + public const string Index = Systems.Index + "/software"; + public const string Hosts = Index + "/{softwareName}/hosts"; - public static string HostsHref(string? software) - { - return Hosts.Replace("{softwareName}", software); - } + public static string HostsHref(string? software) => Hosts.Replace("{softwareName}", software); } public static class Services { - public const string Index = "inventory/services"; - public const string Hosts = "inventory/services/{serviceName}/hosts"; + public const string Index = Systems.Index + "/services"; + public const string Hosts = Index + "/{serviceName}/hosts"; - public static string HostsHref(string? serviceName) - { - return Hosts.Replace("{serviceName}", serviceName); - } + public static string HostsHref(string? serviceName) => Hosts.Replace("{serviceName}", serviceName); } public static class Sessions { - public const string Index = "inventory/sessions"; + public const string Index = Systems.Index + "/sessions"; } public static class Printers { - public const string Index = "inventory/printers"; + public const string Index = Systems.Index + "/printers"; } public static class Volumes { - public const string Index = "inventory/volumes"; + public const string Index = Systems.Index + "/volumes"; } public static class Users { - public const string Index = "inventory/users"; - public const string Hosts = "inventory/users/{userName}/hosts"; + public const string Index = Systems.Index + "/users"; + public const string Hosts = Index + "/{userName}/hosts"; - public static string HostsHref(string? userName) - { - return Hosts.Replace("{userName}", userName); - } + public static string HostsHref(string? userName) => Hosts.Replace("{userName}", userName); } public static class Groups { - public const string Index = "inventory/groups"; - public const string Hosts = "inventory/groups/{groupName}/hosts"; + public const string Index = Systems.Index + "/groups"; + public const string Hosts = Index + "/{groupName}/hosts"; - public static string HostsHref(string? groupName) - { - return Hosts.Replace("{groupName}", groupName); - } + public static string HostsHref(string? groupName) => Hosts.Replace("{groupName}", groupName); } public static class StoragePools { - public const string Index = "inventory/storagepools"; + public const string Index = Systems.Index + "/storagepools"; } public static class VirtualMaschines { - public const string Index = "inventory/virtualmaschines"; + public const string Index = Systems.Index + "/virtualmaschines"; } } public static class Network { + public const string Index = Inventory.Index + "/network"; + public static class Interfaces { - public const string Index = "inventory/interfaces"; + public const string Index = Network.Index + "/interfaces"; } public static class Addresses { - public const string Index = "inventory/addresses"; - public const string Hosts = "inventory/addresses/{address}/hosts"; + public const string Index = Network.Index + "/addresses"; + public const string Hosts = Index + "/{address}/hosts"; - public static string HostsHref(string? address) - { - return Hosts.Replace("{address}", address); - } + public static string HostsHref(string? address) => Hosts.Replace("{address}", address); } public static class Nameservers { - public const string Index = "inventory/nameservers"; - public const string Hosts = "inventory/nameservers/{nameserverAddress}/hosts"; + public const string Index = Network.Index + "/nameservers"; + public const string Hosts = Index + "/{nameserverAddress}/hosts"; - public static string HostsHref(string? nameserverAddress) - { - return Hosts.Replace("{nameserverAddress}", nameserverAddress); - } + public static string HostsHref(string? nameserverAddress) => Hosts.Replace("{nameserverAddress}", nameserverAddress); } public static class Gateways { - public const string Index = "inventory/gateways"; - public const string Hosts = "inventory/gateways/{gatewayAddress}/hosts"; + public const string Index = Network.Index + "/gateways"; + public const string Hosts = Index + "/{gatewayAddress}/hosts"; - public static string HostsHref(string? gatewayAddress) - { - return Hosts.Replace("{gatewayAddress}", gatewayAddress); - } + public static string HostsHref(string? gatewayAddress) => Hosts.Replace("{gatewayAddress}", gatewayAddress); } public static class Routes { - public const string Index = "inventory/routes"; - public const string Hosts = "inventory/routes/{routeAddress}/hosts"; + public const string Index = Network.Index + "/routes"; + public const string Hosts = Index + "/{routeAddress}/hosts"; - public static string HostsHref(string? routeAddress) - { - return Hosts.Replace("{routeAddress}", routeAddress); - } + public static string HostsHref(string? routeAddress) => Hosts.Replace("{routeAddress}", routeAddress); } } public static class Hardware { + public const string Index = Inventory.Index + "/hardware"; + public static class Mainboards { - public const string Index = "inventory/mainboards"; - public const string Hosts = "inventory/mainboards/{mainboardName}/hosts"; + public const string Index = Hardware.Index + "/mainboards"; + public const string Hosts = Index + "/{mainboardName}/hosts"; - public static string HostsHref(string? mainboardName) - { - return Hosts.Replace("{mainboardName}", mainboardName); - } + public static string HostsHref(string? mainboardName) => Hosts.Replace("{mainboardName}", mainboardName); } public static class Processors { - public const string Index = "inventory/processors"; - public const string Hosts = "inventory/processors/{processorName}/hosts"; + public const string Index = Hardware.Index + "/processors"; + public const string Hosts = Index + "/{processorName}/hosts"; - public static string HostsHref(string? processorName) - { - return Hosts.Replace("{processorName}", processorName); - } + public static string HostsHref(string? processorName) => Hosts.Replace("{processorName}", processorName); } public static class Memory { - public const string Index = "inventory/memory"; - public const string Hosts = "inventory/memory/{memoryName}/hosts"; + public const string Index = Hardware.Index + "/memory"; + public const string Hosts = Index + "/{memoryName}/hosts"; - public static string HostsHref(string? memoryName) - { - return Hosts.Replace("{memoryName}", memoryName); - } + public static string HostsHref(string? memoryName) => Hosts.Replace("{memoryName}", memoryName); } public static class Drives { - public const string Index = "inventory/drives"; - public const string Hosts = "inventory/drives/{driveName}/hosts"; + public const string Index = Hardware.Index + "/drives"; + public const string Hosts = Index + "/{driveName}/hosts"; - public static string HostsHref(string? driveName) - { - return Hosts.Replace("{driveName}", driveName); - } + public static string HostsHref(string? driveName) => Hosts.Replace("{driveName}", driveName); } public static class Videocards { - public const string Index = "inventory/videocards"; - public const string Hosts = "inventory/videocards/{videocardName}/hosts"; + public const string Index = Hardware.Index + "/videocards"; + public const string Hosts = Index + "/{videocardName}/hosts"; - public static string HostsHref(string? videocardName) - { - return Hosts.Replace("{videocardName}", videocardName); - } + public static string HostsHref(string? videocardName) => Hosts.Replace("{videocardName}", videocardName); } } } - - public static class Communication - { - public const string Index = "communication"; - - public static class Chat - { - public const string Index = "communication/chat"; - } - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Insight.Web.csproj b/src/Web/Insight.Web/Insight.Web.csproj index 19ffdd0..b5226cd 100644 --- a/src/Web/Insight.Web/Insight.Web.csproj +++ b/src/Web/Insight.Web/Insight.Web.csproj @@ -2,18 +2,14 @@ net7.0 - latest Insight web - 2025.2.24.0 - 2025.2.24.0 + 2023.12.14.0 Insight.Web enable enable false false - none - true @@ -32,27 +28,23 @@ - - + + - - - - - - - - - - + + + PreserveNewest + + + diff --git a/src/Web/Insight.Web/Messages/RemoteMessages.cs b/src/Web/Insight.Web/Messages/RemoteMessages.cs new file mode 100644 index 0000000..6feebaa --- /dev/null +++ b/src/Web/Insight.Web/Messages/RemoteMessages.cs @@ -0,0 +1,9 @@ +using Insight.Web.Network.Remote; + +namespace Insight.Web.Messages; + +public class RemoteMessages +{ + public record RemoteConnected(RemoteSession RemoteSession); + public record RemoteDisconnected(RemoteSession RemoteSession); +} diff --git a/src/Web/Insight.Web/Middleware/IdentityMiddleware.cs b/src/Web/Insight.Web/Middleware/IdentityMiddleware.cs index 24d8763..68c9e01 100644 --- a/src/Web/Insight.Web/Middleware/IdentityMiddleware.cs +++ b/src/Web/Insight.Web/Middleware/IdentityMiddleware.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Insight.Web.Models.Account; using Microsoft.AspNetCore.Authentication; diff --git a/src/Web/Insight.Web/Models/Account/LoginModel.cs b/src/Web/Insight.Web/Models/Account/LoginModel.cs index aed6f6c..89e18f0 100644 --- a/src/Web/Insight.Web/Models/Account/LoginModel.cs +++ b/src/Web/Insight.Web/Models/Account/LoginModel.cs @@ -1,6 +1,4 @@ -using System.ComponentModel.DataAnnotations; - -namespace Insight.Web.Models.Account; +namespace Insight.Web.Models.Account; public class LoginModel { diff --git a/src/Web/Insight.Web/Models/RemoteClient.cs b/src/Web/Insight.Web/Models/RemoteClient.cs new file mode 100644 index 0000000..4a117e5 --- /dev/null +++ b/src/Web/Insight.Web/Models/RemoteClient.cs @@ -0,0 +1,4 @@ +namespace Insight.Web.Models; + +public record RemoteClientConnected(string Id); +public record RemoteClientDisconnected(string Id); \ No newline at end of file diff --git a/src/Web/Insight.Web/Network/Broker/Handlers/AgentHandler.cs b/src/Web/Insight.Web/Network/Broker/Handlers/AgentHandler.cs new file mode 100644 index 0000000..454202a --- /dev/null +++ b/src/Web/Insight.Web/Network/Broker/Handlers/AgentHandler.cs @@ -0,0 +1,24 @@ +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; +using Vaitr.Bus; + +namespace Insight.Web.Network.Broker.Handlers; + +public class AgentHandler : IMessageHandler +{ + private readonly Bus _bus; + + public AgentHandler(Bus bus) + { + _bus = bus; + } + + public async ValueTask HandleAsync(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + { + if (message is Proxy consoleQuery) + { + await _bus.PublishAsync(consoleQuery, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Network/Broker/WebSession.cs b/src/Web/Insight.Web/Network/Broker/WebSession.cs new file mode 100644 index 0000000..07c6351 --- /dev/null +++ b/src/Web/Insight.Web/Network/Broker/WebSession.cs @@ -0,0 +1,55 @@ +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Vaitr.Network; + +namespace Insight.Web.Network.Broker; + +public class WebSession : TcpSession +{ + private readonly IEnumerable> _handlers; + + public WebSession(IEnumerable> handlers, ISerializer serializer, ILogger logger) : base(serializer, logger) + { + _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 ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) + { + return base.OnSentAsync(context, cancellationToken); + } + + protected override async ValueTask OnReceivedAsync(IPacketContext 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; + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs b/src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs deleted file mode 100644 index 26cb7df..0000000 --- a/src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Insight.Web.Interfaces; -using Insight.Web.Messages; -using Vaitr.MemoryBus; - -namespace Insight.Web.Network.Handlers -{ - public class ConsoleHandler : IWebMessageHandler - { - private readonly IMemoryBus _bus; - - public ConsoleHandler(IMemoryBus bus) - { - _bus = bus; - } - - public async ValueTask HandleAsync(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IWebMessage - { - if (message is ConsoleQueryProxy consoleQuery) - { - await _bus.PublishAsync(consoleQuery, cancellationToken); - } - } - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Network/Remote/Handlers/RemoteHandler.cs b/src/Web/Insight.Web/Network/Remote/Handlers/RemoteHandler.cs new file mode 100644 index 0000000..3cee8d5 --- /dev/null +++ b/src/Web/Insight.Web/Network/Remote/Handlers/RemoteHandler.cs @@ -0,0 +1,107 @@ +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Remote.Messages; +using Vaitr.Bus; +using Vaitr.Network; + +namespace Insight.Web.Network.Remote.Handlers; + +public class RemoteHandler : IMessageHandler +{ + private readonly Bus _bus; + private readonly ISessionPool _remotePool; + private readonly ILogger _logger; + + public RemoteHandler(Bus bus, ISessionPool remotePool, ILogger logger) + { + _bus = bus; + _remotePool = remotePool; + _logger = logger; + } + + public async ValueTask HandleAsync(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.Id} => SessionRequest"); + + session.Mode = sessionRequest.Mode; + + await session.SendAsync(new RemoteSessionResponse + { + SessionId = session.Id + }, cancellationToken); + } + + private async Task OnCastRequestResponse(RemoteSession session, CastRequestResponse castRequestResponse, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {castRequestResponse.Id} => CastRequestResponse"); + + await _bus.PublishAsync(castRequestResponse, cancellationToken); + } + + private async Task OnMetricData(RemoteSession session, CastMetric streamMetrics, CancellationToken cancellationToken) + { + //_logger.LogInformation($"Remote {streamMetrics.Id} => MetricData"); + + await _bus.PublishAsync(streamMetrics, cancellationToken); + } + + private async Task OnScreenData(RemoteSession session, CastScreen screenData, CancellationToken cancellationToken) + { + //_logger.LogInformation($"Remote {screenData.Id} => ScreenData"); + + await _bus.PublishAsync(screenData, cancellationToken); + } + + private async Task OnCursorData(RemoteSession session, CastCursor cursorChanged, CancellationToken cancellationToken) + { + //_logger.LogInformation($"Remote {cursorChanged.Id} => CursorData"); + + await _bus.PublishAsync(cursorChanged, cancellationToken); + } + + private async Task OnClipboardData(RemoteSession session, CastClipboardReceived clipboardChanged, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {session.Id} => ClipboardData"); + + await _bus.PublishAsync(clipboardChanged, cancellationToken); + } + + private async Task OnAudioData(RemoteSession session, CastAudio audioSample, CancellationToken cancellationToken) + { + _logger.LogInformation($"Remote {session.Id} => AudioData"); + + await _bus.PublishAsync(audioSample, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Network/Remote/RemoteSession.cs b/src/Web/Insight.Web/Network/Remote/RemoteSession.cs new file mode 100644 index 0000000..6878e20 --- /dev/null +++ b/src/Web/Insight.Web/Network/Remote/RemoteSession.cs @@ -0,0 +1,87 @@ +using Insight.Domain.Enums; +using Insight.Domain.Interfaces; +using Insight.Domain.Network; +using Insight.Domain.Network.Remote.Messages; +using Vaitr.Bus; +using Vaitr.Network; +using static Insight.Web.Messages.RemoteMessages; + +namespace Insight.Web.Network.Remote; + +public class RemoteSession : TcpSession +{ + public string Id { get; } + public RemoteControlMode Mode { get; set; } + + private readonly Bus _bus; + private readonly IEnumerable> _handlers; + + public RemoteSession( + Bus bus, + IEnumerable> handlers, + ISerializer serializer, + ILogger logger) : base(serializer, logger) + { + Id = GenerateRandomId(); + + _bus = bus; + _handlers = handlers; + } + + protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) connected", RemoteEndPoint); + + //return; + + await _bus.PublishAsync(new RemoteDisconnected(this), default); + } + + protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) disconnected", RemoteEndPoint); + + await _bus.PublishAsync(new RemoteDisconnected(this), default); + } + + protected override async ValueTask OnSentAsync(IPacketContext context, CancellationToken cancellationToken) + { + //await base.OnSentAsync(context, cancellationToken); + } + + protected override async ValueTask OnReceivedAsync(IPacketContext 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 async ValueTask OnHeartbeatAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Remote ({ep?}) Heartbeat", RemoteEndPoint); + } + + public async Task ScreenDataAckAsync(CastScreen screenData, CancellationToken cancellationToken) + { + await SendAsync(new CastScreenReceived(screenData), cancellationToken); + } + + 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; + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Network/WebSession.cs b/src/Web/Insight.Web/Network/WebSession.cs deleted file mode 100644 index a5fa400..0000000 --- a/src/Web/Insight.Web/Network/WebSession.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Insight.Web.Interfaces; -using Insight.Web.Messages; -using System.Net.Sockets; -using Vaitr.Network; - -namespace Insight.Web.Network; - -public class WebSession(IEnumerable> handlers, Socket socket, Stream stream, TcpConnectionOptions options, MemPackSerializer serializer, ILogger logger) - : TcpSession(socket, stream, options, serializer, logger) -{ - private readonly IEnumerable> _handlers = handlers; - - protected override async ValueTask OnReceivedAsync(PacketContext context, CancellationToken cancellationToken) - { - await base.OnReceivedAsync(context, cancellationToken); - - foreach (var handler in _handlers) - { - try - { - await handler.HandleAsync(this, context.Data, cancellationToken); - } - catch (Exception ex) - { - _logger.LogWarning("Web ({ep?}) {ex}", RemoteEndPoint, ex.ToString()); - } - } - } -} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Account/ProfileTwoFactorDialog.razor b/src/Web/Insight.Web/Pages/Account/ProfileTwoFactorDialog.razor index 5aced2c..a4a26ae 100644 --- a/src/Web/Insight.Web/Pages/Account/ProfileTwoFactorDialog.razor +++ b/src/Web/Insight.Web/Pages/Account/ProfileTwoFactorDialog.razor @@ -9,17 +9,11 @@