From 3c9ccaafeb00113ceb500ff2f517f0117fe6fa59 Mon Sep 17 00:00:00 2001 From: kkb Date: Fri, 17 Nov 2023 17:12:41 +0100 Subject: [PATCH] testing remote stuff --- insight.sln | 16 + src/Agent/Insight.Agent/Insight.Agent.csproj | 4 +- .../Insight.Agent/Network/AgentSession.cs | 2 +- .../Network/Handlers/AuthenticationHandler.cs | 45 +- .../Network/Handlers/CustomHandler.cs | 40 + .../Network/Handlers/DriveHandler.cs | 16 +- .../Network/Handlers/InterfaceHandler.cs | 17 +- .../Network/Handlers/MainboardHandler.cs | 10 +- .../Network/Handlers/MemoryHandler.cs | 16 +- .../Handlers/OperationSystemHandler.cs | 10 +- .../Network/Handlers/PrinterHandler.cs | 16 +- .../Network/Handlers/ProcessorHandler.cs | 16 +- .../Network/Handlers/ProxyHandler.cs | 44 + .../Network/Handlers/ServiceHandler.cs | 16 +- .../Network/Handlers/SessionHandler.cs | 18 +- .../Network/Handlers/SoftwareHandler.cs | 24 +- .../Network/Handlers/StoragePoolHandler.cs | 22 +- .../Network/Handlers/SystemInfoHandler.cs | 10 +- .../Network/Handlers/UpdateHandler.cs | 26 +- .../Network/Handlers/UserHandler.cs | 16 +- .../Network/Handlers/VideocardHandler.cs | 16 +- .../Handlers/VirtualMaschineHandler.cs | 20 +- src/Agent/Insight.Agent/Program.cs | 12 +- .../Insight.Agent/Services/EventService.cs | 6 +- .../ScriptService.cs} | 62 +- .../Insight.Agent/Services/TrapService.cs | 4 +- .../Insight.Agent/Services/_Collector/_Os.cs | 2 +- .../Services/_Collector/_Session.cs | 2 +- src/Api/Insight.Api/Insight.Api.csproj | 11 +- src/Api/Insight.Api/appsettings.json | 2 +- .../Insight.Domain/Enums/RemoteControlMode.cs | 8 + .../Insight.Domain/Enums/SessionEndReasons.cs | 7 + .../Enums/SessionSwitchReason.cs | 14 + src/Core/Insight.Domain/Insight.Domain.csproj | 4 +- ...ntMessageHandler.cs => IMessageHandler.cs} | 2 +- .../Messages/Agent/ConsoleQuery.cs | 23 - src/Core/Insight.Domain/Messages/IMessage.cs | 46 - src/Core/Insight.Domain/Messages/Keepalive.cs | 6 - src/Core/Insight.Domain/Models/Result.cs | 148 ++ .../Agent/Messages}/Application.cs | 7 +- .../Agent/Messages}/Authentication.cs | 12 +- .../Agent/Messages}/Commands.cs | 2 +- .../Agent => Network/Agent/Messages}/Drive.cs | 5 +- .../Agent => Network/Agent/Messages}/Event.cs | 2 +- .../Agent/Messages}/Interface.cs | 5 +- .../Agent/Messages}/Mainboard.cs | 2 +- .../Agent/Messages}/Memory.cs | 5 +- .../Agent/Messages}/OperationSystem.cs | 2 +- .../Agent/Messages}/Printer.cs | 7 +- .../Agent/Messages}/Processor.cs | 5 +- .../Network/Agent/Messages/Query.cs | 61 + .../Agent/Messages}/Service.cs | 7 +- .../Agent/Messages}/Session.cs | 7 +- .../Agent/Messages}/Status.cs | 2 +- .../Agent/Messages}/StoragePool.cs | 5 +- .../Agent/Messages}/SystemInfo.cs | 2 +- .../Agent => Network/Agent/Messages}/Trap.cs | 2 +- .../Agent/Messages}/Update.cs | 4 +- .../Agent => Network/Agent/Messages}/User.cs | 5 +- .../Agent/Messages}/Videocard.cs | 7 +- .../Agent/Messages}/VirtualMaschine.cs | 5 +- src/Core/Insight.Domain/Network/Collection.cs | 7 + src/Core/Insight.Domain/Network/IMessage.cs | 54 + .../{Messages => Network}/Proxy.cs | 7 +- .../Network/Remote/Messages/Session.cs | 24 + .../Network/Remote/Messages/Stream.cs | 185 +++ .../Constants/Appsettings.cs | 4 + .../Entities/{ => Agent}/Agent.cs | 6 + .../Entities/{ => Agent}/AgentLog.cs | 6 + .../Entities/{ => Customer}/Customer.cs | 6 + .../Entities/{ => Host}/Host.cs | 6 + .../Entities/{ => Host}/HostApplication.cs | 6 + .../Entities/{ => Host}/HostDrive.cs | 6 + .../Entities/{ => Host}/HostHypervisor.cs | 7 + .../Entities/{ => Host}/HostInterface.cs | 10 + .../Entities/{ => Host}/HostLog.cs | 6 + .../Entities/{ => Host}/HostLogMonitoring.cs | 6 + .../Entities/{ => Host}/HostMainboard.cs | 6 + .../Entities/{ => Host}/HostMemory.cs | 6 + .../Entities/{ => Host}/HostOs.cs | 6 + .../Entities/{ => Host}/HostPrinter.cs | 6 + .../Entities/{ => Host}/HostProcessor.cs | 6 + .../Entities/{ => Host}/HostService.cs | 6 + .../Entities/{ => Host}/HostSession.cs | 6 + .../Entities/{ => Host}/HostStoragePool.cs | 8 + .../Entities/Host/HostSysGroup.cs | 45 + .../Entities/Host/HostSysUser.cs | 66 + .../Entities/Host/HostSysUserSysGroup.cs | 36 + .../Entities/{ => Host}/HostSystem.cs | 6 + .../Entities/{ => Host}/HostUpdate.cs | 6 + .../Entities/{ => Host}/HostVideocard.cs | 6 + .../Entities/{ => Host}/HostVolume.cs | 6 + .../Entities/HostGroup/HostGroup.cs | 31 + .../Entities/HostGroup/HostGroupHost.cs | 30 + .../Entities/HostUser.cs | 119 -- .../Entities/{ => Identity}/Identity.cs | 9 + .../Extensions/MongoDatabaseExtensions.cs | 56 - .../Insight.Infrastructure.csproj | 8 +- .../Abstractions/IAudioProvider.cs | 6 + .../Abstractions/IClipboardProvider.cs | 7 + .../Abstractions/ICursorProvider.cs | 9 + .../Abstractions/IDesktopApp.cs | 8 + .../Abstractions/IDispatcher.cs | 10 + .../Abstractions/IFileProvider.cs | 13 + .../Abstractions/IInputProvider.cs | 16 + .../Abstractions/IScreenProvider.cs | 22 + .../Enums/ButtonAction.cs | 7 + .../Extensions/ImageExtensions.cs | 181 +++ .../Extensions/ServiceCollectionExtensions.cs | 21 + .../Insight.Remote.Shared/Helpers/Disposer.cs | 21 + .../Helpers/WaitHelper.cs | 16 + .../Insight.Remote.Shared.csproj | 26 + .../Messages/AudioSampleReady.cs | 3 + .../Messages/CastRequestDemand.cs | 5 + .../Messages/ClipboardChanged.cs | 3 + .../Messages/ConnectionStateChanged.cs | 13 + .../Messages/CursorChanged.cs | 5 + .../Messages/IdentityChanged.cs | 3 + .../Messages/ScreenChanged.cs | 5 + .../Messages/WindowsSessionEnding.cs | 5 + .../Messages/WindowsSessionSwitched.cs | 5 + .../Models/AppOptions.cs | 6 + .../Models/CursorInfo.cs | 17 + .../Models/FrameResult.cs | 5 + .../Insight.Remote.Shared/Models/SentFrame.cs | 13 + .../Native/Linux/LibX11.cs | 140 ++ .../Native/Linux/LibXtst.cs | 15 + .../Native/Linux/Libc.cs | 9 + .../Native/Linux/libXrandr.cs | 62 + .../Native/Windows/ADVAPI32.cs | 364 +++++ .../Native/Windows/GDI32.cs | 79 + .../Native/Windows/Kernel32.cs | 88 ++ .../Native/Windows/SECUR32.cs | 376 +++++ .../Native/Windows/Shlwapi.cs | 51 + .../Native/Windows/User32.cs | 1336 +++++++++++++++++ .../Native/Windows/WTSAPI32.cs | 78 + .../Native/Windows/Win32Interop.cs | 285 ++++ .../Network/Handlers/RemoteHandler.cs | 154 ++ .../Network/RemoteSession.cs | 103 ++ .../Reactive/AsyncRelayCommand.cs | 96 ++ .../Reactive/ObservableObject.cs | 45 + .../Reactive/RelayCommand.cs | 87 ++ .../Insight.Remote.Shared/Services/Runtime.cs | 68 + .../Services/Streamer.cs | 362 +++++ .../ViewModels/FileUpload.cs | 23 + .../Insight.Remote.Windows.csproj | 37 + .../Models/DirectXOutput.cs | 36 + src/Remote/Insight.Remote.Windows/Program.cs | 77 + .../Properties/launchSettings.json | 12 + .../Resources/Styles.xaml | 26 + .../Insight.Remote.Windows/Services/WinApp.cs | 200 +++ .../Services/WinAudioProvider.cs | 82 + .../Services/WinClipboardProvider.cs | 92 ++ .../Services/WinCursorProvider.cs | 112 ++ .../Services/WinDispatcher.cs | 78 + .../Services/WinFileProvider.cs | 172 +++ .../Services/WinInputProvider.cs | 406 +++++ .../Services/WinScreenCapturer.cs | 520 +++++++ .../ViewModels/MainViewModel.cs | 157 ++ .../ViewModels/ViewModelBase.cs | 13 + .../Views/MainWindow.xaml | 86 ++ .../Views/MainWindow.xaml.cs | 63 + .../Insight.Server/Insight.Server.csproj | 9 +- .../Network/{ => Agent}/AgentSession.cs | 17 +- .../Agent => Agent/Handlers}/AgentHandler.cs | 25 +- .../Network/Agent/Handlers/CustomHandler.cs | 31 + .../Agent => Agent/Handlers}/DriveHandler.cs | 13 +- .../Agent => Agent/Handlers}/EventHandler.cs | 15 +- .../Handlers}/InterfaceHandler.cs | 13 +- .../Handlers}/MainboardHandler.cs | 13 +- .../Agent => Agent/Handlers}/MemoryHandler.cs | 13 +- .../Handlers}/OperationSystemHandler.cs | 13 +- .../Handlers}/PrinterHandler.cs | 13 +- .../Handlers}/ProcessorHandler.cs | 13 +- .../Handlers}/ServiceHandler.cs | 13 +- .../Handlers}/SessionHandler.cs | 13 +- .../Handlers}/SoftwareHandler.cs | 13 +- .../Handlers}/StoragePoolHandler.cs | 13 +- .../Handlers}/SystemInfoHandler.cs | 13 +- .../Agent => Agent/Handlers}/TrapHandler.cs | 13 +- .../Agent => Agent/Handlers}/UpdateHandler.cs | 15 +- .../Agent => Agent/Handlers}/UserHandler.cs | 73 +- .../Handlers}/VideocardHandler.cs | 13 +- .../Handlers}/VirtualMaschineHandler.cs | 13 +- .../Network/Handlers/Agent/ConsoleHandler.cs | 41 - .../Network/Remote/Handlers/RemoteHandler.cs | 111 ++ .../Network/Remote/RemoteSession.cs | 92 ++ .../ProxyHandler.cs} | 52 +- .../Network/{ => Web}/WebSession.cs | 4 +- src/Server/Insight.Server/Program.cs | 153 +- .../Insight.Server/Services/JobService.cs | 80 +- .../appsettings.Development.json | 5 + src/Server/Insight.Server/appsettings.json | 1 + .../Insight.Setup.Windows.csproj | 2 +- .../Insight.Updater/Insight.Updater.csproj | 2 +- .../Components/Navbars/Customer.razor.cs | 2 +- .../Components/Navbars/Host.razor.cs | 3 +- .../Insight.Web/Components/Navbars/Main.razor | 4 +- .../Components/Providers/DrawerProvider.razor | 1 - src/Web/Insight.Web/Constants/Navigation.cs | 12 + src/Web/Insight.Web/Insight.Web.csproj | 19 +- .../Insight.Web/Messages/RemoteMessages.cs | 9 + .../Middleware/IdentityMiddleware.cs | 3 +- src/Web/Insight.Web/Models/RemoteClient.cs | 4 + .../Handlers/AgentHandler.cs} | 12 +- .../Network/{ => Broker}/WebSession.cs | 4 +- .../Network/Remote/Handlers/RemoteHandler.cs | 107 ++ .../Network/Remote/RemoteSession.cs | 87 ++ .../Inventory/Hardware/Drives/Hosts.razor.cs | 3 +- .../Inventory/Hardware/Drives/Index.razor.cs | 3 +- .../Hardware/Mainboards/Hosts.razor.cs | 3 +- .../Hardware/Mainboards/Index.razor.cs | 3 +- .../Inventory/Hardware/Memory/Hosts.razor.cs | 3 +- .../Inventory/Hardware/Memory/Index.razor.cs | 3 +- .../Hardware/Processors/Hosts.razor.cs | 3 +- .../Hardware/Processors/Index.razor.cs | 3 +- .../Hardware/Videocards/Hosts.razor.cs | 3 +- .../Hardware/Videocards/Index.razor.cs | 3 +- .../Network/Addresses/Hosts.razor.cs | 3 +- .../Network/Addresses/Index.razor.cs | 3 +- .../Inventory/Network/Gateways/Hosts.razor.cs | 3 +- .../Inventory/Network/Gateways/Index.razor.cs | 3 +- .../Network/Interfaces/Index.razor.cs | 3 +- .../Network/Nameservers/Hosts.razor.cs | 3 +- .../Network/Nameservers/Index.razor.cs | 3 +- .../Inventory/Network/Routes/Hosts.razor.cs | 3 +- .../Inventory/Network/Routes/Index.razor.cs | 3 +- .../Inventory/Systems/Groups/Hosts.razor.cs | 5 +- .../Inventory/Systems/Groups/Index.razor.cs | 7 +- .../Inventory/Systems/Os/Guests.razor.cs | 3 +- .../Pages/Inventory/Systems/Os/Hosts.razor | 2 +- .../Pages/Inventory/Systems/Os/Hosts.razor.cs | 19 +- .../Pages/Inventory/Systems/Os/Index.razor.cs | 2 +- .../Inventory/Systems/Printers/Index.razor.cs | 3 +- .../Inventory/Systems/Services/Hosts.razor.cs | 3 +- .../Inventory/Systems/Services/Index.razor.cs | 3 +- .../Inventory/Systems/Sessions/Index.razor.cs | 3 +- .../Inventory/Systems/Software/Hosts.razor.cs | 3 +- .../Inventory/Systems/Software/Index.razor.cs | 3 +- .../Systems/StoragePools/Index.razor | 2 +- .../Systems/StoragePools/Index.razor.cs | 3 +- .../Inventory/Systems/Updates/Hosts.razor | 2 +- .../Inventory/Systems/Updates/Hosts.razor.cs | 3 +- .../Inventory/Systems/Updates/Index.razor.cs | 3 +- .../Inventory/Systems/Users/Hosts.razor.cs | 5 +- .../Inventory/Systems/Users/Index.razor.cs | 7 +- .../Systems/VirtualMaschines/Index.razor | 2 +- .../Systems/VirtualMaschines/Index.razor.cs | 3 +- .../Inventory/Systems/Volumes/Index.razor.cs | 3 +- .../Accounts/AccountCreateDialog.razor | 18 +- .../Accounts/AccountCreateDialog.razor.cs | 20 +- .../Accounts/AccountDeleteDialog.razor | 11 +- .../Accounts/AccountDeleteDialog.razor.cs | 28 +- .../Accounts/AccountEditDialog.razor | 11 +- .../Accounts/AccountEditDialog.razor.cs | 44 +- .../Management/Accounts/Details.razor.cs | 3 +- .../Pages/Management/Accounts/Index.razor | 26 +- .../Pages/Management/Accounts/Index.razor.cs | 1 - .../Management/Agents/AgentDeleteDialog.razor | 11 +- .../Agents/AgentDeleteDialog.razor.cs | 30 +- .../Management/Agents/AgentHostDialog.razor | 14 +- .../Agents/AgentHostDialog.razor.cs | 34 +- .../Pages/Management/Agents/Details.razor | 6 +- .../Pages/Management/Agents/Details.razor.cs | 3 +- .../Pages/Management/Agents/HostAssign.razor | 2 +- .../Management/Agents/HostAssign.razor.cs | 3 +- .../Pages/Management/Agents/Index.razor | 6 +- .../Pages/Management/Agents/Index.razor.cs | 11 - .../Pages/Management/Agents/Logs.razor.cs | 1 - .../Customers/CustomerCreateDialog.razor | 13 +- .../Customers/CustomerCreateDialog.razor.cs | 21 +- .../Customers/CustomerDeleteDialog.razor | 8 +- .../Customers/CustomerDeleteDialog.razor.cs | 27 +- .../Customers/CustomerEditDialog.razor | 21 +- .../Customers/CustomerEditDialog.razor.cs | 95 +- .../Management/Customers/Details.razor.cs | 3 +- .../Pages/Management/Customers/Hosts.razor.cs | 3 +- .../Management/Customers/HostsAssign.razor.cs | 3 +- .../Pages/Management/Customers/Index.razor | 22 +- .../Pages/Management/Customers/Index.razor.cs | 59 +- .../Pages/Management/HostGroups/Details.razor | 20 + .../Management/HostGroups/Details.razor.cs | 57 + .../Management/HostGroups/HostAssign.razor | 38 + .../Management/HostGroups/HostAssign.razor.cs | 178 +++ .../HostGroups/HostGroupCreateDialog.razor | 33 + .../HostGroups/HostGroupCreateDialog.razor.cs | 61 + .../HostGroups/HostGroupDeleteDialog.razor | 32 + .../HostGroups/HostGroupDeleteDialog.razor.cs | 66 + .../HostGroups/HostGroupEditDialog.razor | 41 + .../HostGroups/HostGroupEditDialog.razor.cs | 71 + .../Pages/Management/HostGroups/Hosts.razor | 39 + .../Management/HostGroups/Hosts.razor.cs | 157 ++ .../Pages/Management/HostGroups/Index.razor | 137 +- .../Management/HostGroups/Index.razor.cs | 416 ++--- .../Hosts/Actions/Console/Index.razor | 11 +- .../Hosts/Actions/Console/Index.razor.cs | 33 +- .../Management/Hosts/AgentAssign.razor.cs | 3 +- .../Management/Hosts/CustomerAssign.razor | 2 +- .../Management/Hosts/CustomerAssign.razor.cs | 3 +- .../Pages/Management/Hosts/Details.razor | 10 +- .../Pages/Management/Hosts/Details.razor.cs | 3 +- .../Hosts/Hardware/Drives/Index.razor.cs | 3 +- .../Hosts/Hardware/Mainboard/Details.razor.cs | 3 +- .../Hosts/Hardware/Memory/Index.razor.cs | 3 +- .../Hardware/Processors/Details.razor.cs | 3 +- .../Hosts/Hardware/Videocards/Index.razor.cs | 3 +- .../Management/Hosts/HostAgentDialog.razor | 14 +- .../Management/Hosts/HostAgentDialog.razor.cs | 26 +- .../Management/Hosts/HostCreateDialog.razor | 13 +- .../Hosts/HostCreateDialog.razor.cs | 22 +- .../Management/Hosts/HostCustomerDialog.razor | 14 +- .../Hosts/HostCustomerDialog.razor.cs | 87 +- .../Management/Hosts/HostDeleteDialog.razor | 13 +- .../Hosts/HostDeleteDialog.razor.cs | 27 +- .../Management/Hosts/HostEditDialog.razor | 21 +- .../Management/Hosts/HostEditDialog.razor.cs | 31 +- .../Pages/Management/Hosts/Index.razor | 14 +- .../Pages/Management/Hosts/Index.razor.cs | 32 +- .../Pages/Management/Hosts/Logs.razor.cs | 1 - .../Hosts/Network/Addresses/Index.razor.cs | 3 +- .../Hosts/Network/Gateways/Index.razor.cs | 3 +- .../Hosts/Network/Interfaces/Details.razor.cs | 3 +- .../Hosts/Network/Interfaces/Index.razor.cs | 3 +- .../Hosts/Network/Nameservers/Index.razor.cs | 3 +- .../Hosts/Network/Routes/Index.razor.cs | 3 +- .../Hosts/Systems/Groups/Index.razor | 6 +- .../Hosts/Systems/Groups/Index.razor.cs | 33 +- .../Hosts/Systems/Os/Details.razor.cs | 3 +- .../Hosts/Systems/Printers/Index.razor.cs | 3 +- .../Hosts/Systems/Services/Index.razor.cs | 3 +- .../Hosts/Systems/Sessions/Index.razor.cs | 3 +- .../Hosts/Systems/Software/Index.razor.cs | 3 +- .../Hosts/Systems/StoragePools/Details.razor | 2 +- .../Systems/StoragePools/Details.razor.cs | 3 +- .../Hosts/Systems/StoragePools/Index.razor | 2 +- .../Hosts/Systems/StoragePools/Index.razor.cs | 3 +- .../StoragePools/PhysicalDisks/Details.razor | 2 +- .../PhysicalDisks/Details.razor.cs | 3 +- .../StoragePools/PhysicalDisks/Index.razor | 2 +- .../StoragePools/PhysicalDisks/Index.razor.cs | 3 +- .../StoragePools/VirtualDisks/Details.razor | 2 +- .../VirtualDisks/Details.razor.cs | 3 +- .../StoragePools/VirtualDisks/Index.razor | 2 +- .../StoragePools/VirtualDisks/Index.razor.cs | 3 +- .../Hosts/Systems/Updates/Installed.razor | 2 +- .../Hosts/Systems/Updates/Installed.razor.cs | 3 +- .../Hosts/Systems/Updates/Pending.razor.cs | 3 +- .../Hosts/Systems/Users/Details.razor.cs | 5 +- .../Hosts/Systems/Users/Index.razor | 12 +- .../Hosts/Systems/Users/Index.razor.cs | 45 +- .../Systems/VirtualMaschines/Details.razor | 2 +- .../Systems/VirtualMaschines/Details.razor.cs | 3 +- .../Systems/VirtualMaschines/Index.razor | 2 +- .../Systems/VirtualMaschines/Index.razor.cs | 3 +- .../Snapshots/Details.razor.cs | 3 +- .../Hosts/Systems/Volumes/Details.razor.cs | 3 +- .../Hosts/Systems/Volumes/Index.razor.cs | 3 +- .../Pages/Management/Overview/Index.razor.cs | 3 +- .../Maintenance/Drives/Index.razor.cs | 3 +- .../Monitoring/Maintenance/Guests/Index.razor | 2 +- .../Maintenance/Guests/Index.razor.cs | 3 +- .../Monitoring/Maintenance/Index.razor.cs | 3 +- .../Maintenance/Snapshots/Index.razor.cs | 3 +- .../Maintenance/Storagepools/Index.razor.cs | 3 +- .../Maintenance/Updates/Index.razor.cs | 3 +- .../Maintenance/Volumes/Index.razor.cs | 3 +- src/Web/Insight.Web/Pages/Remote/Index.razor | 332 ++++ src/Web/Insight.Web/Pages/_Host.cshtml | 3 +- src/Web/Insight.Web/Program.cs | 86 +- src/Web/Insight.Web/Services/ChatService.cs | 2 +- .../Insight.Web/Services/SessionHandler.cs | 2 +- .../Insight.Web/appsettings.Development.json | 6 +- src/Web/Insight.Web/appsettings.json | 6 +- src/Web/Insight.Web/wwwroot/js/interop.js | 73 + 374 files changed, 10526 insertions(+), 2037 deletions(-) create mode 100644 src/Agent/Insight.Agent/Network/Handlers/CustomHandler.cs create mode 100644 src/Agent/Insight.Agent/Network/Handlers/ProxyHandler.cs rename src/Agent/Insight.Agent/{Network/Handlers/ConsoleHandler.cs => Services/ScriptService.cs} (62%) create mode 100644 src/Core/Insight.Domain/Enums/RemoteControlMode.cs create mode 100644 src/Core/Insight.Domain/Enums/SessionEndReasons.cs create mode 100644 src/Core/Insight.Domain/Enums/SessionSwitchReason.cs rename src/Core/Insight.Domain/Interfaces/{IAgentMessageHandler.cs => IMessageHandler.cs} (86%) delete mode 100644 src/Core/Insight.Domain/Messages/Agent/ConsoleQuery.cs delete mode 100644 src/Core/Insight.Domain/Messages/IMessage.cs delete mode 100644 src/Core/Insight.Domain/Messages/Keepalive.cs create mode 100644 src/Core/Insight.Domain/Models/Result.cs rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Application.cs (81%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Authentication.cs (72%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Commands.cs (65%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Drive.cs (93%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Event.cs (93%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Interface.cs (96%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Mainboard.cs (91%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Memory.cs (89%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/OperationSystem.cs (91%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Printer.cs (73%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Processor.cs (89%) create mode 100644 src/Core/Insight.Domain/Network/Agent/Messages/Query.cs rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Service.cs (88%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Session.cs (73%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Status.cs (82%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/StoragePool.cs (97%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/SystemInfo.cs (88%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Trap.cs (91%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Update.cs (94%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/User.cs (90%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/Videocard.cs (73%) rename src/Core/Insight.Domain/{Messages/Agent => Network/Agent/Messages}/VirtualMaschine.cs (97%) create mode 100644 src/Core/Insight.Domain/Network/Collection.cs create mode 100644 src/Core/Insight.Domain/Network/IMessage.cs rename src/Core/Insight.Domain/{Messages => Network}/Proxy.cs (59%) create mode 100644 src/Core/Insight.Domain/Network/Remote/Messages/Session.cs create mode 100644 src/Core/Insight.Domain/Network/Remote/Messages/Stream.cs rename src/Core/Insight.Infrastructure/Entities/{ => Agent}/Agent.cs (88%) rename src/Core/Insight.Infrastructure/Entities/{ => Agent}/AgentLog.cs (80%) rename src/Core/Insight.Infrastructure/Entities/{ => Customer}/Customer.cs (76%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/Host.cs (84%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostApplication.cs (81%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostDrive.cs (85%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostHypervisor.cs (91%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostInterface.cs (86%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostLog.cs (81%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostLogMonitoring.cs (81%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostMainboard.cs (80%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostMemory.cs (86%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostOs.cs (82%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostPrinter.cs (82%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostProcessor.cs (88%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostService.cs (86%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostSession.cs (81%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostStoragePool.cs (89%) create mode 100644 src/Core/Insight.Infrastructure/Entities/Host/HostSysGroup.cs create mode 100644 src/Core/Insight.Infrastructure/Entities/Host/HostSysUser.cs create mode 100644 src/Core/Insight.Infrastructure/Entities/Host/HostSysUserSysGroup.cs rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostSystem.cs (79%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostUpdate.cs (88%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostVideocard.cs (81%) rename src/Core/Insight.Infrastructure/Entities/{ => Host}/HostVolume.cs (89%) create mode 100644 src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroup.cs create mode 100644 src/Core/Insight.Infrastructure/Entities/HostGroup/HostGroupHost.cs delete mode 100644 src/Core/Insight.Infrastructure/Entities/HostUser.cs rename src/Core/Insight.Infrastructure/Entities/{ => Identity}/Identity.cs (79%) delete mode 100644 src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IAudioProvider.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IClipboardProvider.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/ICursorProvider.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IDesktopApp.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IDispatcher.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IFileProvider.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IInputProvider.cs create mode 100644 src/Remote/Insight.Remote.Shared/Abstractions/IScreenProvider.cs create mode 100644 src/Remote/Insight.Remote.Shared/Enums/ButtonAction.cs create mode 100644 src/Remote/Insight.Remote.Shared/Extensions/ImageExtensions.cs create mode 100644 src/Remote/Insight.Remote.Shared/Extensions/ServiceCollectionExtensions.cs create mode 100644 src/Remote/Insight.Remote.Shared/Helpers/Disposer.cs create mode 100644 src/Remote/Insight.Remote.Shared/Helpers/WaitHelper.cs create mode 100644 src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj create mode 100644 src/Remote/Insight.Remote.Shared/Messages/AudioSampleReady.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/CastRequestDemand.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/ClipboardChanged.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/ConnectionStateChanged.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/CursorChanged.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/IdentityChanged.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/ScreenChanged.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/WindowsSessionEnding.cs create mode 100644 src/Remote/Insight.Remote.Shared/Messages/WindowsSessionSwitched.cs create mode 100644 src/Remote/Insight.Remote.Shared/Models/AppOptions.cs create mode 100644 src/Remote/Insight.Remote.Shared/Models/CursorInfo.cs create mode 100644 src/Remote/Insight.Remote.Shared/Models/FrameResult.cs create mode 100644 src/Remote/Insight.Remote.Shared/Models/SentFrame.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Linux/LibX11.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Linux/LibXtst.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Linux/Libc.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Linux/libXrandr.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/ADVAPI32.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/GDI32.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/Kernel32.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/SECUR32.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/Shlwapi.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/User32.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/WTSAPI32.cs create mode 100644 src/Remote/Insight.Remote.Shared/Native/Windows/Win32Interop.cs create mode 100644 src/Remote/Insight.Remote.Shared/Network/Handlers/RemoteHandler.cs create mode 100644 src/Remote/Insight.Remote.Shared/Network/RemoteSession.cs create mode 100644 src/Remote/Insight.Remote.Shared/Reactive/AsyncRelayCommand.cs create mode 100644 src/Remote/Insight.Remote.Shared/Reactive/ObservableObject.cs create mode 100644 src/Remote/Insight.Remote.Shared/Reactive/RelayCommand.cs create mode 100644 src/Remote/Insight.Remote.Shared/Services/Runtime.cs create mode 100644 src/Remote/Insight.Remote.Shared/Services/Streamer.cs create mode 100644 src/Remote/Insight.Remote.Shared/ViewModels/FileUpload.cs create mode 100644 src/Remote/Insight.Remote.Windows/Insight.Remote.Windows.csproj create mode 100644 src/Remote/Insight.Remote.Windows/Models/DirectXOutput.cs create mode 100644 src/Remote/Insight.Remote.Windows/Program.cs create mode 100644 src/Remote/Insight.Remote.Windows/Properties/launchSettings.json create mode 100644 src/Remote/Insight.Remote.Windows/Resources/Styles.xaml create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinApp.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinAudioProvider.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinClipboardProvider.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinCursorProvider.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinDispatcher.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinFileProvider.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinInputProvider.cs create mode 100644 src/Remote/Insight.Remote.Windows/Services/WinScreenCapturer.cs create mode 100644 src/Remote/Insight.Remote.Windows/ViewModels/MainViewModel.cs create mode 100644 src/Remote/Insight.Remote.Windows/ViewModels/ViewModelBase.cs create mode 100644 src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml create mode 100644 src/Remote/Insight.Remote.Windows/Views/MainWindow.xaml.cs rename src/Server/Insight.Server/Network/{ => Agent}/AgentSession.cs (82%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/AgentHandler.cs (86%) create mode 100644 src/Server/Insight.Server/Network/Agent/Handlers/CustomHandler.cs rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/DriveHandler.cs (95%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/EventHandler.cs (96%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/InterfaceHandler.cs (98%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/MainboardHandler.cs (86%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/MemoryHandler.cs (91%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/OperationSystemHandler.cs (86%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/PrinterHandler.cs (90%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/ProcessorHandler.cs (92%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/ServiceHandler.cs (91%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/SessionHandler.cs (90%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/SoftwareHandler.cs (90%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/StoragePoolHandler.cs (97%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/SystemInfoHandler.cs (84%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/TrapHandler.cs (97%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/UpdateHandler.cs (93%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/UserHandler.cs (62%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/VideocardHandler.cs (90%) rename src/Server/Insight.Server/Network/{Handlers/Agent => Agent/Handlers}/VirtualMaschineHandler.cs (96%) delete mode 100644 src/Server/Insight.Server/Network/Handlers/Agent/ConsoleHandler.cs create mode 100644 src/Server/Insight.Server/Network/Remote/Handlers/RemoteHandler.cs create mode 100644 src/Server/Insight.Server/Network/Remote/RemoteSession.cs rename src/Server/Insight.Server/Network/{Handlers/Web/ConsoleProxyHandler.cs => Shared/ProxyHandler.cs} (54%) rename src/Server/Insight.Server/Network/{ => Web}/WebSession.cs (96%) create mode 100644 src/Web/Insight.Web/Messages/RemoteMessages.cs create mode 100644 src/Web/Insight.Web/Models/RemoteClient.cs rename src/Web/Insight.Web/Network/{Handlers/ConsoleHandler.cs => Broker/Handlers/AgentHandler.cs} (58%) rename src/Web/Insight.Web/Network/{ => Broker}/WebSession.cs (96%) create mode 100644 src/Web/Insight.Web/Network/Remote/Handlers/RemoteHandler.cs create mode 100644 src/Web/Insight.Web/Network/Remote/RemoteSession.cs create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor.cs create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor.cs create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor.cs create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor.cs create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor.cs create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor create mode 100644 src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor.cs create mode 100644 src/Web/Insight.Web/Pages/Remote/Index.razor diff --git a/insight.sln b/insight.sln index 4d81ecd..57b9b5c 100644 --- a/insight.sln +++ b/insight.sln @@ -33,6 +33,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Updater", "src\Upda EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Insight.Agent", "src\Agent\Insight.Agent\Insight.Agent.csproj", "{2A391CA2-F96B-4DB7-80AA-0668A5141640}" EndProject +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 Debug|Any CPU = Debug|Any CPU @@ -71,6 +77,14 @@ Global {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 + {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 @@ -84,6 +98,8 @@ Global {1E75F7E9-E6AA-44E7-A2F3-DB4CA85E0118} = {038C3821-E554-496D-B585-A3BC193B7913} {4875D70F-A96B-4EBA-99BE-218886D29BEB} = {F2D241DB-7692-46DB-8A6A-958B365DAAF8} {2A391CA2-F96B-4DB7-80AA-0668A5141640} = {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/Insight.Agent.csproj b/src/Agent/Insight.Agent/Insight.Agent.csproj index fbcdb18..79aa1d5 100644 --- a/src/Agent/Insight.Agent/Insight.Agent.csproj +++ b/src/Agent/Insight.Agent/Insight.Agent.csproj @@ -6,7 +6,7 @@ Insight.Agent Insight agent - 2023.9.21.0 + 2023.11.17.0 enable enable @@ -26,7 +26,7 @@ - + diff --git a/src/Agent/Insight.Agent/Network/AgentSession.cs b/src/Agent/Insight.Agent/Network/AgentSession.cs index 2fcd017..b13df10 100644 --- a/src/Agent/Insight.Agent/Network/AgentSession.cs +++ b/src/Agent/Insight.Agent/Network/AgentSession.cs @@ -1,5 +1,5 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; +using Insight.Domain.Network; using Microsoft.Extensions.Logging; using Vaitr.Network; diff --git a/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs index 027b0c2..cc71964 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/AuthenticationHandler.cs @@ -2,8 +2,8 @@ using Insight.Agent.Services; using Insight.Domain.Constants; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; namespace Insight.Agent.Network.Handlers; @@ -11,28 +11,33 @@ public class AuthenticationHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is AuthenticationRequest) + switch (message) { - Config? config = null; + case AuthenticationRequest: + { + Config? config = null; - try - { - config = await Configurator.ReadAsync(Configuration.DefaultConfig, cancellationToken).ConfigureAwait(false); - } - catch (Exception) { } + 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); - } + 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); + await sender.SendAsync(new AuthenticationResponse + { + Serial = config.Serial ?? throw new InvalidDataException(nameof(config.Serial)), + Version = Configuration.Version, + Hostname = Configuration.Hostname + }, cancellationToken); + + break; + } } } } \ No newline at end of file diff --git a/src/Agent/Insight.Agent/Network/Handlers/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 14dba70..d18393c 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/DriveHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/DriveHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,12 +11,16 @@ public class DriveHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new DriveList(); - result.AddRange(GetDrives()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetDrives()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs index b910c16..e5b5b1b 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/InterfaceHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +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.Domain.Messages.Agent.Route; namespace Insight.Agent.Network.Handlers; @@ -15,12 +14,16 @@ public class InterfaceHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new InterfaceList(); - result.AddRange(GetInterfaces()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetInterfaces()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs index 0c80c47..4869735 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/MainboardHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,9 +11,11 @@ public class MainboardHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - await sender.SendAsync(GetMainboard(), cancellationToken); + case InventoryRequest: + await sender.SendAsync(GetMainboard(), cancellationToken); + break; } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs index 34f0460..aea3bce 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/MemoryHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,12 +11,16 @@ public class MemoryHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new MemoryList(); - result.AddRange(GetMemory()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetMemory()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs index a925804..ef15546 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/OperationSystemHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Win32; using System.Management; using System.Runtime.InteropServices; @@ -14,9 +14,11 @@ public class OperationSystemHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - await sender.SendAsync(GetOperatingSystem(), cancellationToken); + case InventoryRequest: + await sender.SendAsync(GetOperatingSystem(), cancellationToken); + break; } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs index 842f9ea..0b241e6 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/PrinterHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,12 +11,16 @@ public class PrinterHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new PrinterList(); - result.AddRange(GetPrinters()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetPrinters()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs index 2d3c72b..7953846 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/ProcessorHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,12 +11,16 @@ public class ProcessorHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new ProcessorList(); - result.AddRange(GetProcessors()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetProcessors()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } 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 e3ce58a..cfe9342 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/ServiceHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/ServiceHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; using System.ServiceProcess; @@ -12,12 +12,16 @@ public class ServiceHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new ServiceList(); - result.AddRange(GetServices()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetServices()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs index 3b331ca..b393ae5 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/SessionHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -11,19 +11,21 @@ public class SessionHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new SessionList(); - result.AddRange(GetSessions()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetSessions()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } private static List GetSessions() { - var query = NativeMethods.GetSessions(); - var sessions = new List(); foreach (var s in NativeMethods.GetSessions()) diff --git a/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs index c11fd3a..e2c04ca 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/SoftwareHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Win32; using System.Globalization; using System.Runtime.InteropServices; @@ -14,18 +14,22 @@ internal class SoftwareHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var x64 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry64), cancellationToken); - var x86 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry32), cancellationToken); + case InventoryRequest: + { + var x64 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry64), cancellationToken); + var x86 = Task.Run(() => ApplicationRegistryQuery(RegistryView.Registry32), cancellationToken); - await Task.WhenAll(x64, x86).ConfigureAwait(false); + 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))); + 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); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs index 76466c7..18bb8a1 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/StoragePoolHandler.cs @@ -1,11 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -using static Insight.Domain.Messages.Agent.PhysicalDisk; -using static Insight.Domain.Messages.Agent.StoragePool; -using static Insight.Domain.Messages.Agent.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; @@ -14,12 +14,16 @@ public class StoragePoolHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new StoragePoolList(); - result.AddRange(GetStoragePool()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetStoragePool()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs index 132b41f..49269d7 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/SystemInfoHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Win32; using System.Collections; using System.Management; @@ -13,9 +13,11 @@ public class SystemInfoHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - await sender.SendAsync(GetSystem(), cancellationToken); + case InventoryRequest: + await sender.SendAsync(GetSystem(), cancellationToken); + break; } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs index c90ae74..80454a2 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/UpdateHandler.cs @@ -1,10 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Runtime.Versioning; using System.Text.RegularExpressions; using WUApiLib; -using static Insight.Domain.Messages.Agent.Update; +using static Insight.Domain.Network.Agent.Messages.Update; +using UpdateCollection = Insight.Domain.Network.Agent.Messages.UpdateCollection; namespace Insight.Agent.Network.Handlers; @@ -13,21 +14,18 @@ public class UpdateHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - await sender.SendAsync(GetUpdates(), cancellationToken); + case InventoryRequest: + await sender.SendAsync(new UpdateCollection + { + Installed = QueryInstalledUpdates(), + Pending = QueryPendingUpdates() + }, cancellationToken); + break; } } - private static UpdateList GetUpdates() - { - return new UpdateList - { - Installed = QueryInstalledUpdates(), - Pending = QueryPendingUpdates() - }; - } - private static List QueryInstalledUpdates() { var updates = new List(); diff --git a/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs index 98d07fa..76b5602 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/UserHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,12 +11,16 @@ public class UserHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new UserList(); - result.AddRange(GetUsers()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetUsers()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs index 270384b..078c36a 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/VideocardHandler.cs @@ -1,6 +1,6 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; @@ -11,12 +11,16 @@ public class VideocardHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new VideocardList(); - result.AddRange(GetVideocards()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetVideocards()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs b/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs index 98faa5b..f8c843f 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs +++ b/src/Agent/Insight.Agent/Network/Handlers/VirtualMaschineHandler.cs @@ -1,10 +1,10 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using System.Management; using System.Runtime.Versioning; -using static Insight.Domain.Messages.Agent.VirtualMaschine; -using static Insight.Domain.Messages.Agent.VirtualMaschineConfiguration; +using static Insight.Domain.Network.Agent.Messages.VirtualMaschine; +using static Insight.Domain.Network.Agent.Messages.VirtualMaschineConfiguration; namespace Insight.Agent.Network.Handlers; @@ -13,12 +13,16 @@ public class VirtualMaschineHandler : IMessageHandler { public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InventoryRequest) + switch (message) { - var result = new VirtualMaschineList(); - result.AddRange(GetVirtualMaschines()); + case InventoryRequest: + { + var result = new Collection(); + result.AddRange(GetVirtualMaschines()); - await sender.SendAsync(result, cancellationToken); + await sender.SendAsync(result, cancellationToken); + break; + } } } diff --git a/src/Agent/Insight.Agent/Program.cs b/src/Agent/Insight.Agent/Program.cs index d75f668..807ac7a 100644 --- a/src/Agent/Insight.Agent/Program.cs +++ b/src/Agent/Insight.Agent/Program.cs @@ -4,7 +4,7 @@ using Insight.Agent.Network.Handlers; using Insight.Agent.Services; using Insight.Domain.Constants; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; +using Insight.Domain.Network; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -44,12 +44,13 @@ internal class Program builder.ConfigureServices((host, services) => { - // SERVICES + // HOST-SERVICES services.AddHostedService(); services.AddHostedService(); // SERVICES (WINDOWS) if (OperatingSystem.IsWindows()) services.AddHostedService(); + if (OperatingSystem.IsWindows()) services.AddSingleton(); // AGENT NETWORKING services.UseHostedClient(options => @@ -62,9 +63,11 @@ internal class Program options.Compression = true; options.Encryption = Encryption.Tls12; - options.UseSerializer, IMessage>(); + options.UseSerializer>(); }); + services.AddSingleton, CustomHandler>(); + services.AddSingleton, ProxyHandler>(); services.AddSingleton, AuthenticationHandler>(); services.AddSingleton, DriveHandler>(); services.AddSingleton, InterfaceHandler>(); @@ -82,9 +85,10 @@ internal class Program services.AddSingleton, UserHandler>(); services.AddSingleton, VideocardHandler>(); services.AddSingleton, VirtualMaschineHandler>(); - services.AddSingleton, ConsoleHandler>(); + // GLOBAL DEPENDENCIES + //services.AddSingleton(); services.AddTransient(provider => new HttpClient(new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual, diff --git a/src/Agent/Insight.Agent/Services/EventService.cs b/src/Agent/Insight.Agent/Services/EventService.cs index 5499c18..b2520a9 100644 --- a/src/Agent/Insight.Agent/Services/EventService.cs +++ b/src/Agent/Insight.Agent/Services/EventService.cs @@ -1,13 +1,13 @@ using Insight.Agent.Network; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +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.Domain.Messages.Agent.Event; +using static Insight.Domain.Network.Agent.Messages.Event; using EventLevel = System.Diagnostics.Tracing.EventLevel; namespace Insight.Agent.Services; diff --git a/src/Agent/Insight.Agent/Network/Handlers/ConsoleHandler.cs b/src/Agent/Insight.Agent/Services/ScriptService.cs similarity index 62% rename from src/Agent/Insight.Agent/Network/Handlers/ConsoleHandler.cs rename to src/Agent/Insight.Agent/Services/ScriptService.cs index 87aed7c..32e4eda 100644 --- a/src/Agent/Insight.Agent/Network/Handlers/ConsoleHandler.cs +++ b/src/Agent/Insight.Agent/Services/ScriptService.cs @@ -1,39 +1,19 @@ -using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +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 : IMessageHandler +public class ScriptService { - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage + private readonly ILogger _logger; + + public ScriptService(ILogger logger) { - if (message is Proxy consoleQueryRequest) - { - await OnConsoleQueryRequestAsync(sender, consoleQueryRequest, cancellationToken); - } + _logger = logger; } - private async ValueTask OnConsoleQueryRequestAsync(AgentSession sender, Proxy consoleQueryRequest, CancellationToken cancellationToken) - { - var result = await QueryScriptAsync(consoleQueryRequest.Message.Query); - - await sender.SendAsync(new Proxy - { - RequestId = consoleQueryRequest.RequestId, - HostId = consoleQueryRequest.HostId, - Message = new ConsoleQuery - { - 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(); @@ -47,7 +27,7 @@ public class ConsoleHandler : IMessageHandler 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; @@ -60,7 +40,15 @@ public class ConsoleHandler : IMessageHandler } 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; @@ -103,12 +91,12 @@ public class ConsoleHandler : IMessageHandler 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 8e95054..cd1a1a9 100644 --- a/src/Agent/Insight.Agent/Services/TrapService.cs +++ b/src/Agent/Insight.Agent/Services/TrapService.cs @@ -1,6 +1,6 @@ using Insight.Agent.Network; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/src/Agent/Insight.Agent/Services/_Collector/_Os.cs b/src/Agent/Insight.Agent/Services/_Collector/_Os.cs index 3caa4b2..34db513 100644 --- a/src/Agent/Insight.Agent/Services/_Collector/_Os.cs +++ b/src/Agent/Insight.Agent/Services/_Collector/_Os.cs @@ -1,5 +1,5 @@ using Insight.Agent.Extensions; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Logging; using System.Text.RegularExpressions; diff --git a/src/Agent/Insight.Agent/Services/_Collector/_Session.cs b/src/Agent/Insight.Agent/Services/_Collector/_Session.cs index e8d1199..fd42981 100644 --- a/src/Agent/Insight.Agent/Services/_Collector/_Session.cs +++ b/src/Agent/Insight.Agent/Services/_Collector/_Session.cs @@ -1,5 +1,5 @@ using Insight.Agent.Extensions; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network.Agent.Messages; using Microsoft.Extensions.Logging; using System.Text.RegularExpressions; diff --git a/src/Api/Insight.Api/Insight.Api.csproj b/src/Api/Insight.Api/Insight.Api.csproj index 8deb797..c1656c2 100644 --- a/src/Api/Insight.Api/Insight.Api.csproj +++ b/src/Api/Insight.Api/Insight.Api.csproj @@ -4,7 +4,7 @@ net7.0 Insight api - 2023.9.21.1 + 2023.11.17.0 Insight.Api enable enable @@ -30,17 +30,10 @@ - + - - - - - - - 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/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/Insight.Domain.csproj b/src/Core/Insight.Domain/Insight.Domain.csproj index 1ffd066..135a8e4 100644 --- a/src/Core/Insight.Domain/Insight.Domain.csproj +++ b/src/Core/Insight.Domain/Insight.Domain.csproj @@ -6,11 +6,11 @@ enable Insight.Domain Insight - 2023.9.21.1 + 2023.11.17.0 - + diff --git a/src/Core/Insight.Domain/Interfaces/IAgentMessageHandler.cs b/src/Core/Insight.Domain/Interfaces/IMessageHandler.cs similarity index 86% rename from src/Core/Insight.Domain/Interfaces/IAgentMessageHandler.cs rename to src/Core/Insight.Domain/Interfaces/IMessageHandler.cs index b420e74..1c0d3cb 100644 --- a/src/Core/Insight.Domain/Interfaces/IAgentMessageHandler.cs +++ b/src/Core/Insight.Domain/Interfaces/IMessageHandler.cs @@ -1,4 +1,4 @@ -using Insight.Domain.Messages; +using Insight.Domain.Network; namespace Insight.Domain.Interfaces; diff --git a/src/Core/Insight.Domain/Messages/Agent/ConsoleQuery.cs b/src/Core/Insight.Domain/Messages/Agent/ConsoleQuery.cs deleted file mode 100644 index c878462..0000000 --- a/src/Core/Insight.Domain/Messages/Agent/ConsoleQuery.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MemoryPack; - -namespace Insight.Domain.Messages.Agent; - -[MemoryPackable] -public partial class ConsoleQuery : IMessage -{ - [MemoryPackOrder(0)] - public string? Data { get; set; } - - [MemoryPackOrder(1)] - public string? Errors { get; set; } - - [MemoryPackOrder(2)] - public bool HadErrors { get; set; } -} - -[MemoryPackable] -public partial class ConsoleQueryRequest : IMessage -{ - [MemoryPackOrder(0)] - public string? Query { get; set; } -} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/IMessage.cs b/src/Core/Insight.Domain/Messages/IMessage.cs deleted file mode 100644 index 1d9ff68..0000000 --- a/src/Core/Insight.Domain/Messages/IMessage.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MemoryPack; - -namespace Insight.Domain.Messages; - -[MemoryPackable] -[MemoryPackUnion(0, typeof(Keepalive))] -[MemoryPackUnion(1, typeof(Agent.AuthenticationRequest))] -[MemoryPackUnion(2, typeof(Agent.Authentication))] -[MemoryPackUnion(10, typeof(Agent.InventoryRequest))] -[MemoryPackUnion(100, typeof(Agent.Application))] -[MemoryPackUnion(101, typeof(Agent.ApplicationList))] -[MemoryPackUnion(102, typeof(Agent.Drive))] -[MemoryPackUnion(103, typeof(Agent.DriveList))] -[MemoryPackUnion(104, typeof(Agent.Event))] -[MemoryPackUnion(105, typeof(Agent.Interface))] -[MemoryPackUnion(106, typeof(Agent.InterfaceList))] -[MemoryPackUnion(107, typeof(Agent.Mainboard))] -[MemoryPackUnion(108, typeof(Agent.Memory))] -[MemoryPackUnion(109, typeof(Agent.MemoryList))] -[MemoryPackUnion(110, typeof(Agent.OperationSystem))] -[MemoryPackUnion(111, typeof(Agent.Printer))] -[MemoryPackUnion(112, typeof(Agent.PrinterList))] -[MemoryPackUnion(113, typeof(Agent.Processor))] -[MemoryPackUnion(114, typeof(Agent.ProcessorList))] -[MemoryPackUnion(115, typeof(Agent.Service))] -[MemoryPackUnion(116, typeof(Agent.ServiceList))] -[MemoryPackUnion(117, typeof(Agent.Session))] -[MemoryPackUnion(118, typeof(Agent.SessionList))] -[MemoryPackUnion(119, typeof(Agent.Status))] -[MemoryPackUnion(120, typeof(Agent.StoragePool))] -[MemoryPackUnion(121, typeof(Agent.StoragePoolList))] -[MemoryPackUnion(122, typeof(Agent.SystemInfo))] -[MemoryPackUnion(123, typeof(Agent.Trap))] -[MemoryPackUnion(124, typeof(Agent.Update))] -[MemoryPackUnion(125, typeof(Agent.UpdateList))] -[MemoryPackUnion(126, typeof(Agent.User))] -[MemoryPackUnion(127, typeof(Agent.UserList))] -[MemoryPackUnion(128, typeof(Agent.Videocard))] -[MemoryPackUnion(129, typeof(Agent.VideocardList))] -[MemoryPackUnion(130, typeof(Agent.VirtualMaschine))] -[MemoryPackUnion(131, typeof(Agent.VirtualMaschineList))] -[MemoryPackUnion(132, typeof(Agent.ConsoleQuery))] -[MemoryPackUnion(133, typeof(Agent.ConsoleQueryRequest))] -[MemoryPackUnion(1000, typeof(Proxy))] -[MemoryPackUnion(1001, typeof(Proxy))] -public partial interface IMessage { } \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Keepalive.cs b/src/Core/Insight.Domain/Messages/Keepalive.cs deleted file mode 100644 index 3991383..0000000 --- a/src/Core/Insight.Domain/Messages/Keepalive.cs +++ /dev/null @@ -1,6 +0,0 @@ -using MemoryPack; - -namespace Insight.Domain.Messages; - -[MemoryPackable] -public partial class Keepalive : 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/Messages/Agent/Application.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Application.cs similarity index 81% rename from src/Core/Insight.Domain/Messages/Agent/Application.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Application.cs index 45c6696..fc2db2f 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Application.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Application.cs @@ -1,7 +1,7 @@ using MemoryPack; using System.Runtime.InteropServices; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Application : IMessage @@ -29,7 +29,4 @@ public partial class Application : IMessage [MemoryPackOrder(7)] public Architecture? Architecture { get; set; } -} - -[MemoryPackable(GenerateType.Collection)] -public partial class ApplicationList : List, IMessage { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Agent/Authentication.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Authentication.cs similarity index 72% rename from src/Core/Insight.Domain/Messages/Agent/Authentication.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Authentication.cs index b5841ce..566d490 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Authentication.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Authentication.cs @@ -1,9 +1,12 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] -public partial class Authentication : IMessage +public partial class AuthenticationRequest : IMessage { } + +[MemoryPackable] +public partial class AuthenticationResponse : IMessage { [MemoryPackOrder(0)] public PlatformType? Platform { get; set; } @@ -23,7 +26,4 @@ public partial class Authentication : IMessage Windows = 1, Unix = 2 } -} - -[MemoryPackable] -public partial class AuthenticationRequest : IMessage { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Agent/Commands.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Commands.cs similarity index 65% rename from src/Core/Insight.Domain/Messages/Agent/Commands.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Commands.cs index 71853ea..e8b32b6 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Commands.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Commands.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +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/Messages/Agent/Drive.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Drive.cs similarity index 93% rename from src/Core/Insight.Domain/Messages/Agent/Drive.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Drive.cs index 8ac952b..8d8d556 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Drive.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Drive.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Drive : IMessage @@ -39,9 +39,6 @@ public partial class Drive : IMessage public List? Volumes { get; set; } } -[MemoryPackable(GenerateType.Collection)] -public partial class DriveList : List, IMessage { } - [MemoryPackable] public partial class Volume : IMessage { diff --git a/src/Core/Insight.Domain/Messages/Agent/Event.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Event.cs similarity index 93% rename from src/Core/Insight.Domain/Messages/Agent/Event.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Event.cs index 99bb231..1afb844 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Event.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Event.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Event : IMessage diff --git a/src/Core/Insight.Domain/Messages/Agent/Interface.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Interface.cs similarity index 96% rename from src/Core/Insight.Domain/Messages/Agent/Interface.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Interface.cs index 265470d..3c985b6 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Interface.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Interface.cs @@ -3,7 +3,7 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Interface : IMessage @@ -90,9 +90,6 @@ public partial class Interface : IMessage public List? Routes { get; set; } } -[MemoryPackable(GenerateType.Collection)] -public partial class InterfaceList : List, IMessage { } - [MemoryPackable] public partial class Unicast : IMessage { diff --git a/src/Core/Insight.Domain/Messages/Agent/Mainboard.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Mainboard.cs similarity index 91% rename from src/Core/Insight.Domain/Messages/Agent/Mainboard.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Mainboard.cs index 05d44c5..463c12b 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Mainboard.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Mainboard.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Mainboard : IMessage diff --git a/src/Core/Insight.Domain/Messages/Agent/Memory.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Memory.cs similarity index 89% rename from src/Core/Insight.Domain/Messages/Agent/Memory.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Memory.cs index eb42f38..d11ddaf 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Memory.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Memory.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Memory : IMessage @@ -39,9 +39,6 @@ public partial class Memory : IMessage public uint? ConfiguredVoltage { get; set; } } -[MemoryPackable(GenerateType.Collection)] -public partial class MemoryList : List, IMessage { } - [MemoryPackable] public partial class MemoryMetric : IMessage { diff --git a/src/Core/Insight.Domain/Messages/Agent/OperationSystem.cs b/src/Core/Insight.Domain/Network/Agent/Messages/OperationSystem.cs similarity index 91% rename from src/Core/Insight.Domain/Messages/Agent/OperationSystem.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/OperationSystem.cs index 80bd6ff..3eb9602 100644 --- a/src/Core/Insight.Domain/Messages/Agent/OperationSystem.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/OperationSystem.cs @@ -1,7 +1,7 @@ using MemoryPack; using System.Runtime.InteropServices; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class OperationSystem : IMessage diff --git a/src/Core/Insight.Domain/Messages/Agent/Printer.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Printer.cs similarity index 73% rename from src/Core/Insight.Domain/Messages/Agent/Printer.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Printer.cs index f77a4a7..743fc2c 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Printer.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Printer.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Printer : IMessage @@ -19,7 +19,4 @@ public partial class Printer : IMessage [MemoryPackOrder(4)] public string? Comment { get; set; } -} - -[MemoryPackable(GenerateType.Collection)] -public partial class PrinterList : List, IMessage { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Agent/Processor.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Processor.cs similarity index 89% rename from src/Core/Insight.Domain/Messages/Agent/Processor.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Processor.cs index c11f496..94ce8be 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Processor.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Processor.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Processor : IMessage @@ -51,9 +51,6 @@ public partial class Processor : IMessage public bool? Virtualization { get; set; } } -[MemoryPackable(GenerateType.Collection)] -public partial class ProcessorList : List, IMessage { } - [MemoryPackable] public partial class ProcessorMetric : IMessage { 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/Messages/Agent/Service.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Service.cs similarity index 88% rename from src/Core/Insight.Domain/Messages/Agent/Service.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Service.cs index eb61f63..4d7a51b 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Service.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Service.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Service : IMessage @@ -53,7 +53,4 @@ public partial class Service : IMessage Manual = 3, Disabled = 4 } -} - -[MemoryPackable(GenerateType.Collection)] -public partial class ServiceList : List, IMessage { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Agent/Session.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Session.cs similarity index 73% rename from src/Core/Insight.Domain/Messages/Agent/Session.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Session.cs index a9d68b8..024db30 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Session.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Session.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Session : IMessage @@ -19,7 +19,4 @@ public partial class Session : IMessage [MemoryPackOrder(4)] public string? Remote { get; set; } -} - -[MemoryPackable(GenerateType.Collection)] -public partial class SessionList : List, IMessage { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Agent/Status.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Status.cs similarity index 82% rename from src/Core/Insight.Domain/Messages/Agent/Status.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Status.cs index 59264d3..ec59e1f 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Status.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Status.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Status : IMessage diff --git a/src/Core/Insight.Domain/Messages/Agent/StoragePool.cs b/src/Core/Insight.Domain/Network/Agent/Messages/StoragePool.cs similarity index 97% rename from src/Core/Insight.Domain/Messages/Agent/StoragePool.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/StoragePool.cs index 3d39ed9..d2d22da 100644 --- a/src/Core/Insight.Domain/Messages/Agent/StoragePool.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/StoragePool.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class StoragePool : IMessage @@ -90,9 +90,6 @@ public partial class StoragePool : IMessage } } -[MemoryPackable(GenerateType.Collection)] -public partial class StoragePoolList : List, IMessage { } - [MemoryPackable] public partial class PhysicalDisk : IMessage { diff --git a/src/Core/Insight.Domain/Messages/Agent/SystemInfo.cs b/src/Core/Insight.Domain/Network/Agent/Messages/SystemInfo.cs similarity index 88% rename from src/Core/Insight.Domain/Messages/Agent/SystemInfo.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/SystemInfo.cs index 641202b..187abfd 100644 --- a/src/Core/Insight.Domain/Messages/Agent/SystemInfo.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/SystemInfo.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class SystemInfo : IMessage diff --git a/src/Core/Insight.Domain/Messages/Agent/Trap.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Trap.cs similarity index 91% rename from src/Core/Insight.Domain/Messages/Agent/Trap.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Trap.cs index a2cd071..e2199ae 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Trap.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Trap.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Trap : IMessage diff --git a/src/Core/Insight.Domain/Messages/Agent/Update.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Update.cs similarity index 94% rename from src/Core/Insight.Domain/Messages/Agent/Update.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Update.cs index cf4b51f..5a039cb 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Update.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Update.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Update : IMessage @@ -68,7 +68,7 @@ public partial class Update : IMessage } [MemoryPackable] -public partial class UpdateList : IMessage +public partial class UpdateCollection : IMessage { [MemoryPackOrder(0)] public List? Installed { get; set; } diff --git a/src/Core/Insight.Domain/Messages/Agent/User.cs b/src/Core/Insight.Domain/Network/Agent/Messages/User.cs similarity index 90% rename from src/Core/Insight.Domain/Messages/Agent/User.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/User.cs index 2ca6aed..5417641 100644 --- a/src/Core/Insight.Domain/Messages/Agent/User.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/User.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class User : IMessage @@ -45,9 +45,6 @@ public partial class User : IMessage public List? Groups { get; set; } } -[MemoryPackable(GenerateType.Collection)] -public partial class UserList : List, IMessage { } - [MemoryPackable] public partial class Group : IMessage { diff --git a/src/Core/Insight.Domain/Messages/Agent/Videocard.cs b/src/Core/Insight.Domain/Network/Agent/Messages/Videocard.cs similarity index 73% rename from src/Core/Insight.Domain/Messages/Agent/Videocard.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/Videocard.cs index 0bc8dcf..f651294 100644 --- a/src/Core/Insight.Domain/Messages/Agent/Videocard.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/Videocard.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class Videocard : IMessage @@ -19,7 +19,4 @@ public partial class Videocard : IMessage [MemoryPackOrder(4)] public string? DriverVersion { get; set; } -} - -[MemoryPackable(GenerateType.Collection)] -public partial class VideocardList : List, IMessage { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Core/Insight.Domain/Messages/Agent/VirtualMaschine.cs b/src/Core/Insight.Domain/Network/Agent/Messages/VirtualMaschine.cs similarity index 97% rename from src/Core/Insight.Domain/Messages/Agent/VirtualMaschine.cs rename to src/Core/Insight.Domain/Network/Agent/Messages/VirtualMaschine.cs index e630502..7e2eef5 100644 --- a/src/Core/Insight.Domain/Messages/Agent/VirtualMaschine.cs +++ b/src/Core/Insight.Domain/Network/Agent/Messages/VirtualMaschine.cs @@ -1,6 +1,6 @@ using MemoryPack; -namespace Insight.Domain.Messages.Agent; +namespace Insight.Domain.Network.Agent.Messages; [MemoryPackable] public partial class VirtualMaschine : IMessage @@ -150,9 +150,6 @@ public partial class VirtualMaschine : IMessage } } -[MemoryPackable(GenerateType.Collection)] -public partial class VirtualMaschineList : List, IMessage { } - [MemoryPackable] public partial class VirtualMaschineConfiguration : IMessage { 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/Messages/Proxy.cs b/src/Core/Insight.Domain/Network/Proxy.cs similarity index 59% rename from src/Core/Insight.Domain/Messages/Proxy.cs rename to src/Core/Insight.Domain/Network/Proxy.cs index a322ba6..a16fb78 100644 --- a/src/Core/Insight.Domain/Messages/Proxy.cs +++ b/src/Core/Insight.Domain/Network/Proxy.cs @@ -1,17 +1,14 @@ using MemoryPack; -namespace Insight.Domain.Messages; +namespace Insight.Domain.Network; [MemoryPackable] public partial class Proxy : IMessage where TMessage : IMessage { [MemoryPackOrder(0)] - public string? RequestId { get; set; } + public string? ProxyId { get; set; } [MemoryPackOrder(1)] - public string? HostId { get; set; } - - [MemoryPackOrder(2)] 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 2d45d12..e1adf44 100644 --- a/src/Core/Insight.Infrastructure/Constants/Appsettings.cs +++ b/src/Core/Insight.Infrastructure/Constants/Appsettings.cs @@ -10,4 +10,8 @@ public class Appsettings 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/Entities/Agent.cs b/src/Core/Insight.Infrastructure/Entities/Agent/Agent.cs similarity index 88% rename from src/Core/Insight.Infrastructure/Entities/Agent.cs rename to src/Core/Insight.Infrastructure/Entities/Agent/Agent.cs index 1f4109e..e673e8e 100644 --- a/src/Core/Insight.Infrastructure/Entities/Agent.cs +++ b/src/Core/Insight.Infrastructure/Entities/Agent/Agent.cs @@ -1,10 +1,16 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/AgentLog.cs b/src/Core/Insight.Infrastructure/Entities/Agent/AgentLog.cs similarity index 80% rename from src/Core/Insight.Infrastructure/Entities/AgentLog.cs rename to src/Core/Insight.Infrastructure/Entities/Agent/AgentLog.cs index 9cbd8cb..b8fc06e 100644 --- a/src/Core/Insight.Infrastructure/Entities/AgentLog.cs +++ b/src/Core/Insight.Infrastructure/Entities/Agent/AgentLog.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/Customer.cs b/src/Core/Insight.Infrastructure/Entities/Customer/Customer.cs similarity index 76% rename from src/Core/Insight.Infrastructure/Entities/Customer.cs rename to src/Core/Insight.Infrastructure/Entities/Customer/Customer.cs index e6a23fe..8a1b737 100644 --- a/src/Core/Insight.Infrastructure/Entities/Customer.cs +++ b/src/Core/Insight.Infrastructure/Entities/Customer/Customer.cs @@ -1,10 +1,16 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/Host.cs b/src/Core/Insight.Infrastructure/Entities/Host/Host.cs similarity index 84% rename from src/Core/Insight.Infrastructure/Entities/Host.cs rename to src/Core/Insight.Infrastructure/Entities/Host/Host.cs index 1766708..2e065c8 100644 --- a/src/Core/Insight.Infrastructure/Entities/Host.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/Host.cs @@ -1,10 +1,16 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostApplication.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostApplication.cs similarity index 81% rename from src/Core/Insight.Infrastructure/Entities/HostApplication.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostApplication.cs index 8d1b52b..388b522 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostApplication.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostApplication.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostDrive.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostDrive.cs similarity index 85% rename from src/Core/Insight.Infrastructure/Entities/HostDrive.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostDrive.cs index 2e77a64..72d46e7 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostDrive.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostDrive.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostHypervisor.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostHypervisor.cs similarity index 91% rename from src/Core/Insight.Infrastructure/Entities/HostHypervisor.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostHypervisor.cs index abbdf4b..0bfdcb5 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostHypervisor.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostHypervisor.cs @@ -1,9 +1,16 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostInterface.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostInterface.cs similarity index 86% rename from src/Core/Insight.Infrastructure/Entities/HostInterface.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostInterface.cs index 4761f12..7756a47 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostInterface.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostInterface.cs @@ -1,9 +1,19 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostLog.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostLog.cs similarity index 81% rename from src/Core/Insight.Infrastructure/Entities/HostLog.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostLog.cs index da1e86a..ee34fa6 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostLog.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostLog.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostLogMonitoring.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostLogMonitoring.cs similarity index 81% rename from src/Core/Insight.Infrastructure/Entities/HostLogMonitoring.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostLogMonitoring.cs index 6b8a14f..6579981 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostLogMonitoring.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostLogMonitoring.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostMainboard.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostMainboard.cs similarity index 80% rename from src/Core/Insight.Infrastructure/Entities/HostMainboard.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostMainboard.cs index 4cd6d1a..5339e78 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostMainboard.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostMainboard.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostMemory.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostMemory.cs similarity index 86% rename from src/Core/Insight.Infrastructure/Entities/HostMemory.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostMemory.cs index 8b65ca6..0f0dc70 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostMemory.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostMemory.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostOs.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostOs.cs similarity index 82% rename from src/Core/Insight.Infrastructure/Entities/HostOs.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostOs.cs index 2e37f3b..06a65df 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostOs.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostOs.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostPrinter.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostPrinter.cs similarity index 82% rename from src/Core/Insight.Infrastructure/Entities/HostPrinter.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostPrinter.cs index e3b1653..afa266f 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostPrinter.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostPrinter.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostProcessor.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostProcessor.cs similarity index 88% rename from src/Core/Insight.Infrastructure/Entities/HostProcessor.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostProcessor.cs index 8d4a7ea..a31fba5 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostProcessor.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostProcessor.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostService.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostService.cs similarity index 86% rename from src/Core/Insight.Infrastructure/Entities/HostService.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostService.cs index 2167e53..c55ab70 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostService.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostService.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostSession.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSession.cs similarity index 81% rename from src/Core/Insight.Infrastructure/Entities/HostSession.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostSession.cs index 6992f95..f2b15d6 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostSession.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSession.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostStoragePool.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostStoragePool.cs similarity index 89% rename from src/Core/Insight.Infrastructure/Entities/HostStoragePool.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostStoragePool.cs index 3fc138d..4d8ce19 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostStoragePool.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostStoragePool.cs @@ -1,9 +1,17 @@ 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 { 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/HostSystem.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostSystem.cs similarity index 79% rename from src/Core/Insight.Infrastructure/Entities/HostSystem.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostSystem.cs index f6a1e0d..610544f 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostSystem.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostSystem.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostUpdate.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostUpdate.cs similarity index 88% rename from src/Core/Insight.Infrastructure/Entities/HostUpdate.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostUpdate.cs index 4b7b243..7f497ea 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostUpdate.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostUpdate.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostVideocard.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostVideocard.cs similarity index 81% rename from src/Core/Insight.Infrastructure/Entities/HostVideocard.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostVideocard.cs index da9fb62..1ce11d8 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostVideocard.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostVideocard.cs @@ -1,9 +1,15 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Entities/HostVolume.cs b/src/Core/Insight.Infrastructure/Entities/Host/HostVolume.cs similarity index 89% rename from src/Core/Insight.Infrastructure/Entities/HostVolume.cs rename to src/Core/Insight.Infrastructure/Entities/Host/HostVolume.cs index 21f5dfb..901c644 100644 --- a/src/Core/Insight.Infrastructure/Entities/HostVolume.cs +++ b/src/Core/Insight.Infrastructure/Entities/Host/HostVolume.cs @@ -1,9 +1,15 @@ 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 { 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/HostUser.cs b/src/Core/Insight.Infrastructure/Entities/HostUser.cs deleted file mode 100644 index 95670f8..0000000 --- a/src/Core/Insight.Infrastructure/Entities/HostUser.cs +++ /dev/null @@ -1,119 +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/Identity.cs b/src/Core/Insight.Infrastructure/Entities/Identity/Identity.cs similarity index 79% rename from src/Core/Insight.Infrastructure/Entities/Identity.cs rename to src/Core/Insight.Infrastructure/Entities/Identity/Identity.cs index 732a69d..4f01e89 100644 --- a/src/Core/Insight.Infrastructure/Entities/Identity.cs +++ b/src/Core/Insight.Infrastructure/Entities/Identity/Identity.cs @@ -1,11 +1,20 @@ 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 { diff --git a/src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs b/src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs deleted file mode 100644 index 3a76e9b..0000000 --- a/src/Core/Insight.Infrastructure/Extensions/MongoDatabaseExtensions.cs +++ /dev/null @@ -1,56 +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/Insight.Infrastructure.csproj b/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj index 99eb1c5..3da34a0 100644 --- a/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj +++ b/src/Core/Insight.Infrastructure/Insight.Infrastructure.csproj @@ -4,7 +4,7 @@ net7.0 Insight.Infrastructure Insight - 2023.9.21.1 + 2023.11.17.0 true enable @@ -18,10 +18,10 @@ - - + + - + 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..abfa5b1 --- /dev/null +++ b/src/Remote/Insight.Remote.Shared/Insight.Remote.Shared.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + Insight.Remote.Shared + Insight + Insight.Remote.Shared + 2023.11.17.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..2c6eb46 --- /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.11.17.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/Insight.Server.csproj b/src/Server/Insight.Server/Insight.Server.csproj index 5a406de..df5a67e 100644 --- a/src/Server/Insight.Server/Insight.Server.csproj +++ b/src/Server/Insight.Server/Insight.Server.csproj @@ -5,7 +5,7 @@ net7.0 Insight server - 2023.9.21.1 + 2023.11.17.0 Insight.Server enable enable @@ -42,13 +42,6 @@ - - - - - - - diff --git a/src/Server/Insight.Server/Network/AgentSession.cs b/src/Server/Insight.Server/Network/Agent/AgentSession.cs similarity index 82% rename from src/Server/Insight.Server/Network/AgentSession.cs rename to src/Server/Insight.Server/Network/Agent/AgentSession.cs index b15549d..e97f48e 100644 --- a/src/Server/Insight.Server/Network/AgentSession.cs +++ b/src/Server/Insight.Server/Network/Agent/AgentSession.cs @@ -1,11 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Server.Network.Handlers.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; +using Insight.Server.Network.Agent.Handlers; using Microsoft.Extensions.Logging; using Vaitr.Network; -namespace Insight.Server.Network; +namespace Insight.Server.Network.Agent; public class AgentSession : TcpSession { @@ -56,7 +56,7 @@ public class AgentSession : TcpSession { await base.OnReceivedAsync(context, cancellationToken); - if (Id is null && context.Packet is not Authentication) return; + if (Id is null && context.Packet is not AuthenticationResponse) return; await _agentHandler.StatisticUpdateAsync(this, cancellationToken); @@ -69,13 +69,6 @@ public class AgentSession : TcpSession catch (Exception ex) { _logger.LogWarning("Agent ({ep?}) {ex}", RemoteEndPoint, ex.ToString()); - - //await _mediator.Send(new AgentLog(new AgentLogEntity - //{ - // Category = CategoryEnum.Network.ToString(), - // Status = StatusEnum.Error.ToString(), - // Message = e.StackTrace - //}, this), cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/AgentHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/AgentHandler.cs similarity index 86% rename from src/Server/Insight.Server/Network/Handlers/Agent/AgentHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/AgentHandler.cs index ac0c7a6..d99d2df 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/AgentHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/AgentHandler.cs @@ -1,13 +1,12 @@ using Insight.Domain.Enums; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using Microsoft.Extensions.Logging; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class AgentHandler : IMessageHandler { @@ -22,18 +21,18 @@ public class AgentHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is AuthenticationRequest authenticationRequest) + switch (message) { - await AuthenticationRequestAsync(sender, authenticationRequest, cancellationToken); - } - - if (message is Authentication authentication) - { - await AuthenticationAsync(sender, authentication, cancellationToken); + case AuthenticationRequest authenticationRequest: + await OnAuthenticationRequestAsync(sender, authenticationRequest, cancellationToken); + break; + case AuthenticationResponse authenticationResponse: + await OnAuthenticationResponseAsync(sender, authenticationResponse, cancellationToken); + break; } } - private async ValueTask AuthenticationRequestAsync(AgentSession session, AuthenticationRequest message, CancellationToken cancellationToken) + private async ValueTask OnAuthenticationRequestAsync(AgentSession session, AuthenticationRequest message, CancellationToken cancellationToken) { await session.SendAsync(message, cancellationToken); @@ -52,7 +51,7 @@ public class AgentHandler : IMessageHandler session.Disconnect(); } - private async ValueTask AuthenticationAsync(AgentSession session, Authentication authentication, CancellationToken cancellationToken) + private async ValueTask OnAuthenticationResponseAsync(AgentSession session, AuthenticationResponse authentication, CancellationToken cancellationToken) { if (authentication is null) { 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/Handlers/Agent/DriveHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/DriveHandler.cs similarity index 95% rename from src/Server/Insight.Server/Network/Handlers/Agent/DriveHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/DriveHandler.cs index 51435e1..bc1323d 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/DriveHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/DriveHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class DriveHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class DriveHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is DriveList drives) + switch (message) { - await OnDrivesAsync(sender, drives, cancellationToken); + case Collection drives: + await OnDrivesAsync(sender, drives, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/EventHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/EventHandler.cs similarity index 96% rename from src/Server/Insight.Server/Network/Handlers/Agent/EventHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/EventHandler.cs index 78077bc..ed4bdec 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/EventHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/EventHandler.cs @@ -1,14 +1,13 @@ using Insight.Domain.Enums; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +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.Messages.Agent.Event; +using static Insight.Domain.Network.Agent.Messages.Event; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class EventHandler : IMessageHandler { @@ -23,9 +22,11 @@ public class EventHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is Event @event) + switch (message) { - await OnEventAsync(sender, @event, cancellationToken); + case Event @event: + await OnEventAsync(sender, @event, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/InterfaceHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/InterfaceHandler.cs similarity index 98% rename from src/Server/Insight.Server/Network/Handlers/Agent/InterfaceHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/InterfaceHandler.cs index b9b5e5d..70f4579 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/InterfaceHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/InterfaceHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class InterfaceHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class InterfaceHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is InterfaceList interfaces) + switch (message) { - await OnInterfacesAsync(sender, interfaces, cancellationToken); + case Collection interfaces: + await OnInterfacesAsync(sender, interfaces, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/MainboardHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/MainboardHandler.cs similarity index 86% rename from src/Server/Insight.Server/Network/Handlers/Agent/MainboardHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/MainboardHandler.cs index 99e5dfc..fcbc2b0 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/MainboardHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/MainboardHandler.cs @@ -1,11 +1,10 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +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 : IMessageHandler { @@ -18,9 +17,11 @@ namespace Insight.Server.Network.Handlers.Agent 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/Handlers/Agent/MemoryHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/MemoryHandler.cs similarity index 91% rename from src/Server/Insight.Server/Network/Handlers/Agent/MemoryHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/MemoryHandler.cs index 7c02e92..b6f1b33 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/MemoryHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/MemoryHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class MemoryHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class MemoryHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is MemoryList memory) + switch (message) { - await OnMemoryAsync(sender, memory, cancellationToken); + case Collection memory: + await OnMemoryAsync(sender, memory, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/OperationSystemHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/OperationSystemHandler.cs similarity index 86% rename from src/Server/Insight.Server/Network/Handlers/Agent/OperationSystemHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/OperationSystemHandler.cs index 9f274c1..eb8b7af 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/OperationSystemHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/OperationSystemHandler.cs @@ -1,11 +1,10 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +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 OperationSystemHandler : IMessageHandler { @@ -18,9 +17,11 @@ public class OperationSystemHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is OperationSystem os) + switch (message) { - await OnOperationSystemAsync(sender, os, cancellationToken); + case OperationSystem os: + await OnOperationSystemAsync(sender, os, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/PrinterHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/PrinterHandler.cs similarity index 90% rename from src/Server/Insight.Server/Network/Handlers/Agent/PrinterHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/PrinterHandler.cs index 18d5694..83a53cd 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/PrinterHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/PrinterHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class PrinterHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class PrinterHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is PrinterList printers) + switch (message) { - await OnPrintersAsync(sender, printers, cancellationToken); + case Collection printers: + await OnPrintersAsync(sender, printers, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/ProcessorHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/ProcessorHandler.cs similarity index 92% rename from src/Server/Insight.Server/Network/Handlers/Agent/ProcessorHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/ProcessorHandler.cs index 81befab..67b3ffb 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/ProcessorHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/ProcessorHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class ProcessorHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class ProcessorHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is ProcessorList processors) + switch (message) { - await OnProcessorsAsync(sender, processors, cancellationToken); + case Collection processors: + await OnProcessorsAsync(sender, processors, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/ServiceHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/ServiceHandler.cs similarity index 91% rename from src/Server/Insight.Server/Network/Handlers/Agent/ServiceHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/ServiceHandler.cs index 2aee03f..9df3799 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/ServiceHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/ServiceHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class ServiceHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class ServiceHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is ServiceList services) + switch (message) { - await OnServicesAsync(sender, services, cancellationToken); + case Collection services: + await OnServicesAsync(sender, services, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/SessionHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/SessionHandler.cs similarity index 90% rename from src/Server/Insight.Server/Network/Handlers/Agent/SessionHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/SessionHandler.cs index c7c9bf3..0dfb293 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/SessionHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/SessionHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class SessionHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class SessionHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is SessionList sessions) + switch (message) { - await OnSessionsAsync(sender, sessions, cancellationToken); + case Collection sessions: + await OnSessionsAsync(sender, sessions, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/SoftwareHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/SoftwareHandler.cs similarity index 90% rename from src/Server/Insight.Server/Network/Handlers/Agent/SoftwareHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/SoftwareHandler.cs index 36e1853..f236ae6 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/SoftwareHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/SoftwareHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class SoftwareHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class SoftwareHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is ApplicationList applications) + switch (message) { - await OnApplicationsAsync(sender, applications, cancellationToken); + case Collection applications: + await OnApplicationsAsync(sender, applications, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/StoragePoolHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/StoragePoolHandler.cs similarity index 97% rename from src/Server/Insight.Server/Network/Handlers/Agent/StoragePoolHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/StoragePoolHandler.cs index 3085a14..712897f 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/StoragePoolHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/StoragePoolHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class StoragePoolHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class StoragePoolHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is StoragePoolList storagePools) + switch (message) { - await OnStoragePoolsAsync(sender, storagePools, cancellationToken); + case Collection storagePools: + await OnStoragePoolsAsync(sender, storagePools, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/SystemInfoHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/SystemInfoHandler.cs similarity index 84% rename from src/Server/Insight.Server/Network/Handlers/Agent/SystemInfoHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/SystemInfoHandler.cs index 82b0b2c..3f11402 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/SystemInfoHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/SystemInfoHandler.cs @@ -1,11 +1,10 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +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 SystemInfoHandler : IMessageHandler { @@ -18,9 +17,11 @@ public class SystemInfoHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is SystemInfo systemInfo) + switch (message) { - await OnSystemInfoAsync(sender, systemInfo, cancellationToken); + case SystemInfo systemInfo: + await OnSystemInfoAsync(sender, systemInfo, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/TrapHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs similarity index 97% rename from src/Server/Insight.Server/Network/Handlers/Agent/TrapHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs index 97649e2..de0aeae 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/TrapHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/TrapHandler.cs @@ -1,8 +1,7 @@ using Insight.Domain.Enums; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using Insight.Server.Models; using MongoDB.Driver; @@ -10,7 +9,7 @@ using System.Text; using System.Text.RegularExpressions; using static Insight.Server.Models.MonitorMessage; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class TrapHandler : IMessageHandler { @@ -23,9 +22,11 @@ public class TrapHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is Trap trap) + switch (message) { - await OnTrapAsync(sender, trap, cancellationToken); + case Trap trap: + await OnTrapAsync(sender, trap, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/UpdateHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/UpdateHandler.cs similarity index 93% rename from src/Server/Insight.Server/Network/Handlers/Agent/UpdateHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/UpdateHandler.cs index 6129da6..7f90eca 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/UpdateHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/UpdateHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class UpdateHandler : IMessageHandler { @@ -19,13 +18,15 @@ public class UpdateHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is UpdateList updates) + switch (message) { - await OnUpdatesAsync(sender, updates, cancellationToken); + case UpdateCollection updates: + await OnUpdatesAsync(sender, updates, cancellationToken); + break; } } - private async ValueTask OnUpdatesAsync(AgentSession session, UpdateList updates, CancellationToken cancellationToken) + 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; diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/UserHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/UserHandler.cs similarity index 62% rename from src/Server/Insight.Server/Network/Handlers/Agent/UserHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/UserHandler.cs index eb1d729..824117f 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/UserHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/UserHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class UserHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class UserHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is UserList users) + switch (message) { - await OnUsersAsync(sender, users, cancellationToken); + case Collection users: + await OnUsersAsync(sender, users, cancellationToken); + break; } } @@ -40,18 +41,18 @@ public class UserHandler : IMessageHandler if (users is not null && users.Any()) { - var userBulk = new List>(); + var userBulk = new List>(); foreach (var user in users) { - var userFilter = Builders.Filter.And(new List> + 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) + 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 + var userUpdate = Builders.Update .SetOnInsert(p => p.Insert, date) .SetOnInsert(p => p.Host, hostEntity.Id) .SetOnInsert(p => p.Domain, user?.Domain) @@ -70,16 +71,16 @@ public class UserHandler : IMessageHandler .Set(p => p.PasswordExpires, user?.PasswordExpires) .Set(p => p.PasswordRequired, user?.PasswordRequired); - userBulk.Add(new UpdateOneModel(userFilter, userUpdate) + userBulk.Add(new UpdateOneModel(userFilter, userUpdate) { IsUpsert = true }); } - userBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + userBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) }))); var userResult = await _database.HostSystemUser().BulkWriteAsync(userBulk, cancellationToken: cancellationToken); @@ -89,7 +90,7 @@ public class UserHandler : IMessageHandler if (users is not null && users.Any()) { - var groupBulk = new List>(); + var groupBulk = new List>(); var distinctGroups = users.SelectMany(p => p.Groups) .GroupBy(p => new { p?.Domain, p?.Name }) @@ -97,14 +98,14 @@ public class UserHandler : IMessageHandler foreach (var group in distinctGroups) { - var groupFilter = Builders.Filter.And(new List> + 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) + 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 + var groupUpdate = Builders.Update .SetOnInsert(p => p.Insert, date) .SetOnInsert(p => p.Host, hostEntity.Id) .SetOnInsert(p => p.Domain, group?.Domain) @@ -116,16 +117,16 @@ public class UserHandler : IMessageHandler .Set(p => p.Description, group?.Description) .Set(p => p.LocalAccount, group?.LocalAccount); - groupBulk.Add(new UpdateOneModel(groupFilter, groupUpdate) + groupBulk.Add(new UpdateOneModel(groupFilter, groupUpdate) { IsUpsert = true }); } - groupBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + groupBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) }))); var groupResult = await _database.HostSystemGroup().BulkWriteAsync(groupBulk, cancellationToken: cancellationToken); @@ -135,7 +136,7 @@ public class UserHandler : IMessageHandler if (users is not null && users.Any()) { - var relationBulk = new List>(); + var relationBulk = new List>(); foreach (var user in users) { @@ -153,14 +154,14 @@ public class UserHandler : IMessageHandler .Project(p => p.Id) .FirstOrDefaultAsync(); - var relationFilter = Builders.Filter.And(new List> + 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) + 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 + var relationUpdate = Builders.Update .SetOnInsert(p => p.Insert, date) .SetOnInsert(p => p.Host, hostEntity.Id) .SetOnInsert(p => p.User, userId) @@ -168,7 +169,7 @@ public class UserHandler : IMessageHandler .Set(p => p.Update, date) .Set(p => p.Batch, batch); - relationBulk.Add(new UpdateOneModel(relationFilter, relationUpdate) + relationBulk.Add(new UpdateOneModel(relationFilter, relationUpdate) { IsUpsert = true }); @@ -176,10 +177,10 @@ public class UserHandler : IMessageHandler } } - relationBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> + relationBulk.Add(new DeleteManyModel(Builders.Filter.And(new List> { - Builders.Filter.Eq(x => x.Host, hostEntity.Id), - Builders.Filter.Ne(x => x.Batch, batch) + Builders.Filter.Eq(x => x.Host, hostEntity.Id), + Builders.Filter.Ne(x => x.Batch, batch) }))); var relationResult = await _database.HostSystemUserSystemGroup().BulkWriteAsync(relationBulk, cancellationToken: cancellationToken); diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/VideocardHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/VideocardHandler.cs similarity index 90% rename from src/Server/Insight.Server/Network/Handlers/Agent/VideocardHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/VideocardHandler.cs index 1637c5f..ca0fba9 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/VideocardHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/VideocardHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class VideocardHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class VideocardHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is VideocardList videocards) + switch (message) { - await OnVideocardsAsync(sender, videocards, cancellationToken); + case Collection videocards: + await OnVideocardsAsync(sender, videocards, cancellationToken); + break; } } diff --git a/src/Server/Insight.Server/Network/Handlers/Agent/VirtualMaschineHandler.cs b/src/Server/Insight.Server/Network/Agent/Handlers/VirtualMaschineHandler.cs similarity index 96% rename from src/Server/Insight.Server/Network/Handlers/Agent/VirtualMaschineHandler.cs rename to src/Server/Insight.Server/Network/Agent/Handlers/VirtualMaschineHandler.cs index 220154b..6737b81 100644 --- a/src/Server/Insight.Server/Network/Handlers/Agent/VirtualMaschineHandler.cs +++ b/src/Server/Insight.Server/Network/Agent/Handlers/VirtualMaschineHandler.cs @@ -1,12 +1,11 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using MongoDB.Bson; using MongoDB.Driver; -namespace Insight.Server.Network.Handlers.Agent; +namespace Insight.Server.Network.Agent.Handlers; public class VirtualMaschineHandler : IMessageHandler { @@ -19,9 +18,11 @@ public class VirtualMaschineHandler : IMessageHandler public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is VirtualMaschineList virtualMaschines) + switch (message) { - await OnVirtualMaschinesAsync(sender, virtualMaschines, cancellationToken); + case Collection virtualMaschines: + await OnVirtualMaschinesAsync(sender, virtualMaschines, cancellationToken); + break; } } 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 f8740e8..0000000 --- a/src/Server/Insight.Server/Network/Handlers/Agent/ConsoleHandler.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; -using Vaitr.Network; - -namespace Insight.Server.Network.Handlers.Agent; - -public class ConsoleHandler : IMessageHandler -{ - private readonly ISessionPool _webPool; - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - - public ConsoleHandler( - ISessionPool webPool, - IMongoDatabase database, - ILogger logger) - { - _webPool = webPool; - _database = database; - _logger = logger; - } - - public async ValueTask HandleAsync(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage - { - if (message is Proxy consoleResponse) - { - await OnConsoleQueryAsync(sender, consoleResponse, cancellationToken); - } - } - - private async ValueTask OnConsoleQueryAsync(AgentSession session, Proxy consoleResponse, CancellationToken cancellationToken) - { - // check if web online - if (_webPool.FirstOrDefault().Value is not WebSession web) return; - - await web.SendAsync(consoleResponse, 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/Handlers/Web/ConsoleProxyHandler.cs b/src/Server/Insight.Server/Network/Shared/ProxyHandler.cs similarity index 54% rename from src/Server/Insight.Server/Network/Handlers/Web/ConsoleProxyHandler.cs rename to src/Server/Insight.Server/Network/Shared/ProxyHandler.cs index ce707e0..1d305bb 100644 --- a/src/Server/Insight.Server/Network/Handlers/Web/ConsoleProxyHandler.cs +++ b/src/Server/Insight.Server/Network/Shared/ProxyHandler.cs @@ -1,55 +1,61 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +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.Bus; using Vaitr.Network; -namespace Insight.Server.Network.Handlers.Web; +namespace Insight.Server.Network.Globals; -public class ConsoleProxyHandler : IMessageHandler +public class ProxyHandler : IMessageHandler, IMessageHandler { - private readonly List _subscriptions = new(); - private readonly ISessionPool _agentPool; private readonly ISessionPool _webPool; private readonly IMongoDatabase _database; - private readonly Bus _bus; - private readonly ILogger _logger; + private readonly ILogger _logger; - public ConsoleProxyHandler( + public ProxyHandler( ISessionPool agentPool, ISessionPool webPool, IMongoDatabase database, - Bus bus, - ILogger logger) + ILogger logger) { _agentPool = agentPool; _webPool = webPool; _database = database; - _bus = bus; _logger = logger; } public async ValueTask HandleAsync(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage { - if (message is Proxy consoleRequest) + switch (message) { - _logger.LogCritical("received: {0}" + consoleRequest.Message); - await OnConsoleQueryRequestAsync(sender, consoleRequest, cancellationToken); + case Proxy proxyRequest: + await OnProxyRequestAsync(sender, proxyRequest, cancellationToken); + break; } } - private async ValueTask OnConsoleQueryRequestAsync(WebSession session, Proxy request, CancellationToken cancellationToken) + 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.HostId)) + .Eq(p => p.Id, request.ProxyId)) .FirstOrDefaultAsync(cancellationToken); if (hostEntity is null) @@ -77,4 +83,12 @@ public class ConsoleProxyHandler : IMessageHandler // 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/WebSession.cs b/src/Server/Insight.Server/Network/Web/WebSession.cs similarity index 96% rename from src/Server/Insight.Server/Network/WebSession.cs rename to src/Server/Insight.Server/Network/Web/WebSession.cs index df4d2c7..9625bb0 100644 --- a/src/Server/Insight.Server/Network/WebSession.cs +++ b/src/Server/Insight.Server/Network/Web/WebSession.cs @@ -1,9 +1,9 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; +using Insight.Domain.Network; using Microsoft.Extensions.Logging; using Vaitr.Network; -namespace Insight.Server.Network; +namespace Insight.Server.Network.Web; public class WebSession : TcpSession { diff --git a/src/Server/Insight.Server/Program.cs b/src/Server/Insight.Server/Program.cs index 82d4670..fc2a392 100644 --- a/src/Server/Insight.Server/Program.cs +++ b/src/Server/Insight.Server/Program.cs @@ -1,11 +1,12 @@ using Insight.Domain.Constants; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; +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 Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -48,74 +49,16 @@ internal class Program builder.ConfigureServices((host, services) => { - //var databaseLoggerFactory = LoggerFactory.Create(b => - //{ - // b.AddSimpleConsole(); - // b.SetMinimumLevel(LogLevel.Debug); - //}); + // AGENT + services.AddAgentServices(host.Configuration); + + // WEB + services.AddWebServices(host.Configuration); // INFRASTRUCTURE services.AddDatabase(host.Configuration); - // AGENT SERVER - services.UseHostedServer(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.Compression = true; - options.Encryption = Encryption.Tls12; - 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.UseSerializer, IMessage>(); - }); - - 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>(); - - // WEB (FRONTEND-PROXY) SERVER - services.UseHostedServer(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.Compression = true; - options.Encryption = Encryption.Tls12; - 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.UseSerializer, IMessage>(); - }); - - services.AddSingleton, ConsoleProxyHandler>(); - - // DISPATCH + // BACKGROUND SERVICES services.AddHostedService(); services.AddHostedService(); @@ -131,4 +74,78 @@ internal class Program var host = builder.Build(); await host.RunAsync().ConfigureAwait(false); } +} + +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; + + options.Compression = true; + options.Encryption = Encryption.Tls12; + options.Certificate = configuration.GetValue(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)"); + options.CertificatePassword = configuration.GetValue(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)"); + + options.UseSerializer>(); + }); + + // HANDLER + services.AddSingleton(); + services.AddSingleton, CustomHandler>(); + services.AddSingleton, ProxyHandler>(); + services.AddSingleton, AgentHandler>(); + services.AddSingleton, DriveHandler>(); + services.AddSingleton, Network.Agent.Handlers.EventHandler>(); + services.AddSingleton, InterfaceHandler>(); + 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; + } + + 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; + + options.Certificate = configuration.GetValue(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)"); + options.CertificatePassword = configuration.GetValue(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)"); + + options.UseSerializer>(); + }); + + // HANDLER + services.AddSingleton, ProxyHandler>(); + + return services; + } } \ 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 cb8b619..471128f 100644 --- a/src/Server/Insight.Server/Services/JobService.cs +++ b/src/Server/Insight.Server/Services/JobService.cs @@ -1,9 +1,9 @@ -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -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; @@ -30,25 +30,8 @@ internal class JobService : BackgroundService var jobs = new List { - Task.Run(async () => - { - while (cancellationToken.IsCancellationRequested is false) - { - try - { - foreach (var agent in await GetAssignedAgentsAsync(cancellationToken)) - { - await agent.SendAsync(new InventoryRequest(), cancellationToken); - } - } - catch (OperationCanceledException) { } - catch (Exception) { } - finally - { - await Task.Delay(TimeSpan.FromHours(1), cancellationToken); - } - } - }, default) + RunInventoryAsync(cancellationToken), + RunCustomsAsync(cancellationToken) }; try @@ -59,6 +42,57 @@ internal class JobService : BackgroundService catch (Exception) { } } + private async Task RunInventoryAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("RunInventoryAsync"); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + foreach (var agent in await GetAssignedAgentsAsync(cancellationToken)) + { + await agent.SendAsync(new InventoryRequest(), cancellationToken); + } + } + catch (OperationCanceledException) { } + catch (Exception) { } + finally + { + await Task.Delay(TimeSpan.FromHours(1), cancellationToken); + } + } + } + + private async Task RunCustomsAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("RunCustomsAsync"); + + while (cancellationToken.IsCancellationRequested is false) + { + try + { + // check if agent online + if (_agentPool.FirstOrDefault().Value is not AgentSession agent) continue; + + // proxy-send request packet to agent + await agent.SendAsync(new Request + { + RequestId = "test", + RequestData = "Get-Date" + }, cancellationToken); + } + catch (OperationCanceledException) { } + catch (Exception) { } + finally + { + await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); + } + } + + + } + private async ValueTask> GetAssignedAgentsAsync(CancellationToken cancellationToken) { var valid = new List(); diff --git a/src/Server/Insight.Server/appsettings.Development.json b/src/Server/Insight.Server/appsettings.Development.json index 74223d4..859a335 100644 --- a/src/Server/Insight.Server/appsettings.Development.json +++ b/src/Server/Insight.Server/appsettings.Development.json @@ -1,5 +1,10 @@ { "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/Insight.Setup.Windows.csproj b/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj index 5437db6..7cd4052 100644 --- a/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj +++ b/src/Setup/Insight.Setup.Windows/Insight.Setup.Windows.csproj @@ -8,7 +8,7 @@ setup Insight.Setup Insight - 2023.9.21.1 + 2023.11.17.0 diff --git a/src/Updater/Insight.Updater/Insight.Updater.csproj b/src/Updater/Insight.Updater/Insight.Updater.csproj index d277e80..9c3d355 100644 --- a/src/Updater/Insight.Updater/Insight.Updater.csproj +++ b/src/Updater/Insight.Updater/Insight.Updater.csproj @@ -6,7 +6,7 @@ Insight Insight.Updater updater - 2023.9.21.1 + 2023.11.17.0 enable enable diff --git a/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs b/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs index 4dee46e..87a89ae 100644 --- a/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs +++ b/src/Web/Insight.Web/Components/Navbars/Customer.razor.cs @@ -1,4 +1,4 @@ -using Insight.Infrastructure; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; diff --git a/src/Web/Insight.Web/Components/Navbars/Host.razor.cs b/src/Web/Insight.Web/Components/Navbars/Host.razor.cs index de85d62..719b30a 100644 --- a/src/Web/Insight.Web/Components/Navbars/Host.razor.cs +++ b/src/Web/Insight.Web/Components/Navbars/Host.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; diff --git a/src/Web/Insight.Web/Components/Navbars/Main.razor b/src/Web/Insight.Web/Components/Navbars/Main.razor index 3c61848..66261b4 100644 --- a/src/Web/Insight.Web/Components/Navbars/Main.razor +++ b/src/Web/Insight.Web/Components/Navbars/Main.razor @@ -97,9 +97,9 @@ Hosts - @* + Host Groups - *@ + Agents diff --git a/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor b/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor index 68a7295..22dbe6e 100644 --- a/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor +++ b/src/Web/Insight.Web/Components/Providers/DrawerProvider.razor @@ -12,6 +12,5 @@ [CascadingParameter] public IReadOnlyDictionary? RouteValues { get; set; } private bool _open = true; - public void Toggle() => _open = !_open; } \ 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 8f10442..cc2209a 100644 --- a/src/Web/Insight.Web/Constants/Navigation.cs +++ b/src/Web/Insight.Web/Constants/Navigation.cs @@ -514,11 +514,23 @@ public static class Navigation { public const string Index = "management/hostgroups"; public const string Details = "management/hostgroups/{groupId}"; + public const string Hosts = "management/hostgroups/{groupId}/hosts"; + public const string HostsAssign = "management/hostgroups/{groupId}/hosts/assign"; public static string DetailsHref(string? groupId) { return Details.Replace("{groupId}", groupId); } + + public static string HostsHref(string? groupId) + { + return Hosts.Replace("{groupId}", groupId); + } + + public static string HostsAssignHref(string? groupId) + { + return HostsAssign.Replace("{groupId}", groupId); + } } } diff --git a/src/Web/Insight.Web/Insight.Web.csproj b/src/Web/Insight.Web/Insight.Web.csproj index 827c2fb..3f387e8 100644 --- a/src/Web/Insight.Web/Insight.Web.csproj +++ b/src/Web/Insight.Web/Insight.Web.csproj @@ -4,7 +4,7 @@ net7.0 Insight web - 2023.9.21.1 + 2023.11.17.0 Insight.Web enable enable @@ -28,17 +28,10 @@ - - + + - - - - - - - @@ -49,4 +42,10 @@ + + + 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/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/Handlers/ConsoleHandler.cs b/src/Web/Insight.Web/Network/Broker/Handlers/AgentHandler.cs similarity index 58% rename from src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs rename to src/Web/Insight.Web/Network/Broker/Handlers/AgentHandler.cs index fb99333..454202a 100644 --- a/src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs +++ b/src/Web/Insight.Web/Network/Broker/Handlers/AgentHandler.cs @@ -1,22 +1,22 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Vaitr.Bus; -namespace Insight.Web.Network.Handlers; +namespace Insight.Web.Network.Broker.Handlers; -public class ConsoleHandler : IMessageHandler +public class AgentHandler : IMessageHandler { private readonly Bus _bus; - public ConsoleHandler(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) + if (message is Proxy consoleQuery) { await _bus.PublishAsync(consoleQuery, cancellationToken); } diff --git a/src/Web/Insight.Web/Network/WebSession.cs b/src/Web/Insight.Web/Network/Broker/WebSession.cs similarity index 96% rename from src/Web/Insight.Web/Network/WebSession.cs rename to src/Web/Insight.Web/Network/Broker/WebSession.cs index 8dbd7ff..07c6351 100644 --- a/src/Web/Insight.Web/Network/WebSession.cs +++ b/src/Web/Insight.Web/Network/Broker/WebSession.cs @@ -1,8 +1,8 @@ using Insight.Domain.Interfaces; -using Insight.Domain.Messages; +using Insight.Domain.Network; using Vaitr.Network; -namespace Insight.Web.Network; +namespace Insight.Web.Network.Broker; public class WebSession : TcpSession { 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/Pages/Inventory/Hardware/Drives/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Hosts.razor.cs index 4aebfec..f5c9bb7 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Index.razor.cs index ffb2c01..a3125c8 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Drives/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Hosts.razor.cs index a67b32a..76c5458 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Index.razor.cs index a84a376..db7a9b6 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Mainboards/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Hosts.razor.cs index 62b82c9..944a4b2 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Index.razor.cs index 3cc8440..bb7eb16 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Memory/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Hosts.razor.cs index 916305b..80c4d52 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Index.razor.cs index 66e5ce8..2428819 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Processors/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Hosts.razor.cs index 8a7b91b..8d780f0 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Index.razor.cs index f801e5a..dc3f8b5 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Hardware/Videocards/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Hosts.razor.cs index 05b30f8..78fe365 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Index.razor.cs index b73a74a..eb2a6e2 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Addresses/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Hosts.razor.cs index 613d2f6..97d5c8e 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Index.razor.cs index 77ddd23..279110c 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Gateways/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Interfaces/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Interfaces/Index.razor.cs index 39db193..e798c94 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Interfaces/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Interfaces/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Hosts.razor.cs index b2f36eb..f72b171 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Index.razor.cs index ab26365..ec3e608 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Nameservers/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Hosts.razor.cs index 6d6dd8a..ad440b2 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Index.razor.cs index 71f0585..b9f1a11 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Network/Routes/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Hosts.razor.cs index 83e76c2..e9321a0 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; @@ -66,7 +65,7 @@ public partial class Hosts var query = Database.HostSystemGroup() .Aggregate() - .Match(Builders.Filter.Eq(p => p.Name, GroupName)) + .Match(Builders.Filter.Eq(p => p.Name, GroupName)) .Lookup("host", "_host", "_id", "hosts") .Match(new BsonDocument("hosts", new BsonDocument { diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Index.razor.cs index 1758043..64cd2ee 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Groups/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -36,12 +35,12 @@ public partial class Index { try { - var filter = Builders.Filter.Empty; + var filter = Builders.Filter.Empty; if (string.IsNullOrWhiteSpace(Search) is false) { var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); - filter &= Builders.Filter.Regex(x => x.Name, regex); + filter &= Builders.Filter.Regex(x => x.Name, regex); } var query = Database.HostSystemGroup() diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Guests.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Guests.razor.cs index 59e7d15..955178d 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Guests.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Guests.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor index 845e018..d5ee67b 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor @@ -4,7 +4,7 @@ @ref="Container" @bind-Search="Search" Title="@Title" - Breadcrumbs="@Breadcrumbs" + Breadcrumbs="@_breadcrumbs" Data="LoadDataAsync">
diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor.cs index b33f78c..ae40eb5 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; @@ -24,18 +23,19 @@ public partial class Hosts [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private NavigationManager NavigationManager { get; init; } = default!; - private TableContainer? Container { get; set; } + private readonly List _breadcrumbs = new(); + private string Title { get; set; } = Global.Name; - private List Breadcrumbs { get; } = new(); + private TableContainer? Container { get; set; } private string? Search { get; set; } protected override void OnInitialized() { OsName = OsName.UriEscape(true); - Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); - Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true)); - Breadcrumbs.Add(new BreadcrumbItem("OS", href: Navigation.Inventory.Systems.Os.Index)); + _breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); + _breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true)); + _breadcrumbs.Add(new BreadcrumbItem("OS", href: Navigation.Inventory.Systems.Os.Index)); if (string.IsNullOrWhiteSpace(OsName)) { @@ -45,8 +45,9 @@ public partial class Hosts } Title = $"Inventory » OS » {OsName} » Hosts|Insight"; - Breadcrumbs.Add(new BreadcrumbItem(OsName, href: "#", true)); - Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true)); + + _breadcrumbs.Add(new BreadcrumbItem(OsName, href: "#", true)); + _breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true)); } private async Task> LoadDataAsync(TableState state) diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Index.razor.cs index b8c4230..e39bbd5 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Os/Index.razor.cs @@ -1,4 +1,4 @@ -using Insight.Infrastructure; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Printers/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Printers/Index.razor.cs index 78f9f03..1211784 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Printers/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Printers/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Hosts.razor.cs index 094cbae..1e89877 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Index.razor.cs index da8df9a..f286b16 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Services/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Sessions/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Sessions/Index.razor.cs index 26fc249..fc32682 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Sessions/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Sessions/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Hosts.razor.cs index 12bcb88..62e23b7 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Insight.Web.Extensions; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Index.razor.cs index a37d657..95e1fb0 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Software/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/StoragePools/Index.razor b/src/Web/Insight.Web/Pages/Inventory/Systems/StoragePools/Index.razor index 9a25827..6c1104c 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/StoragePools/Index.razor +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/StoragePools/Index.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.StoragePool; +@using static Insight.Domain.Network.Agent.Messages.StoragePool; @inherits ComponentBase .Filter.Eq(p => p.Name, UserName)) + .Match(Builders.Filter.Eq(p => p.Name, UserName)) .Lookup("host", "_host", "_id", "hosts") .Match(new BsonDocument("hosts", new BsonDocument { diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/Users/Index.razor.cs b/src/Web/Insight.Web/Pages/Inventory/Systems/Users/Index.razor.cs index 9f3b7ad..4615ff9 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/Users/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/Users/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -36,12 +35,12 @@ public partial class Index { try { - var filter = Builders.Filter.Empty; + var filter = Builders.Filter.Empty; if (string.IsNullOrWhiteSpace(Search) is false) { var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); - filter &= Builders.Filter.Regex(x => x.Name, regex); + filter &= Builders.Filter.Regex(x => x.Name, regex); } var query = Database.HostSystemUser() diff --git a/src/Web/Insight.Web/Pages/Inventory/Systems/VirtualMaschines/Index.razor b/src/Web/Insight.Web/Pages/Inventory/Systems/VirtualMaschines/Index.razor index dd09a65..e774650 100644 --- a/src/Web/Insight.Web/Pages/Inventory/Systems/VirtualMaschines/Index.razor +++ b/src/Web/Insight.Web/Pages/Inventory/Systems/VirtualMaschines/Index.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.VirtualMaschine; +@using static Insight.Domain.Network.Agent.Messages.VirtualMaschine; @inherits ComponentBase - @if (Account is not null) + @if (_model is not null) { - - - - + + + - @@ -39,12 +39,6 @@ private InputType PasswordInput { get; set; } = InputType.Password; private InputType PasswordConfirmInput { get; set; } = InputType.Password; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } - private void TogglePasswordVisible() { if (PasswordVisible) diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/AccountCreateDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Accounts/AccountCreateDialog.razor.cs index 3ac9bb0..a455825 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/AccountCreateDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Accounts/AccountCreateDialog.razor.cs @@ -14,26 +14,34 @@ public partial class AccountCreateDialog [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; - private IndexCreateDialogModel? Account { get; set; } = new(); + private IndexCreateDialogModel? _model; + + public async Task ToggleAsync() + { + _model = new(); + _visible = !_visible; + + await InvokeAsync(StateHasChanged); + } private async Task SubmitAsync() { - if (Account is null) return; + if (_model is null) return; try { - if (string.IsNullOrWhiteSpace(Account.Email) is false && Account.Email != Account.EmailConfirm) + if (string.IsNullOrWhiteSpace(_model.Email) is false && _model.Email != _model.EmailConfirm) { Notification.Error(Snackbar, "Email not matching"); return; } - if (string.IsNullOrWhiteSpace(Account.Password) is false && Account.Password != Account.PasswordConfirm) + if (string.IsNullOrWhiteSpace(_model.Password) is false && _model.Password != _model.PasswordConfirm) { Notification.Error(Snackbar, "Password not matching"); return; } - var result = await IdentityService.CreateUserAsync(Account.Email, Account.Password); + var result = await IdentityService.CreateUserAsync(_model.Email, _model.Password); if (result.Succeeded is false) { @@ -57,7 +65,7 @@ public partial class AccountCreateDialog } finally { - Account = new(); + _model = new(); _visible = false; } } diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor b/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor index 527303d..d2e1a41 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor @@ -7,11 +7,11 @@ - @if (Account is not null) + @if (_model is not null) { - + - + @@ -28,9 +28,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor.cs index 86c2434..971b1bf 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Accounts/AccountDeleteDialog.razor.cs @@ -1,7 +1,7 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; +using MongoDB.Bson; using MongoDB.Driver; using MudBlazor; @@ -9,22 +9,40 @@ namespace Insight.Web.Pages.Management.Accounts; public partial class AccountDeleteDialog { - [CascadingParameter(Name = "Account")] public InsightUser? Account { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; + private InsightUser? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.User().Find(p => p.Id == new ObjectId(id)).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + private async Task SubmitAsync() { - if (Account is null) return; + if (_model is null) return; try { await Database.User() .DeleteOneAsync(Builders - .Filter.Eq(p => p.Id, Account.Id), + .Filter.Eq(p => p.Id, _model.Id), cancellationToken: default).ConfigureAwait(false); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor b/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor index cbf1bb6..cba4f46 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor @@ -7,11 +7,11 @@ - @if (Account is not null) + @if (_model is not null) { - + - + @@ -28,9 +28,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor.cs index 18ae1c7..f49152e 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Accounts/AccountEditDialog.razor.cs @@ -1,6 +1,7 @@ using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; +using MongoDB.Bson; using MongoDB.Driver; using MudBlazor; @@ -8,28 +9,47 @@ namespace Insight.Web.Pages.Management.Accounts; public partial class AccountEditDialog { - [CascadingParameter(Name = "Account")] public InsightUser? Account { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; - private async Task SubmitAsync() + private InsightUser? _model; + + public async void ToggleAsync(string? id) { - if (Account is null) return; + if (id is null) return; try { - //await Database.Users() - // .UpdateOneAsync(Builders - // .Filter - // .Eq(p => p.Id, Account.Id), Builders - // .Update - // .Set(p => p.Updated, DateTime.Now) - // .Set(p => p.Name, Host.Name) - // .Set(p => p.Description, Host.Description), - // cancellationToken: default); + _model = await Database.User().Find(p => p.Id == new ObjectId(id)).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + + private async Task SubmitAsync() + { + if (_model is null) return; + + try + { + await Database.User() + .UpdateOneAsync(Builders + .Filter + .Eq(p => p.Id, _model.Id), Builders + .Update + .Set(p => p.Email, _model.Email) + .Set(p => p.NormalizedEmail, _model.Email.ToUpperInvariant()) + .Set(p => p.UserName, _model.Email) + .Set(p => p.NormalizedUserName, _model.Email.ToUpperInvariant()), + cancellationToken: default); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Accounts/Details.razor.cs index 826bf30..b511ba2 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Accounts/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor b/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor index a2d9109..dc49207 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor @@ -7,7 +7,7 @@ Title="@_title" Breadcrumbs="@_breadcrumbs" Data="LoadDataAsync" - OnAdd="()=>_createDialog?.Toggle()"> + OnAdd="()=>_createDialog?.ToggleAsync()">
@@ -39,35 +39,21 @@ - + Edit - + Delete - - - - - + + + @code { private AccountCreateDialog? _createDialog; private AccountEditDialog? _editDialog; private AccountDeleteDialog? _deleteDialog; - - private void OnEdit(InsightUser model) - { - _selected = model; - _editDialog?.Toggle(); - } - - private void OnDelete(InsightUser model) - { - _selected = model; - _deleteDialog?.Toggle(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor.cs index 16cf853..9f027c5 100644 --- a/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Accounts/Index.razor.cs @@ -20,7 +20,6 @@ public partial class Index private TableContainer? _container; private string? _search; - private InsightUser? _selected; private readonly string _title = "Accounts|Insight"; private readonly List _breadcrumbs = new() diff --git a/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor b/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor index 96185c3..5afee21 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor @@ -7,11 +7,11 @@ - @if (Agent is not null) + @if (_model is not null) { - + - + @@ -28,9 +28,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor.cs index 39aa909..7f8a61a 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Agents/AgentDeleteDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -9,29 +8,46 @@ namespace Insight.Web.Pages.Management.Agents; public partial class AgentDeleteDialog { - [CascadingParameter(Name = "Agent")] public ViewModel? Agent { get; set; } - [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; + private AgentEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.Agent().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + private async Task SubmitAsync() { - if (Agent is null) return; + if (_model is null) return; try { await Database.Agent() .DeleteOneAsync(Builders - .Filter.Eq(p => p.Id, Agent.Id), + .Filter.Eq(p => p.Id, _model.Id), cancellationToken: default).ConfigureAwait(false); await Database.Host() .UpdateOneAsync(Builders .Filter - .Eq(p => p.Agent, Agent.Id), Builders + .Eq(p => p.Agent, _model.Id), Builders .Update .Set(p => p.Agent, null)) .ConfigureAwait(false); diff --git a/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor b/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor index a002ef3..b252a59 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor @@ -7,12 +7,12 @@ - @if (Agent is not null) + @if (_model is not null) { - @if (Agent.Hosts is not null && Agent.Hosts.Any()) + @if (_model.Hosts is not null && _model.Hosts.Any()) { - + Select @@ -26,7 +26,7 @@ else { - + Assign @@ -40,10 +40,4 @@ @code { private bool _visible; - - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor.cs index 705a30e..20b5c55 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Agents/AgentHostDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -9,7 +8,6 @@ namespace Insight.Web.Pages.Management.Agents; public partial class AgentHostDialog { - [CascadingParameter(Name = "Agent")] public AgentEntity? Agent { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; @@ -18,13 +16,37 @@ public partial class AgentHostDialog private enum Content { Unassign, Assign } - private async Task UnassignAsync() + private AgentEntity? _model; + + public async void ToggleAsync(string? id) { - if (Agent is null) return; + if (id is null) return; try { - await Database.Host().UpdateOneAsync(p => p.Agent == Agent.Id.ToString(), Builders + _model = await Database.Agent() + .Aggregate() + .Match(Builders.Filter.Eq(p => p.Id, id)) + .Lookup(Database.Host(), p => p.Id, p => p.Agent, p => p.Hosts) + .FirstOrDefaultAsync(default); + + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + + private async Task UnassignAsync() + { + if (_model is null) return; + + try + { + await Database.Host().UpdateOneAsync(p => p.Agent == _model.Id, Builders .Update.Set(p => p.Agent, null)); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Agents/Details.razor b/src/Web/Insight.Web/Pages/Management/Agents/Details.razor index 414c576..259685a 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/Details.razor +++ b/src/Web/Insight.Web/Pages/Management/Agents/Details.razor @@ -13,7 +13,7 @@ - + @@ -75,9 +75,7 @@ - - - + @code { private AgentHostDialog? _hostDialog; diff --git a/src/Web/Insight.Web/Pages/Management/Agents/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Agents/Details.razor.cs index fabc280..0600f04 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Agents/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor b/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor index ed2f08e..7914e2f 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor +++ b/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor @@ -7,7 +7,7 @@ Title="@Title" Breadcrumbs="@Breadcrumbs" Data="LoadDataAsync" - OnAdd="()=>_createDialog?.Toggle()"> + OnAdd="()=>_createDialog?.ToggleAsync()">
diff --git a/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor.cs b/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor.cs index 7c05392..4cc0678 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Agents/HostAssign.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Agents/Index.razor b/src/Web/Insight.Web/Pages/Management/Agents/Index.razor index b73b5d7..8737306 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Agents/Index.razor @@ -57,7 +57,7 @@ - + Delete @@ -79,9 +79,7 @@ - - - + @code { private AgentDeleteDialog? _deleteDialog; diff --git a/src/Web/Insight.Web/Pages/Management/Agents/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Agents/Index.razor.cs index 5a33e21..ff43f78 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Agents/Index.razor.cs @@ -137,17 +137,6 @@ public partial class Index await Container.RefreshAsync().ConfigureAwait(false); } - private void OnDelete(ViewModel model) - { - Model = new ViewModel - { - Id = model.Id, - Serial = model?.Serial - }; - - _deleteDialog?.Toggle(); - } - private bool _filter; private bool Filter { diff --git a/src/Web/Insight.Web/Pages/Management/Agents/Logs.razor.cs b/src/Web/Insight.Web/Pages/Management/Agents/Logs.razor.cs index 412cff4..d18c72c 100644 --- a/src/Web/Insight.Web/Pages/Management/Agents/Logs.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Agents/Logs.razor.cs @@ -1,5 +1,4 @@ using Insight.Domain.Enums; -using Insight.Infrastructure; using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; diff --git a/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor b/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor index 7017709..3dc1694 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor @@ -7,13 +7,13 @@ - @if (Customer is not null) + @if (_model is not null) { - + - - + + @@ -30,9 +30,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor.cs index e79fa5b..6fa8b0e 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/CustomerCreateDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -15,17 +14,26 @@ public partial class CustomerCreateDialog [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; - private CustomerEntity? Customer { get; set; } = new(); + private CustomerEntity? _model; + + public async void ToggleAsync() + { + _model = new(); + _visible = !_visible; + + await InvokeAsync(StateHasChanged); + } private async Task SubmitAsync() { - if (Customer is null) return; - Customer.Insert = DateTime.Now; + if (_model is null) return; + + _model.Insert = DateTime.Now; try { await Database.Customer() - .InsertOneAsync(Customer, cancellationToken: default) + .InsertOneAsync(_model, cancellationToken: default) .ConfigureAwait(false); Notification.Success(Snackbar); @@ -45,7 +53,6 @@ public partial class CustomerCreateDialog } finally { - Customer = new(); _visible = false; } } diff --git a/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor b/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor index 8cbf1c7..a0ac157 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor @@ -7,12 +7,12 @@ - @if (Customer is not null) + @if (_model is not null) { - + - - + + diff --git a/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor.cs index 8451293..11e97bc 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/CustomerDeleteDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -9,22 +8,40 @@ namespace Insight.Web.Pages.Management.Customers; public partial class CustomerDeleteDialog { - [CascadingParameter(Name = "Customer")] public CustomerEntity? Customer { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; + private CustomerEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.Customer().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + private async Task SubmitAsync() { - if (Customer is null) return; + if (_model is null) return; try { await Database.Customer() .DeleteOneAsync(Builders - .Filter.Eq(p => p.Id, Customer.Id), + .Filter.Eq(p => p.Id, _model.Id), cancellationToken: default).ConfigureAwait(false); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor b/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor index 6b66eff..ac7ba6b 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor @@ -7,21 +7,13 @@ - @if (Customer is not null) + @if (_model is not null) { - + - @* - - - - - - - *@ - - + + @@ -39,9 +31,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor.cs index bd25528..30e134d 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/CustomerEditDialog.razor.cs @@ -1,56 +1,71 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; using MudBlazor; -namespace Insight.Web.Pages.Management.Customers +namespace Insight.Web.Pages.Management.Customers; + +public partial class CustomerEditDialog { - public partial class CustomerEditDialog + [Parameter] public EventCallback OnChanges { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private ILogger Logger { get; init; } = default!; + + private CustomerEntity? _model; + + public async void ToggleAsync(string? id) { - [CascadingParameter(Name = "Customer")] public CustomerEntity? Customer { get; set; } - [Parameter] public EventCallback OnChanges { get; set; } + if (id is null) return; - [Inject] private IMongoDatabase Database { get; init; } = default!; - [Inject] private ISnackbar Snackbar { get; init; } = default!; - [Inject] private ILogger Logger { get; init; } = default!; - - private async Task SubmitAsync() + try { - if (Customer is null) return; - Customer.Update = DateTime.Now; + _model = await Database.Customer().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } - try + await InvokeAsync(StateHasChanged); + } + + private async Task SubmitAsync() + { + if (_model is null) return; + + try + { + await Database.Customer() + .UpdateOneAsync(Builders + .Filter + .Eq(p => p.Id, _model.Id), Builders + .Update + .Set(p => p.Update, DateTime.Now) + .Set(p => p.Name, _model.Name) + .Set(p => p.Tag, _model.Tag), default).ConfigureAwait(false); + + Notification.Success(Snackbar); + + if (OnChanges.HasDelegate) { - await Database.Customer() - .UpdateOneAsync(Builders - .Filter - .Eq(p => p.Id, Customer.Id), Builders - .Update - .Set(p => p.Update, DateTime.Now) - .Set(p => p.Name, Customer.Name) - .Set(p => p.Tag, Customer.Tag), default).ConfigureAwait(false); - - Notification.Success(Snackbar); - - if (OnChanges.HasDelegate) + await InvokeAsync(async () => { - await InvokeAsync(async () => - { - await OnChanges.InvokeAsync(this); - }); - } - } - catch (Exception ex) - { - Notification.Error(Snackbar); - Logger.LogError(ex.ToString()); - } - finally - { - _visible = false; + await OnChanges.InvokeAsync(this); + }); } } + catch (Exception ex) + { + Notification.Error(Snackbar); + Logger.LogError(ex.ToString()); + } + finally + { + _visible = false; + } } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Customers/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/Details.razor.cs index 4414756..56973ef 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Customers/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/Hosts.razor.cs index 0877312..00e2a97 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/Hosts.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/Hosts.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Customers/HostsAssign.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/HostsAssign.razor.cs index 73c7405..f237eef 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/HostsAssign.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/HostsAssign.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Customers/Index.razor b/src/Web/Insight.Web/Pages/Management/Customers/Index.razor index 0872a55..6e8bbeb 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Customers/Index.razor @@ -1,12 +1,12 @@ @inherits ComponentBase + OnAdd="()=>_createDialog?.ToggleAsync()">
@@ -49,20 +49,18 @@ - + Edit - + Delete - - - - - + + + @code{ private CustomerCreateDialog? _createDialog; diff --git a/src/Web/Insight.Web/Pages/Management/Customers/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Customers/Index.razor.cs index c17083e..6e254a7 100644 --- a/src/Web/Insight.Web/Pages/Management/Customers/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Customers/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -17,18 +16,17 @@ public partial class Index [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; - private TableContainer? Container { get; set; } - private string Title { get; set; } = Global.Name; - private List Breadcrumbs { get; } = new(); - private string? Search { get; set; } + private readonly List _breadcrumbs = new(); - private CustomerEntity? Model { get; set; } + private TableContainer? _container; + private string _title = Global.Name; + private string? _search; protected override void OnInitialized() { - Title = "Customers|Insight"; - Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); - Breadcrumbs.Add(new BreadcrumbItem("Customers", href: Navigation.Management.Customers.Index, true)); + _title = "Customers|Insight"; + _breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); + _breadcrumbs.Add(new BreadcrumbItem("Customers", href: Navigation.Management.Customers.Index, true)); } private async Task> LoadDataAsync(TableState state) @@ -37,9 +35,9 @@ public partial class Index { var filter = Builders.Filter.Empty; - if (string.IsNullOrWhiteSpace(Search) is false) + if (string.IsNullOrWhiteSpace(_search) is false) { - var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); + var regex = new BsonRegularExpression(new Regex(_search, RegexOptions.IgnoreCase)); filter &= Builders.Filter.Regex(x => x.Name, regex) | Builders.Filter.Regex(x => x.Tag, regex) | Builders.Filter.Regex(x => x.Hosts, regex); @@ -85,40 +83,7 @@ public partial class Index private async Task OnRefreshAsync() { - if (Container is null) return; - await Container.RefreshAsync().ConfigureAwait(false); - } - - private void OnCreate() - { - _createDialog?.Toggle(); - } - - private void OnEdit(CustomerEntity model) - { - Model = new CustomerEntity - { - Id = model.Id, - Insert = model?.Insert, - Update = model?.Update, - Name = model?.Name, - Tag = model?.Tag - }; - - _editDialog?.Toggle(); - } - - private void OnDelete(CustomerEntity model) - { - Model = new CustomerEntity - { - Id = model.Id, - Insert = model?.Insert, - Update = model?.Update, - Name = model?.Name, - Tag = model?.Tag - }; - - _deleteDialog?.Toggle(); + if (_container is null) return; + await _container.RefreshAsync().ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor new file mode 100644 index 0000000..a803c8f --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor @@ -0,0 +1,20 @@ +@inherits ComponentBase + + + + + @if (_model is not null) + { + + + + @if (string.IsNullOrWhiteSpace(_model?.Description) is false) + { + + + + } + } + + + \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor.cs new file mode 100644 index 0000000..d6ba5ac --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/Details.razor.cs @@ -0,0 +1,57 @@ +using Insight.Infrastructure.Entities; +using Insight.Web.Constants; +using Microsoft.AspNetCore.Components; +using MongoDB.Driver; +using MudBlazor; + +namespace Insight.Web.Pages.Management.HostGroups; + +[Route(Navigation.Management.HostGroups.Details)] +public partial class Details +{ + [Parameter] public string? GroupId { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private NavigationManager NavigationManager { get; init; } = default!; + + private readonly List _breadcrumbs = new(); + + private string _title = Global.Name; + private HostGroupEntity? _model; + + private async Task LoadDataAsync() + { + _model = null; + + _breadcrumbs.Clear(); + _breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); + _breadcrumbs.Add(new BreadcrumbItem("Host Groups", href: Navigation.Management.HostGroups.Index)); + + StateHasChanged(); + + if (string.IsNullOrWhiteSpace(GroupId)) + { + Notification.Error(Snackbar, "Not Found"); + NavigationManager.NavigateTo(Navigation.Management.HostGroups.Index); + return; + } + + _model = await Database.HostGroup() + .Aggregate() + .Match(Builders.Filter.Eq(p => p.Id, GroupId)) + .FirstOrDefaultAsync(default); + + if (_model is null) + { + Notification.Error(Snackbar, "Not Found"); + NavigationManager.NavigateTo(Navigation.Management.HostGroups.Index); + return; + } + + _title = $"Host Groups » {_model.Name}|Insight"; + _breadcrumbs.Add(new BreadcrumbItem(_model.Name, href: "#", true)); + + await InvokeAsync(StateHasChanged); + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor new file mode 100644 index 0000000..336f04d --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor @@ -0,0 +1,38 @@ +@inherits ComponentBase + + +
+ + + Customer + + + + + Host + + +
+ + + + @context?.CustomerName + + + + + @context?.HostName + + + + + + Assign + + +
\ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor.cs new file mode 100644 index 0000000..1956cf1 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostAssign.razor.cs @@ -0,0 +1,178 @@ +using Insight.Infrastructure.Entities; +using Insight.Web.Components.Containers; +using Insight.Web.Constants; +using Microsoft.AspNetCore.Components; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using MudBlazor; +using System.Text.RegularExpressions; +using SortDirection = MudBlazor.SortDirection; + +namespace Insight.Web.Pages.Management.HostGroups; + +[Route(Navigation.Management.HostGroups.HostsAssign)] +public partial class HostAssign +{ + [Parameter] public string? GroupId { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private NavigationManager NavigationManager { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + + private TableContainer? Container { get; set; } + private string Title { get; set; } = Global.Name; + private List Breadcrumbs { get; } = new(); + private string? Search { get; set; } + + protected override async Task OnInitializedAsync() + { + if (string.IsNullOrWhiteSpace(GroupId) || ObjectId.TryParse(GroupId, out var groupId) is false) + { + Notification.Error(Snackbar, "Not Found"); + NavigationManager.NavigateTo(Navigation.Management.HostGroups.Index); + return; + } + + var hostgroup = await Database.HostGroup() + .Aggregate() + .Match(Builders.Filter.Eq(p => p.Id, GroupId)) + .FirstOrDefaultAsync(default); + + if (hostgroup is null) + { + Notification.Error(Snackbar, "Not Found"); + NavigationManager.NavigateTo(Navigation.Management.HostGroups.Index); + return; + } + + Title = $"Host Groups » {hostgroup.Name} » Hosts » Assign|Insight"; + Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); + Breadcrumbs.Add(new BreadcrumbItem("Host Groups", href: Navigation.Management.HostGroups.Index)); + Breadcrumbs.Add(new BreadcrumbItem(hostgroup.Name, href: Navigation.Management.HostGroups.DetailsHref(GroupId))); + Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: Navigation.Management.HostGroups.HostsHref(GroupId))); + Breadcrumbs.Add(new BreadcrumbItem("Assign", href: "#", true)); + } + + private async Task> LoadDataAsync(TableState state) + { + try + { + var search = Builders.Filter.Empty; + + if (string.IsNullOrWhiteSpace(Search) is false) + { + var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); + search &= Builders.Filter.Regex("customerName", regex) | + Builders.Filter.Regex("hostName", regex); + } + + var query = Database.Host() + .Aggregate() + .AppendStage(new BsonDocument("$lookup", new BsonDocument + { + { "from", "hostgroup_host" }, + { "localField", "_id" }, + { "foreignField", "_host" }, + { "as", "map" } + })) + .AppendStage(new BsonDocument("$project", new BsonDocument + { + { "_id", 0 }, + { "hostId", "$_id" }, + { "hostName", "$name" }, + { "customerId", "$_customer" }, + { "hostgroup", new BsonDocument("$first", "$map._hostgroup") } + })) + .AppendStage( + new BsonDocument("$match", + new BsonDocument("hostgroup", new BsonDocument("$ne", new ObjectId(GroupId)))) + ) + .AppendStage(new BsonDocument("$lookup", new BsonDocument + { + { "from", "customer" }, + { "localField", "customerId" }, + { "foreignField", "_id" }, + { "as", "customerName" } + })) + .AppendStage(new BsonDocument("$addFields", + new BsonDocument("customerName", new BsonDocument("$first", "$customerName.name")))) + .Match(search) + .Sort(state.SortDirection switch + { + SortDirection.Ascending => state.SortLabel switch + { + "Customer" => Builders.Sort.Ascending("customer.name"), + "Name" => Builders.Sort.Ascending("name"), + _ => null + }, + SortDirection.Descending => state.SortLabel switch + { + "Customer" => Builders.Sort.Descending("customer.name"), + "Name" => Builders.Sort.Descending("name"), + _ => null + }, + _ => Builders.Sort.Ascending("customer.name") + }); + + var countResult = await query.Count().FirstOrDefaultAsync(default); + var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default); + + return new TableData() + { + TotalItems = countResult is null ? 0 : (int)countResult.Count, + Items = itemResult.Select(x => BsonSerializer.Deserialize(x)) + }; + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + + Notification.Error(Snackbar); + return new TableData(); + } + } + + private async Task OnAssignAsync(string? hostId) + { + try + { + await Database.HostGroupHost() + .InsertOneAsync(new HostGroupHostEntity + { + Insert = DateTime.Now, + HostGroup = GroupId, + Host = hostId + }, null, default); + + Container?.RefreshAsync(); + + Notification.Success(Snackbar); + } + catch (Exception) + { + Notification.Error(Snackbar); + } + finally + { + NavigationManager?.NavigateTo(Navigation.Management.HostGroups.HostsHref(GroupId)); + } + } + + [BsonIgnoreExtraElements] + public class ViewModel + { + [BsonElement("customerId"), BsonRepresentation(BsonType.ObjectId)] + public string? CustomerId { get; set; } + + [BsonElement("customerName")] + public string? CustomerName { get; set; } + + [BsonElement("hostId"), BsonRepresentation(BsonType.ObjectId)] + public string? HostId { get; set; } + + [BsonElement("hostName")] + public string? HostName { get; set; } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor new file mode 100644 index 0000000..c3ac381 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor @@ -0,0 +1,33 @@ +@using MongoDB.Bson; + + + + + Create Host Group + + + + @if (_model is not null) + { + + + + + + + + + Cancel + + + Create + + + + } + + + +@code { + private bool _visible; +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor.cs new file mode 100644 index 0000000..678fb11 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupCreateDialog.razor.cs @@ -0,0 +1,61 @@ +using Insight.Infrastructure.Entities; +using Insight.Web.Constants; +using Microsoft.AspNetCore.Components; +using MongoDB.Driver; +using MudBlazor; + +namespace Insight.Web.Pages.Management.HostGroups; + +public partial class HostGroupCreateDialog +{ + [Parameter] public EventCallback OnChanges { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private ILogger Logger { get; init; } = default!; + + private HostGroupEntity? _model; + + public async void ToggleAsync() + { + _model = new(); + _visible = !_visible; + + await InvokeAsync(StateHasChanged); + } + + private async Task SubmitAsync() + { + try + { + await Database.HostGroup() + .InsertOneAsync(new HostGroupEntity + { + Insert = DateTime.Now, + Name = _model?.Name, + Description = _model?.Description, + }, cancellationToken: default) + .ConfigureAwait(false); + + Notification.Success(Snackbar); + + if (OnChanges.HasDelegate) + { + await InvokeAsync(async () => + { + await OnChanges.InvokeAsync(this); + }); + } + } + catch (Exception ex) + { + Notification.Error(Snackbar); + Logger.LogError(ex.ToString()); + } + finally + { + _model = new(); + _visible = false; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor new file mode 100644 index 0000000..9d202af --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor @@ -0,0 +1,32 @@ +@using MongoDB.Bson; + + + + + Delete Host Group + + + + @if (_model is not null) + { + + + + + + + + Cancel + + + Delete + + + + } + + + +@code { + private bool _visible; +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor.cs new file mode 100644 index 0000000..c4e0670 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupDeleteDialog.razor.cs @@ -0,0 +1,66 @@ +using Insight.Infrastructure.Entities; +using Insight.Web.Constants; +using Microsoft.AspNetCore.Components; +using MongoDB.Driver; +using MudBlazor; + +namespace Insight.Web.Pages.Management.HostGroups; + +public partial class HostGroupDeleteDialog +{ + [Parameter] public EventCallback OnChanges { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private ILogger Logger { get; init; } = default!; + + private HostGroupEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.HostGroup().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + + private async Task SubmitAsync() + { + if (_model is null) return; + + try + { + await Database.HostGroup().DeleteOneAsync(Builders + .Filter.Eq(p => p.Id, _model.Id), + cancellationToken: default).ConfigureAwait(false); + + Notification.Success(Snackbar); + + if (OnChanges.HasDelegate) + { + await InvokeAsync(async () => + { + await OnChanges.InvokeAsync(this); + }); + } + } + catch (Exception ex) + { + Notification.Error(Snackbar); + Logger.LogError(ex.ToString()); + } + finally + { + _visible = false; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor new file mode 100644 index 0000000..234a878 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor @@ -0,0 +1,41 @@ +@using MongoDB.Bson; + + + + + Edit Host Group + + + + @if (_model is not null) + { + + + @* + + + + + + + *@ + + + + + + + Cancel + + + Edit + + + + } + + + +@code { + private bool _visible; +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor.cs new file mode 100644 index 0000000..0eea264 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/HostGroupEditDialog.razor.cs @@ -0,0 +1,71 @@ +using Insight.Infrastructure.Entities; +using Insight.Web.Constants; +using Microsoft.AspNetCore.Components; +using MongoDB.Driver; +using MudBlazor; + +namespace Insight.Web.Pages.Management.HostGroups; + +public partial class HostGroupEditDialog +{ + [Parameter] public EventCallback OnChanges { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private ILogger Logger { get; init; } = default!; + + private HostGroupEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.HostGroup().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + + private async Task SubmitAsync() + { + if (_model is null) return; + + try + { + await Database.HostGroup().UpdateOneAsync(Builders + .Filter + .Eq(p => p.Id, _model.Id), Builders + .Update + .Set(p => p.Update, DateTime.Now) + .Set(p => p.Name, _model.Name) + .Set(p => p.Description, _model.Description), + cancellationToken: default); + + Notification.Success(Snackbar); + + if (OnChanges.HasDelegate) + { + await InvokeAsync(async () => + { + await OnChanges.InvokeAsync(this); + }); + } + } + catch (Exception ex) + { + Notification.Error(Snackbar); + Logger.LogError(ex.ToString()); + } + finally + { + _visible = false; + } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor new file mode 100644 index 0000000..e2ad8c8 --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor @@ -0,0 +1,39 @@ +@inherits ComponentBase + + +
+ + + Customer + + + + + Host + + +
+ + + + @context?.Customers?.Name + + + + + @context?.Hosts?.Name + + + + + + Unassign + + +
\ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor.cs new file mode 100644 index 0000000..7b856fc --- /dev/null +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/Hosts.razor.cs @@ -0,0 +1,157 @@ +using Insight.Infrastructure.Entities; +using Insight.Web.Components.Containers; +using Insight.Web.Constants; +using Microsoft.AspNetCore.Components; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using MudBlazor; +using System.Text.RegularExpressions; +using SortDirection = MudBlazor.SortDirection; + +namespace Insight.Web.Pages.Management.HostGroups; + +[Route(Navigation.Management.HostGroups.Hosts)] +public partial class Hosts +{ + [Parameter] public string? GroupId { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private NavigationManager NavigationManager { get; init; } = default!; + + private TableContainer? _container; + private string _title = Global.Name; + private string? _search; + private HostGroupEntity? _model; + + private readonly List _breadcrumbs = new(); + + protected override async Task OnInitializedAsync() + { + _breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); + _breadcrumbs.Add(new BreadcrumbItem("Host Groups", href: Navigation.Management.HostGroups.Index)); + + if (string.IsNullOrWhiteSpace(GroupId)) + { + Notification.Error(Snackbar, "Not Found"); + NavigationManager.NavigateTo(Navigation.Management.HostGroups.Index); + return; + } + + _model = await Database.HostGroup() + .Aggregate() + .Match(Builders.Filter.Eq(p => p.Id, GroupId)) + .FirstOrDefaultAsync(default); + + if (_model is null) + { + Notification.Error(Snackbar, "Not Found"); + NavigationManager.NavigateTo(Navigation.Management.HostGroups.Index); + return; + } + + _title = $"Host Groups » {_model.Name} » Hosts|Insight"; + + _breadcrumbs.Add(new BreadcrumbItem(_model.Name, href: Navigation.Management.HostGroups.DetailsHref(GroupId))); + _breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true)); + + await InvokeAsync(StateHasChanged); + } + + private async Task> LoadDataAsync(TableState state) + { + try + { + var filter = Builders.Filter.Empty; + + if (string.IsNullOrWhiteSpace(_search) is false) + { + var regex = new BsonRegularExpression(new Regex(_search, RegexOptions.IgnoreCase)); + filter &= Builders.Filter.Regex("customer.name", regex) | + Builders.Filter.Regex("host.name", regex); + } + + var query = Database.HostGroupHost() + .Aggregate() + .Match(Builders.Filter.Eq(p => p.HostGroup, GroupId)) + .Lookup("host", "_host", "_id", "hosts") + .Match(new BsonDocument("hosts", new BsonDocument + { + { "$exists", true }, + { "$ne", new BsonArray() } + })) + .Lookup("customer", "hosts._customer", "_id", "customers") + .Project(new BsonDocument + { + { "_id", 0 }, + { "host", new BsonDocument("$first", "$hosts") }, + { "customer", new BsonDocument("$first", "$customers") } + }) + .Match(filter) + .Sort(state.SortDirection switch + { + SortDirection.Ascending => state.SortLabel switch + { + "Customer" => Builders.Sort.Ascending("customer.name"), + "Host" => Builders.Sort.Ascending("host.name"), + _ => null + }, + SortDirection.Descending => state.SortLabel switch + { + "Customer" => Builders.Sort.Descending("customer.name"), + "Host" => Builders.Sort.Descending("host.name"), + _ => null + }, + _ => Builders.Sort.Ascending("customer.name") + }); + + var countResult = await query.Count().FirstOrDefaultAsync(default); + var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default); + + return new TableData() + { + TotalItems = countResult is null ? 0 : (int)countResult.Count, + Items = itemResult.Select(x => BsonSerializer.Deserialize(x)) + }; + } + catch (Exception) + { + Notification.Error(Snackbar); + return new TableData(); + } + } + + private async Task OnUnassignAsync(string? hostId) + { + try + { + await Database.HostGroupHost() + .DeleteOneAsync(Builders + .Filter.Eq(p => p.Host, hostId), default); + + _container?.RefreshAsync(); + + Notification.Success(Snackbar); + } + catch (Exception) + { + Notification.Error(Snackbar); + } + finally + { + NavigationManager?.NavigateTo(Navigation.Management.HostGroups.HostsHref(GroupId)); + } + } + + [BsonIgnoreExtraElements] + public class ViewModel + { + [BsonElement("customer")] + public CustomerEntity? Customers { get; set; } + + [BsonElement("host")] + public HostEntity? Hosts { get; set; } + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor b/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor index 4185d90..ff5b4ec 100644 --- a/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor @@ -1,14 +1,12 @@ @inherits ComponentBase + OnAdd="()=>_createDialog?.ToggleAsync()">
@@ -16,128 +14,49 @@ - - Customer + + Description - - Agent - - - - - Online + + Hosts
- + @context?.Name - - - @context.Customer?.Name - - - - - @context?.Agent?.Serial - - - - - @(context.Online ? "Online" : "Offline") + + + @context.Description + + + @context.Hosts + + - + Edit - + Delete
- - - - Unassigned Customer - - - Unassigned Agent - - - - - Reset - - - Done - - - + + + - - - - - - - - - - - - Cancel - - - Create - - - - - - - - - - - - - - - - Cancel - - - Edit - - - - - - - - - - - - - - - - - - - Cancel - - - Delete - - - \ No newline at end of file + @code { + private HostGroupCreateDialog? _createDialog; + private HostGroupEditDialog? _editDialog; + private HostGroupDeleteDialog? _deleteDialog; +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor.cs index 3459d9a..2e70cd0 100644 --- a/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/HostGroups/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -19,131 +18,121 @@ public partial class Index [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; - private TableContainer? Container { get; set; } - private string Title { get; set; } = Global.Name; - private List Breadcrumbs { get; } = new(); - private string? Search { get; set; } + private readonly string _title = "Host Groups|Insight"; + private readonly List _breadcrumbs = new(); - private IndexViewModel? Model { get; set; } + private TableContainer? _container; + private string? _search; protected override void OnInitialized() { - Title = "Host Groups|Insight"; - Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); - Breadcrumbs.Add(new BreadcrumbItem("Host Groups", href: Navigation.Management.HostGroups.Index, true)); + _breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home)); + _breadcrumbs.Add(new BreadcrumbItem("Host Groups", href: Navigation.Management.HostGroups.Index, true)); } private async Task> LoadDataAsync(TableState state) { try { - var filter = Builders.Filter.Empty; - - if (FilterUnassignedCustomer) - { - filter &= Builders.Filter.Eq(p => p.Customer, null); - } - - if (FilterUnassignedAgent) - { - filter &= Builders.Filter.Eq(p => p.Agent, null); - } - + var filter = Builders.Filter.Empty; var search = Builders.Filter.Empty; - if (string.IsNullOrWhiteSpace(Search) is false) + if (string.IsNullOrWhiteSpace(_search) is false) { - var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); + var regex = new BsonRegularExpression(new Regex(_search, RegexOptions.IgnoreCase)); search &= Builders.Filter.Regex("name", regex) | - Builders.Filter.Regex("description", regex) | - Builders.Filter.Regex("customer.name", regex) | - Builders.Filter.Regex("agent.serial", regex); + Builders.Filter.Regex("description", regex); } - var query = Database.Host() + var query = Database.HostGroup() .Aggregate() .Match(filter) .AppendStage(new BsonDocument("$lookup", new BsonDocument { - { "from", "agent" }, - { "localField", "agent" }, - { "foreignField", "_id" }, - { "pipeline", new BsonArray - { - new BsonDocument("$project", new BsonDocument - { - { "serial", 1 }, - { "last_activity", 1 } - }) - } - }, - { "as", "agents" } + { "from", "hostgroup_host" }, + { "localField", "_id" }, + { "foreignField", "_hostgroup" }, + //{ "pipeline", new BsonArray + // { + // new BsonDocument("$project", new BsonDocument + // { + // { "serial", 1 }, + // { "last_activity", 1 } + // }) + // } + //}, + { "as", "hosts" } })) - .AppendStage(new BsonDocument("$lookup", new BsonDocument - { - { "from", "customer" }, - { "localField", "customer" }, - { "foreignField", "_id" }, - { "pipeline", new BsonArray - { - new BsonDocument("$project", - new BsonDocument("name", 1)) - } - }, - { "as", "customers" } - })) - .AppendStage(new BsonDocument("$project", new BsonDocument + .Project(new BsonDocument { + { "_id", 1 }, { "name", 1 }, { "description", 1 }, - { "agent", new BsonDocument("$first", "$agents") }, - { "customer", new BsonDocument("$first", "$customers") } - })) - .AppendStage(new BsonDocument("$addFields", - new BsonDocument("online_diff", - new BsonDocument("$subtract", new BsonArray - { - DateTime.Now, - "$agent.last_activity" - })) - )) - .AppendStage(new BsonDocument("$addFields", - new BsonDocument("online", - new BsonDocument("$and", new BsonArray - { - new BsonDocument("$cond", - new BsonArray - { - new BsonDocument("$isNumber", - new BsonArray - { - "$online_diff" - }), 1, 0 - }), new BsonDocument("$lt", - new BsonArray - { - "$online_diff", - 10000 - }) - })) - )) + { "hosts", new BsonDocument("$size", "$hosts") } + }) + //.AppendStage(new BsonDocument("$lookup", new BsonDocument + //{ + // { "from", "customer" }, + // { "localField", "customer" }, + // { "foreignField", "_id" }, + // { "pipeline", new BsonArray + // { + // new BsonDocument("$project", + // new BsonDocument("name", 1)) + // } + // }, + // { "as", "customers" } + //})) + //.AppendStage(new BsonDocument("$project", new BsonDocument + //{ + // { "name", 1 }, + // { "description", 1 }, + // { "hosts", new BsonDocument("$first", "$agents") }, + // { "customer", new BsonDocument("$first", "$customers") } + //})) + //.AppendStage(new BsonDocument("$addFields", + // new BsonDocument("online_diff", + // new BsonDocument("$subtract", new BsonArray + // { + // DateTime.Now, + // "$agent.last_activity" + // })) + //)) + //.AppendStage(new BsonDocument("$addFields", + // new BsonDocument("online", + // new BsonDocument("$and", new BsonArray + // { + // new BsonDocument("$cond", + // new BsonArray + // { + // new BsonDocument("$isNumber", + // new BsonArray + // { + // "$online_diff" + // }), 1, 0 + // }), new BsonDocument("$lt", + // new BsonArray + // { + // "$online_diff", + // 10000 + // }) + // })) + //)) .Match(search) .Sort(state.SortDirection switch { SortDirection.Ascending => state.SortLabel switch { "Name" => Builders.Sort.Ascending("name"), - "Customer" => Builders.Sort.Ascending("customer.name"), - "Agent" => Builders.Sort.Ascending("agent.serial"), - "Online" => Builders.Sort.Ascending("online"), + "Description" => Builders.Sort.Ascending("description"), + "Hosts" => Builders.Sort.Ascending("hosts"), _ => null }, SortDirection.Descending => state.SortLabel switch { "Name" => Builders.Sort.Descending("name"), - "Customer" => Builders.Sort.Descending("customer.name"), - "Agent" => Builders.Sort.Descending("agent.serial"), - "Online" => Builders.Sort.Descending("online"), + "Description" => Builders.Sort.Descending("description"), + "Hosts" => Builders.Sort.Descending("hosts"), _ => null }, _ => Builders.Sort.Ascending("name") @@ -165,239 +154,12 @@ public partial class Index } } - - private bool _create; - public bool Create + private async Task OnRefreshAsync() { - get - { - return _create; - } - set - { - if (value != _create) - { - _create = value; - - if (value) - { - Model = new(); - } - - StateHasChanged(); - } - } + if (_container is null) return; + await _container.RefreshAsync().ConfigureAwait(false); } - private async Task OnCreateSubmitAsync() - { - try - { - await Database.Host() - .InsertOneAsync(new HostEntity - { - Name = Model?.Name, - Description = Model?.Description, - }, cancellationToken: default); - - Container?.RefreshAsync(); - - Notification.Success(Snackbar); - } - catch (Exception) - { - Notification.Error(Snackbar); - } - finally - { - Create = false; - } - } - - - private bool _edit; - public bool Edit - { - get - { - return _edit; - } - set - { - if (value != _edit) - { - _edit = value; - StateHasChanged(); - - Container?.RefreshAsync(); - } - } - } - - private void OnEdit(IndexViewModel model) - { - Model = model; - Edit = true; - } - - private async Task OnEditSubmitAsync() - { - try - { - await Database.Host() - .UpdateOneAsync(Builders - .Filter - .Eq(p => p.Id, Model?.Id?.ToString()), Builders - .Update - .Set(p => p.Name, Model.Name) - .Set(p => p.Description, Model.Description), - cancellationToken: default); - - if (Container is not null) - { - _ = Container.RefreshAsync(); - } - - Notification.Success(Snackbar); - } - catch (Exception) - { - Notification.Error(Snackbar); - } - finally - { - Edit = false; - } - } - - - private bool _delete; - public bool Delete - { - get - { - return _delete; - } - set - { - if (value != _delete) - { - _delete = value; - StateHasChanged(); - - Container?.RefreshAsync(); - } - } - } - - private void OnDelete(IndexViewModel model) - { - Model = model; - Delete = true; - } - - private async Task OnDeleteSubmitAsync() - { - try - { - await Database.Host() - .DeleteOneAsync(Builders - .Filter.Eq(p => p.Id, Model?.Id?.ToString()), - cancellationToken: default); - - Notification.Success(Snackbar); - } - catch (Exception) - { - Notification.Error(Snackbar); - } - finally - { - Delete = false; - } - - if (Container is not null) - { - _ = Container.RefreshAsync(); - } - } - - - private bool _filter; - private bool Filter - { - get - { - return _filter; - } - set - { - if (value != _filter) - { - _filter = value; - StateHasChanged(); - } - } - } - - private bool Filtered => FilterUnassignedCustomer || FilterUnassignedAgent; - - private bool _filterUnassignedCustomer; - public bool FilterUnassignedCustomer - { - get - { - return _filterUnassignedCustomer; - } - set - { - if (value != _filterUnassignedCustomer) - { - _filterUnassignedCustomer = value; - - Container?.RefreshAsync(); - - if (value) - { - Notification.Warning(Snackbar, "Filtered"); - } - } - } - } - - private bool _filterUnassignedAgent; - public bool FilterUnassignedAgent - { - get - { - return _filterUnassignedAgent; - } - set - { - if (value != _filterUnassignedAgent) - { - _filterUnassignedAgent = value; - - if (Container is not null) - { - _ = Container.RefreshAsync(); - } - - if (value) - { - Notification.Warning(Snackbar, "Filtered"); - } - } - } - } - - private void OnFilterReset() - { - FilterUnassignedCustomer = false; - FilterUnassignedAgent = false; - } - - [BsonIgnoreExtraElements] public class IndexViewModel { @@ -410,13 +172,7 @@ public partial class Index [BsonElement("description")] public string? Description { get; set; } - [BsonElement("agent")] - public AgentEntity? Agent { get; set; } - - [BsonElement("customer")] - public CustomerEntity? Customer { get; set; } - - [BsonElement("online")] - public bool Online { get; set; } + [BsonElement("hosts")] + public int? Hosts { get; set; } } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor index f988c31..b1a6f3b 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor @@ -6,18 +6,15 @@ { - + - @if (_model.Response.HadErrors is false) + @if (_model.Response.ResponseError ?? false) { - - } - else - { - + Response contains Errors } + diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor.cs index eac27e5..8fb04bd 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Actions/Console/Index.razor.cs @@ -1,9 +1,8 @@ -using Insight.Domain.Messages; -using Insight.Domain.Messages.Agent; -using Insight.Infrastructure; +using Insight.Domain.Network; +using Insight.Domain.Network.Agent.Messages; using Insight.Infrastructure.Entities; using Insight.Web.Constants; -using Insight.Web.Network; +using Insight.Web.Network.Broker; using Microsoft.AspNetCore.Components; using MongoDB.Bson; using MongoDB.Driver; @@ -33,7 +32,7 @@ public partial class Index protected override async Task OnInitializedAsync() { - _subscriptions.Add(Bus.SubscribeAsync>(OnQueryResultAsync, p => p.RequestId == _id)); + _subscriptions.Add(Bus.SubscribeAsync>(OnQueryResultAsync, p => p.Message?.RequestId == _id)); } private async Task LoadDataAsync() @@ -75,15 +74,18 @@ public partial class Index private async Task SubmitAsync() { - if (_model.Request.Query is null || WebPool.Any() is false) return; + if (_model.Request.RequestData is null || WebPool.Any() is false) return; try { - await WebPool.First().Value.SendAsync(new Proxy + await WebPool.First().Value.SendAsync(new Proxy { - RequestId = _id, - HostId = HostId, - Message = _model.Request + ProxyId = HostId, + Message = new Request + { + RequestId = _id, + RequestData = _model.Request.RequestData + } }, default); Notification.Information(Snackbar, "Sent Query Command"); @@ -94,18 +96,17 @@ public partial class Index } } - private async ValueTask OnQueryResultAsync(Proxy proxy, CancellationToken cancellationToken) + private async ValueTask OnQueryResultAsync(Proxy proxy, CancellationToken cancellationToken) { - _model.Response.HadErrors = proxy.Message.HadErrors; - _model.Response.Data = proxy.Message?.Data; - _model.Response.Errors = proxy.Message?.Errors; + _model.Response.ResponseError = proxy.Message?.ResponseError; + _model.Response.ResponseData = proxy.Message?.ResponseData; await InvokeAsync(StateHasChanged); } private class IndexViewModel { - public readonly ConsoleQueryRequest Request = new(); - public readonly ConsoleQuery Response = new(); + public readonly Request Request = new(); + public readonly Response Response = new(); } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/AgentAssign.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/AgentAssign.razor.cs index 1eafaa8..19cae2d 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/AgentAssign.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/AgentAssign.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor b/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor index 0743004..39e4829 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor @@ -7,7 +7,7 @@ Title="@Title" Breadcrumbs="@Breadcrumbs" Data="LoadDataAsync" - OnAdd="()=>_createDialog?.Toggle()"> + OnAdd="()=>_createDialog?.ToggleAsync()">
diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor.cs index 6d84e7d..3996778 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/CustomerAssign.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor index ef71106..016cbd8 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor @@ -9,10 +9,10 @@ - + - + @@ -54,10 +54,8 @@ - - - - + + @code { private HostCustomerDialog? _customerDialog; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor.cs index 2a4dd64..cd37787 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Drives/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Drives/Index.razor.cs index a7d8427..7d3d556 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Drives/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Drives/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Mainboard/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Mainboard/Details.razor.cs index 0bd04b6..ca37073 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Mainboard/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Mainboard/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Memory/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Memory/Index.razor.cs index 7791c44..546aa16 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Memory/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Memory/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Processors/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Processors/Details.razor.cs index db5c193..bf8a272 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Processors/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Processors/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Videocards/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Videocards/Index.razor.cs index b44e0a9..99d6b3b 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Videocards/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Hardware/Videocards/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor b/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor index 06dfbdf..97388de 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor @@ -7,12 +7,12 @@ - @if (Host is not null) + @if (_model is not null) { - @if (Host.Agent is not null) + @if (_model.Agent is not null) { - + Select @@ -26,7 +26,7 @@ else { - + Assign @@ -40,10 +40,4 @@ @code { private bool _visible; - - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor.cs index f7db0e3..8bc5c73 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostAgentDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -9,7 +8,6 @@ namespace Insight.Web.Pages.Management.Hosts; public partial class HostAgentDialog { - [CascadingParameter(Name = "Host")] public HostEntity? Host { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; @@ -17,15 +15,33 @@ public partial class HostAgentDialog [Inject] private ILogger Logger { get; init; } = default!; private enum Content { Unassign, Assign } + private HostEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.Host().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } private async Task UnassignAsync() { - if (Host is null) return; + if (_model is null) return; try { await Database.Host().UpdateOneAsync(Builders - .Filter.Eq(p => p.Id, Host.Id), Builders + .Filter.Eq(p => p.Id, _model.Id), Builders .Update.Set(p => p.Agent, null)); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor b/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor index e2f523d..69a12d7 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor @@ -7,13 +7,13 @@ - @if (Host is not null) + @if (_model is not null) { - + - - + + @@ -30,9 +30,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor.cs index 60b1f67..14f14fe 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostCreateDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -15,12 +14,19 @@ public partial class HostCreateDialog [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; - private HostEntity? Host { get; set; } = new(); + private HostEntity? _model; + + public async Task ToggleAsync() + { + _model = new(); + _visible = !_visible; + + await InvokeAsync(StateHasChanged); + } private async Task SubmitAsync() { - if (Host is null) return; - Host.Insert = DateTime.Now; + if (_model is null) return; try { @@ -28,8 +34,8 @@ public partial class HostCreateDialog .InsertOneAsync(new HostEntity { Insert = DateTime.Now, - Name = Host?.Name, - Description = Host?.Description, + Name = _model.Name, + Description = _model.Description, }, cancellationToken: default) .ConfigureAwait(false); @@ -50,7 +56,7 @@ public partial class HostCreateDialog } finally { - Host = new(); + _model = new(); _visible = false; } } diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor b/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor index 350b65e..c15cc2c 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor @@ -7,12 +7,12 @@ - @if (Host is not null) + @if (_model is not null) { - @if (Host.Customer is not null) + @if (_model.Customer is not null) { - + Select @@ -26,7 +26,7 @@ else { - + Assign @@ -40,10 +40,4 @@ @code { private bool _visible; - - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor.cs index 52413cb..4eec46a 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostCustomerDialog.razor.cs @@ -1,52 +1,67 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; using MudBlazor; -namespace Insight.Web.Pages.Management.Hosts +namespace Insight.Web.Pages.Management.Hosts; + +public partial class HostCustomerDialog { - public partial class HostCustomerDialog + [Parameter] public EventCallback OnChanges { get; set; } + + [Inject] private IMongoDatabase Database { get; init; } = default!; + [Inject] private ISnackbar Snackbar { get; init; } = default!; + [Inject] private ILogger Logger { get; init; } = default!; + + private enum Content { Unassign, Assign } + private HostEntity? _model; + + public async void ToggleAsync(string? id) { - [CascadingParameter(Name = "Host")] public HostEntity? Host { get; set; } - [Parameter] public EventCallback OnChanges { get; set; } + if (id is null) return; - [Inject] private IMongoDatabase Database { get; init; } = default!; - [Inject] private ISnackbar Snackbar { get; init; } = default!; - [Inject] private ILogger Logger { get; init; } = default!; - - private enum Content { Unassign, Assign } - - private async Task UnassignAsync() + try { - if (Host is null) return; + _model = await Database.Host().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } - try + await InvokeAsync(StateHasChanged); + } + + private async Task UnassignAsync() + { + if (_model is null) return; + + try + { + await Database.Host().UpdateOneAsync(Builders + .Filter.Eq(p => p.Id, _model.Id), Builders + .Update.Set(p => p.Customer, null)); + + Notification.Success(Snackbar); + + if (OnChanges.HasDelegate) { - await Database.Host().UpdateOneAsync(Builders - .Filter.Eq(p => p.Id, Host.Id), Builders - .Update.Set(p => p.Customer, null)); - - Notification.Success(Snackbar); - - if (OnChanges.HasDelegate) + await InvokeAsync(async () => { - await InvokeAsync(async () => - { - await OnChanges.InvokeAsync(this); - }); - } - } - catch (Exception ex) - { - Notification.Error(Snackbar); - Logger.LogError(ex.ToString()); - } - finally - { - _visible = false; + await OnChanges.InvokeAsync(this); + }); } } + catch (Exception ex) + { + Notification.Error(Snackbar); + Logger.LogError(ex.ToString()); + } + finally + { + _visible = false; + } } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor b/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor index 7dfb3e2..112111d 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor @@ -7,12 +7,12 @@ - @if (Host is not null) + @if (_model is not null) { - + - - + + @@ -29,9 +29,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor.cs index 313cb2d..661c8a0 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostDeleteDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -9,21 +8,39 @@ namespace Insight.Web.Pages.Management.Hosts; public partial class HostDeleteDialog { - [CascadingParameter(Name = "Host")] public ViewModel? Host { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; + private HostEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.Host().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + private async Task SubmitAsync() { - if (Host is null) return; + if (_model is null) return; try { await Database.Host().DeleteOneAsync(Builders - .Filter.Eq(p => p.Id, Host.Id?.ToString()), + .Filter.Eq(p => p.Id, _model.Id), cancellationToken: default).ConfigureAwait(false); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor b/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor index e4b6b99..9bcbbf9 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor @@ -7,21 +7,13 @@ - @if (Host is not null) + @if (_model is not null) { - + - @* - - - - - - - *@ - - + + @@ -38,9 +30,4 @@ @code { private bool _visible; - public void Toggle() - { - _visible = !_visible; - StateHasChanged(); - } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor.cs index 181660a..47358f6 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/HostEditDialog.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Driver; @@ -9,26 +8,44 @@ namespace Insight.Web.Pages.Management.Hosts; public partial class HostEditDialog { - [CascadingParameter(Name = "Host")] public ViewModel? Host { get; set; } [Parameter] public EventCallback OnChanges { get; set; } [Inject] private IMongoDatabase Database { get; init; } = default!; [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private ILogger Logger { get; init; } = default!; + private HostEntity? _model; + + public async void ToggleAsync(string? id) + { + if (id is null) return; + + try + { + _model = await Database.Host().Find(p => p.Id == id).FirstAsync(); + _visible = !_visible; + } + catch (Exception) + { + Notification.Error(Snackbar); + } + + await InvokeAsync(StateHasChanged); + } + private async Task SubmitAsync() { - if (Host is null) return; + if (_model is null) return; try { await Database.Host().UpdateOneAsync(Builders .Filter - .Eq(p => p.Id, Host.Id.ToString()), Builders + .Eq(p => p.Id, _model.Id), Builders .Update .Set(p => p.Update, DateTime.Now) - .Set(p => p.Name, Host.Name) - .Set(p => p.Description, Host.Description), + .Set(p => p.Name, _model.Name) + .Set(p => p.Description, _model.Description), cancellationToken: default); Notification.Success(Snackbar); diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor index aea8fa5..f906036 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor @@ -6,7 +6,7 @@ Title="@Title" Breadcrumbs="@Breadcrumbs" Data="LoadDataAsync" - OnAdd="OnCreate" + OnAdd="()=>_createDialog?.ToggleAsync()" OnFilter="()=>Filter = true" Filtered="Filtered">
@@ -54,10 +54,10 @@ - + Edit - + Delete @@ -82,11 +82,9 @@ - - - - - + + + @code { private HostCreateDialog? _createDialog; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor.cs index 72d4169..e2b6a9f 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -171,35 +170,6 @@ public partial class Index await Container.RefreshAsync().ConfigureAwait(false); } - private void OnCreate() - { - _createDialog?.Toggle(); - } - - private void OnEdit(ViewModel model) - { - Model = new ViewModel - { - Id = model.Id, - Name = model?.Name, - Description = Model?.Description - }; - - _editDialog?.Toggle(); - } - - private void OnDelete(ViewModel model) - { - Model = new ViewModel - { - Id = model.Id, - Name = model?.Name, - Description = Model?.Description - }; - - _deleteDialog?.Toggle(); - } - private bool _filter; private bool Filter { diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Logs.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Logs.razor.cs index 0538514..317a055 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Logs.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Logs.razor.cs @@ -1,5 +1,4 @@ using Insight.Domain.Enums; -using Insight.Infrastructure; using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Addresses/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Addresses/Index.razor.cs index dc703bf..b17a4ce 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Addresses/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Addresses/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Gateways/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Gateways/Index.razor.cs index 2071600..7dd20a6 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Gateways/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Gateways/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Details.razor.cs index 9c986a1..b0fb0b9 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Index.razor.cs index aaf1d60..b5c1d2c 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Interfaces/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Nameservers/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Nameservers/Index.razor.cs index af3cbe6..2c4343d 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Nameservers/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Nameservers/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Routes/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Routes/Index.razor.cs index 147372c..7dcf02a 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Network/Routes/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Network/Routes/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor index d240ca5..d6ee0a5 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor @@ -1,6 +1,6 @@ @inherits ComponentBase -
- + Name - + SID diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor.cs index ba49f9b..b8c3a1e 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Groups/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -20,7 +19,7 @@ public partial class Index [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private NavigationManager NavigationManager { get; init; } = default!; - private TableContainer? Container { get; set; } + private TableContainer? Container { get; set; } private string Title { get; set; } = Global.Name; private List Breadcrumbs { get; } = new(); private string? Search { get; set; } @@ -54,18 +53,18 @@ public partial class Index Breadcrumbs.Add(new BreadcrumbItem("Groups", href: "#", true)); } - private async Task> LoadDataAsync(TableState state) + private async Task> LoadDataAsync(TableState state) { try { - var filter = Builders.Filter.Eq(p => p.Host, HostId); + var filter = Builders.Filter.Eq(p => p.Host, HostId); if (string.IsNullOrWhiteSpace(Search) is false) { var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); - filter &= Builders.Filter.Regex(x => x.Domain, regex) | - Builders.Filter.Regex(x => x.Name, regex) | - Builders.Filter.Regex(x => x.Sid, regex); + filter &= Builders.Filter.Regex(x => x.Domain, regex) | + Builders.Filter.Regex(x => x.Name, regex) | + Builders.Filter.Regex(x => x.Sid, regex); } var query = Database.HostSystemGroup() @@ -74,22 +73,22 @@ public partial class Index { SortDirection.Ascending => state.SortLabel switch { - "Name" => Builders.Sort.Ascending(p => p.Domain).Ascending(p => p.Name), - "Sid" => Builders.Sort.Ascending(p => p.Sid), - "LocalAccount" => Builders.Sort.Ascending(p => p.LocalAccount), + "Name" => Builders.Sort.Ascending(p => p.Domain).Ascending(p => p.Name), + "Sid" => Builders.Sort.Ascending(p => p.Sid), + "LocalAccount" => Builders.Sort.Ascending(p => p.LocalAccount), _ => null }, SortDirection.Descending => state.SortLabel switch { - "Name" => Builders.Sort.Descending(p => p.Domain).Descending(p => p.Name), - "Sid" => Builders.Sort.Descending(p => p.Sid), - "LocalAccount" => Builders.Sort.Descending(p => p.LocalAccount), + "Name" => Builders.Sort.Descending(p => p.Domain).Descending(p => p.Name), + "Sid" => Builders.Sort.Descending(p => p.Sid), + "LocalAccount" => Builders.Sort.Descending(p => p.LocalAccount), _ => null }, - _ => Builders.Sort.Ascending(p => p.Domain).Ascending(p => p.Name) + _ => Builders.Sort.Ascending(p => p.Domain).Ascending(p => p.Name) }); - return new TableData() + return new TableData() { TotalItems = (int)await query.CountDocumentsAsync(default), Items = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default) @@ -98,7 +97,7 @@ public partial class Index catch (Exception) { Notification.Error(Snackbar); - return new TableData(); + return new TableData(); } } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Os/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Os/Details.razor.cs index a3af16a..413c69e 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Os/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Os/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Printers/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Printers/Index.razor.cs index 7342ea2..ee7735d 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Printers/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Printers/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Services/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Services/Index.razor.cs index a702e30..06d65a0 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Services/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Services/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Sessions/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Sessions/Index.razor.cs index 97d411c..aba0d62 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Sessions/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Sessions/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Software/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Software/Index.razor.cs index d71ee31..c41172d 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Software/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Software/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor index d7c1e28..40dc72b 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.StoragePool; +@using static Insight.Domain.Network.Agent.Messages.StoragePool; @inherits ComponentBase diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor.cs index 7551137..170fc41 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Index.razor index 9947582..a7fe9c8 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/Index.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.StoragePool; +@using static Insight.Domain.Network.Agent.Messages.StoragePool; @inherits ComponentBase diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Details.razor.cs index 0c15620..fcf3845 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Index.razor index 6b3e104..f88a3a2 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/PhysicalDisks/Index.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.PhysicalDisk; +@using static Insight.Domain.Network.Agent.Messages.PhysicalDisk; @inherits ComponentBase diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Details.razor.cs index 785c7be..a1d2231 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Index.razor index 7eb4d6b..fcd0573 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/StoragePools/VirtualDisks/Index.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.VirtualDisk; +@using static Insight.Domain.Network.Agent.Messages.VirtualDisk; @inherits ComponentBase Breadcrumbs { get; } = new(); - private HostUserEntity? HostUser { get; set; } + private HostSysUserEntity? HostUser { get; set; } private async Task LoadDataAsync() { diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor index aa50f1c..f1d05cc 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor @@ -1,6 +1,6 @@ @inherits ComponentBase -
- + Name - + Full Name - + SID - + Lockout @@ -33,7 +33,7 @@ *@ - + Status diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor.cs index 927723f..f03c5d1 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/Users/Index.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Components.Containers; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; @@ -20,7 +19,7 @@ public partial class Index [Inject] private ISnackbar Snackbar { get; init; } = default!; [Inject] private NavigationManager NavigationManager { get; init; } = default!; - private TableContainer? Container { get; set; } + private TableContainer? Container { get; set; } private string Title { get; set; } = Global.Name; private List Breadcrumbs { get; } = new(); private string? Search { get; set; } @@ -54,21 +53,21 @@ public partial class Index Breadcrumbs.Add(new BreadcrumbItem("Users", href: "#", true)); } - private async Task> LoadDataAsync(TableState state) + private async Task> LoadDataAsync(TableState state) { try { - var filter = Builders.Filter.Eq(p => p.Host, HostId); + var filter = Builders.Filter.Eq(p => p.Host, HostId); if (string.IsNullOrWhiteSpace(Search) is false) { var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase)); - filter &= Builders.Filter.Regex(x => x.Name, regex) | - Builders.Filter.Regex(x => x.FullName, regex) | - Builders.Filter.Regex(x => x.Sid, regex) | - Builders.Filter.Regex(x => x.Lockout, regex) | + filter &= Builders.Filter.Regex(x => x.Name, regex) | + Builders.Filter.Regex(x => x.FullName, regex) | + Builders.Filter.Regex(x => x.Sid, regex) | + Builders.Filter.Regex(x => x.Lockout, regex) | //Builders.Filter.Regex(x => x.Groups, regex) | - Builders.Filter.Regex(x => x.Status, regex); + Builders.Filter.Regex(x => x.Status, regex); } var query = Database.HostSystemUser() @@ -77,28 +76,28 @@ public partial class Index { SortDirection.Ascending => state.SortLabel switch { - "Name" => Builders.Sort.Ascending(p => p.Name), - "FullName" => Builders.Sort.Ascending(p => p.FullName), - "Sid" => Builders.Sort.Ascending(p => p.Sid), - "Lockout" => Builders.Sort.Ascending(p => p.Lockout), + "Name" => Builders.Sort.Ascending(p => p.Name), + "FullName" => Builders.Sort.Ascending(p => p.FullName), + "Sid" => Builders.Sort.Ascending(p => p.Sid), + "Lockout" => Builders.Sort.Ascending(p => p.Lockout), //"Groups" => Builders.Sort.Ascending(p => p.Groups), - "Status" => Builders.Sort.Ascending(p => p.Status), + "Status" => Builders.Sort.Ascending(p => p.Status), _ => null }, SortDirection.Descending => state.SortLabel switch { - "Name" => Builders.Sort.Descending(p => p.Name), - "FullName" => Builders.Sort.Descending(p => p.FullName), - "Sid" => Builders.Sort.Descending(p => p.Sid), - "Lockout" => Builders.Sort.Descending(p => p.Lockout), + "Name" => Builders.Sort.Descending(p => p.Name), + "FullName" => Builders.Sort.Descending(p => p.FullName), + "Sid" => Builders.Sort.Descending(p => p.Sid), + "Lockout" => Builders.Sort.Descending(p => p.Lockout), //"Groups" => Builders.Sort.Descending(p => p.Groups), - "Status" => Builders.Sort.Descending(p => p.Status), + "Status" => Builders.Sort.Descending(p => p.Status), _ => null }, - _ => Builders.Sort.Ascending(p => p.Domain).Ascending(p => p.Name) + _ => Builders.Sort.Ascending(p => p.Domain).Ascending(p => p.Name) }); - return new TableData() + return new TableData() { TotalItems = (int)await query.CountDocumentsAsync(default), Items = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default) @@ -107,7 +106,7 @@ public partial class Index catch (Exception) { Notification.Error(Snackbar); - return new TableData(); + return new TableData(); } } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor index 86e107b..823ffe1 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor @@ -1,7 +1,7 @@ @inherits ComponentBase @using Insight.Web.Pages.Management.Hosts.Systems.VirtualMaschines.Snapshots @using Microsoft.AspNetCore.Components.Rendering -@using static Insight.Domain.Messages.Agent.VirtualMaschine; +@using static Insight.Domain.Network.Agent.Messages.VirtualMaschine; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor.cs b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor.cs index 28c4bc9..627c4b9 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor.cs +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Details.razor.cs @@ -1,5 +1,4 @@ -using Insight.Infrastructure; -using Insight.Infrastructure.Entities; +using Insight.Infrastructure.Entities; using Insight.Web.Constants; using Microsoft.AspNetCore.Components; using MongoDB.Bson; diff --git a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Index.razor b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Index.razor index 51a734c..e293291 100644 --- a/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Index.razor +++ b/src/Web/Insight.Web/Pages/Management/Hosts/Systems/VirtualMaschines/Index.razor @@ -1,4 +1,4 @@ -@using static Insight.Domain.Messages.Agent.VirtualMaschine; +@using static Insight.Domain.Network.Agent.Messages.VirtualMaschine; @inherits ComponentBase Logger +@inject ISnackbar Snackbar +@inject IJSRuntime JSRuntime +@inject ISessionPool SessionPool +@inject Bus Bus + + + + + +

Status: @_status

+

Casting: @_casting

+
+ @if (_casting) + { + +
+ + +
+
+ } + + + + + + @if (_casting is false) + { + + Connect + + } + else + { + + ReadOnly + + + Stats + + + Disconnect + + } + + + + + + + @if (_casting && _stats) + { + +

Timestamp: @_metric?.Timestamp

+

Mbps: @_metric?.Mbps

+

Fps: @_metric?.Fps

+

RTT: @_metric?.RTT

+ @*

GPU: @_metric?.IsGpuAccelerated

*@ +
+ +

Width: @_screen?.ViewWidth

+

Height: @_screen?.ViewHeight

+

Client Cursor X: @_cursor?.X | Client Cursor Y: @_cursor?.Y

+

Canvas Cursor X: @_canvasCursorX | Canvas Cursor Y: @_canvasCursorY

+ @*

CB: @_clipboard?.Text

*@ +
+ +

Status: @_status

+

ReadOnly: @_readOnly

+
+ } + @foreach (var client in SessionPool) + { +

@client.Value.Id

+ } +
+
+ +@code{ + private string? _id = null; + private bool _rendered = false; + private bool _casting = false; + private bool _stats = false; + private bool _readOnly = true; + private string _status = ""; + + private int _canvasCursorX; + private int _canvasCursorY; + + private RemoteSession? _cachedSession; + + private CastMetric? _metric; + private CastScreen? _screen; + private CastCursor? _cursor; + private CastClipboardReceived? _clipboard; + + private List _subs = new(); + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + _rendered = true; + _status = "rendered"; + + _subs.Add(Bus.SubscribeAsync(OnClientConnectedAsync, null)); + _subs.Add(Bus.SubscribeAsync(OnClientDisconnectedAsync, null)); + } + } + + [JSInvokable] + public async Task HandleCursorEventAsync(double x, double y) + { + _canvasCursorX = (int)Math.Truncate(x); + _canvasCursorY = (int)Math.Truncate(y); + + await InvokeAsync(StateHasChanged); + + // if not disabled, send cursor position + if (_readOnly) return; + + if (SessionPool.FirstOrDefault().Value is not RemoteSession session) return; + + await session.SendAsync(new Domain.Network.Remote.Messages.CastCursorReceived + { + Id = _id, + X = _canvasCursorX, + Y = _canvasCursorY + }, default); + } + + private async ValueTask OnClientConnectedAsync(RemoteConnected message, CancellationToken cancellationToken) + { + Snackbar.Add($"{message.RemoteSession.Id} connected"); + + await Task.Delay(1000); + + await InvokeAsync(StateHasChanged); + } + + private async ValueTask OnClientDisconnectedAsync(RemoteDisconnected message, CancellationToken cancellationToken) + { + Snackbar.Add($"{message.RemoteSession.Id} disconnected"); + + _casting = false; + + await Task.Delay(1000); + + await InvokeAsync(StateHasChanged); + } + + private async ValueTask OnRequestAsync() + { + if (SessionPool.Where(p => p.Value.Id == _id).FirstOrDefault().Value is not RemoteSession session) + { + _status = $"client not available"; + + Snackbar.Add("client not available", Severity.Error); + return; + } + + _cachedSession = session; + + _subs.Add(Bus.SubscribeAsync(OnRequestResponseAsync, p => p.Id == _id)); + + _status = $"request to ({_id})"; + Snackbar.Add($"request to ({_id})"); + + await _cachedSession.SendAsync(new Domain.Network.Remote.Messages.CastRequest + { + Id = _id, + RequesterName = "Web", + Mode = Domain.Enums.RemoteControlMode.Attended + }, default); + } + + private async ValueTask OnRequestResponseAsync(CastRequestResponse response, CancellationToken cancellationToken) + { + if (response.Accepted is false) + { + _status = "denied"; + _casting = false; + + Snackbar.Add($"client ({_id}) denied"); + } + else + { + _subs.Add(Bus.SubscribeAsync(OnMetricDataAsync, p => p.Id == _id)); + _subs.Add(Bus.SubscribeAsync(OnScreenDataAsync, p => p.Id == _id)); + _subs.Add(Bus.SubscribeAsync(OnCursorDataAsync, p => p.Id == _id)); + + _status = $"client ({_id}) authorized"; + _casting = true; + + await JSRuntime.InvokeVoidAsync("setupCursorEvents", DotNetObjectReference.Create(this), "cursorViewer", "HandleCursorEventAsync"); + + Snackbar.Add($"client ({_id}) authorized"); + } + + await InvokeAsync(StateHasChanged); + } + + private async ValueTask OnMetricDataAsync(CastMetric metricData, CancellationToken cancellationToken) + { + _metric = metricData; + await InvokeAsync(StateHasChanged); + } + + private async ValueTask OnScreenDataAsync(CastScreen screenData, CancellationToken cancellationToken) + { + _screen = screenData; + + if (_rendered is false) return; + + try + { + await JSRuntime.InvokeVoidAsync( + "displayScreenData", + "screenViewer", + screenData.ViewWidth, + screenData.ViewHeight, + screenData.Left, + screenData.Top, + screenData.Width, + screenData.Height, + screenData.Image); + + await _cachedSession.ScreenDataAckAsync(screenData, cancellationToken); + } + catch (Exception ex) + { + Snackbar.Add(ex.Message, Severity.Warning); + } + } + + private async ValueTask OnCursorDataAsync(CastCursor cursorData, CancellationToken cancellationToken) + { + if (_rendered is false) return; + + if (_cursor?.X == cursorData.X || + _cursor?.Y == cursorData.Y || + _cursor?.Icon == cursorData.Icon) return; + + _cursor = cursorData; + await InvokeAsync(StateHasChanged); + + try + { + await JSRuntime.InvokeVoidAsync( + "updateCursor", + "screenViewer", + "cursorViewer", + cursorData.X, + cursorData.Y, + cursorData.Icon); + } + catch (Exception ex) + { + Snackbar.Add(ex.Message, Severity.Warning); + } + } + + private async ValueTask OnAbortAsync() + { + if (SessionPool.Where(p => p.Value.Id == _id).FirstOrDefault().Value is not RemoteSession session) + { + _status = $"client not available"; + + Snackbar.Add("client not available", Severity.Error); + return; + } + + _status = $"abort to ({_id})"; + + Snackbar.Add($"abort to ({_id})"); + + //await session.SendAsync(new Domain.Network.Remote.Messages.CastAbort(), default); + session.Disconnect(); + + _casting = false; + + await InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + foreach (var sub in _subs) sub?.Dispose(); + _subs.Clear(); + + Logger.LogWarning("Remote Page disposed"); + } +} \ No newline at end of file diff --git a/src/Web/Insight.Web/Pages/_Host.cshtml b/src/Web/Insight.Web/Pages/_Host.cshtml index bcf2b39..976805c 100644 --- a/src/Web/Insight.Web/Pages/_Host.cshtml +++ b/src/Web/Insight.Web/Pages/_Host.cshtml @@ -32,9 +32,8 @@ - - + diff --git a/src/Web/Insight.Web/Program.cs b/src/Web/Insight.Web/Program.cs index 923a770..e03c538 100644 --- a/src/Web/Insight.Web/Program.cs +++ b/src/Web/Insight.Web/Program.cs @@ -1,11 +1,14 @@ using Insight.Domain.Constants; using Insight.Domain.Interfaces; -using Insight.Domain.Messages; +using Insight.Domain.Network; using Insight.Infrastructure; using Insight.Web.Hosting; using Insight.Web.Middleware; -using Insight.Web.Network; -using Insight.Web.Network.Handlers; +using Insight.Web.Network.Broker; +using Insight.Web.Network.Broker.Handlers; +using Insight.Web.Network.Remote; +using Insight.Web.Network.Remote.Handlers; +using System.Net; using Vaitr.Bus; using Vaitr.Network; using Vaitr.Network.Hosting; @@ -35,13 +38,15 @@ internal class Program builder.Logging.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/web_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}"); + // BROKER + builder.Services.AddBrokerServices(builder.Configuration); + + // REMOTE + builder.Services.AddRemoteServices(builder.Configuration); + // ASSETS builder.WebHost.UseStaticWebAssets(); - // INFRASTRUCTURE - builder.Services.AddDatabase(builder.Configuration); - builder.Services.AddInfrastructureServices(); - // IDENTITY builder.Services.AddIdentityServices(builder.Configuration); builder.Services.AddCustomAuthentication(builder.Configuration); @@ -57,23 +62,14 @@ internal class Program builder.Services.AddSignalR(options => { options.EnableDetailedErrors = true; + options.MaximumReceiveMessageSize = 1024 * 1024; + //options.StreamBufferCapacity = 1024; + options.EnableDetailedErrors = true; }); - // NETWORKING - builder.Services.UseHostedClient(options => - { - options.Host = builder.Configuration.GetValue(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} value not set (appsettings)"); - options.Port = builder.Configuration.GetValue(Appsettings.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} value not set (appsettings)"); - options.Keepalive = 10000; - options.Timeout = 30000; - - options.Compression = true; - options.Encryption = Encryption.Tls12; - - options.UseSerializer, IMessage>(); - }); - - builder.Services.AddSingleton, ConsoleHandler>(); + // INFRASTRUCTURE + builder.Services.AddDatabase(builder.Configuration); + builder.Services.AddInfrastructureServices(); // WEB:APP var app = builder.Build(); @@ -112,6 +108,7 @@ internal class Program // BLAZOR HUBS (SIGNALR) app.MapBlazorHub(); + // FALLBACK app.MapFallbackToPage("/_Host"); @@ -121,4 +118,49 @@ internal class Program // HOST START await app.RunAsync().ConfigureAwait(false); } +} + +internal static class ServiceExtensions +{ + internal static IServiceCollection AddBrokerServices(this IServiceCollection services, IConfiguration configuration) + { + services.UseHostedClient(options => + { + options.Host = configuration.GetValue(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} value not set (appsettings)"); + options.Port = configuration.GetValue(Appsettings.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} value not set (appsettings)"); + options.Keepalive = 10000; + options.Timeout = 30000; + options.Encryption = Encryption.Tls12; + options.Compression = true; + + options.UseSerializer>(); + }); + + services.AddSingleton, AgentHandler>(); + + return services; + } + + internal static IServiceCollection AddRemoteServices(this IServiceCollection services, IConfiguration configuration) + { + services.UseHostedServer(options => + { + options.Address = IPAddress.Any; + options.Port = configuration.GetValue(Appsettings.RemoteServerPort) ?? throw new Exception($"{Appsettings.RemoteServerPort} value not set (appsettings)"); + options.Keepalive = 10000; + options.Timeout = 30000; + options.Backlog = 128; + + options.Compression = true; + options.Encryption = Encryption.Tls12; + options.Certificate = configuration.GetValue(Appsettings.RemoteServerCertificate) ?? throw new Exception($"{Appsettings.RemoteServerCertificate} value not set (appsettings)"); + options.CertificatePassword = configuration.GetValue(Appsettings.RemoteServerCertificatePassword) ?? throw new Exception($"{Appsettings.RemoteServerCertificatePassword} value not set (appsettings)"); + + options.UseSerializer>(); + }); + + services.AddSingleton, RemoteHandler>(); + + return services; + } } \ No newline at end of file diff --git a/src/Web/Insight.Web/Services/ChatService.cs b/src/Web/Insight.Web/Services/ChatService.cs index d89a0d6..b348462 100644 --- a/src/Web/Insight.Web/Services/ChatService.cs +++ b/src/Web/Insight.Web/Services/ChatService.cs @@ -1,4 +1,4 @@ -using Insight.Infrastructure; +using Insight.Infrastructure.Entities; using Insight.Web.Models; using MongoDB.Bson; using MongoDB.Driver; diff --git a/src/Web/Insight.Web/Services/SessionHandler.cs b/src/Web/Insight.Web/Services/SessionHandler.cs index 330d09a..190d6c9 100644 --- a/src/Web/Insight.Web/Services/SessionHandler.cs +++ b/src/Web/Insight.Web/Services/SessionHandler.cs @@ -1,4 +1,4 @@ -using Insight.Infrastructure; +using Insight.Infrastructure.Entities; using Insight.Web.Models; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Server.Circuits; diff --git a/src/Web/Insight.Web/appsettings.Development.json b/src/Web/Insight.Web/appsettings.Development.json index e850623..2890dc9 100644 --- a/src/Web/Insight.Web/appsettings.Development.json +++ b/src/Web/Insight.Web/appsettings.Development.json @@ -4,5 +4,9 @@ "database": "mongodb://db.insight.local:27017", "database0": "mongodb://insight.webmatic.de:27017", "server.host": "insight.local", - "server.port": 3001 + "server.port": 3001, + + "remote.port": 3003, + "remote.certificate": "localhost.pfx", + "remote.certificate.password": "Webmatic12" } \ No newline at end of file diff --git a/src/Web/Insight.Web/appsettings.json b/src/Web/Insight.Web/appsettings.json index f211279..d9998f4 100644 --- a/src/Web/Insight.Web/appsettings.json +++ b/src/Web/Insight.Web/appsettings.json @@ -3,5 +3,9 @@ "Urls": "http://127.0.0.1:5001", "database": "mongodb://127.0.0.1:27017", "server.host": "127.0.0.1", - "server.port": 3001 + "server.port": 3001, + + "remote.port": 3003, + "remote.certificate": "localhost.pfx", + "remote.certificate.password": "Webmatic12" } \ No newline at end of file diff --git a/src/Web/Insight.Web/wwwroot/js/interop.js b/src/Web/Insight.Web/wwwroot/js/interop.js index e05d08e..cc1e3dc 100644 --- a/src/Web/Insight.Web/wwwroot/js/interop.js +++ b/src/Web/Insight.Web/wwwroot/js/interop.js @@ -10,4 +10,77 @@ function clearQrCode(elementId) { var qrc = document.getElementById(elementId); qrc.innerHTML = ''; qrc.title = ''; +} + +async function displayScreenData(screenId, viewWidth, viewHeight, left, top, width, height, buffer) { + + const screenDiv = document.getElementById(screenId); + + if (screenDiv.width !== viewWidth || screenDiv.height !== viewHeight) { + screenDiv.width = viewWidth; + screenDiv.height = viewHeight; + } + + const ctx = screenDiv.getContext('2d'); + + const imageBlob = new Blob([buffer], { type: 'image/bitmap' }); + + try { + const bitmap = await createImageBitmap(imageBlob); + ctx.drawImage(bitmap, left, top, width, height); + bitmap.close(); + } catch (err) { + console.error("Error creating image bitmap:", err); + } +} + +async function updateCursor(screenId, cursorId, hotSpotX, hotSpotY, icon) { + + const screenDiv = document.getElementById(screenId); + const cursorDiv = document.getElementById(cursorId); + + if (cursorDiv.width !== screenDiv.width || cursorDiv.height !== screenDiv.height) { + cursorDiv.width = screenDiv.width; + cursorDiv.height = screenDiv.height; + } + + if (cursorDiv.style.marginLeft !== `-${screenDiv.clientWidth}px`) { + cursorDiv.style.marginLeft = `-${screenDiv.clientWidth}px`; + } + + const ctx = cursorDiv.getContext('2d'); + ctx.clearRect(0, 0, cursorDiv.width, cursorDiv.height); + + if (icon) { + const iconBuffer = new Blob([icon], { type: 'image/png' }); + + const bitmap = await createImageBitmap(iconBuffer); + ctx.drawImage(bitmap, hotSpotX, hotSpotY, 32, 32); + bitmap.close(); + } + else { + ctx.fillStyle = 'white'; + ctx.beginPath(); + ctx.arc(hotSpotX, hotSpotY, 10, 0, Math.PI * 2); + ctx.fill(); + } +} + +async function setupCursorEvents(dotNetReference, canvasId, methodName) { + const canvas = document.getElementById(canvasId); + if (canvas) { + canvas.addEventListener('mousemove', function (event) { + const rect = canvas.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + + let scaleX = canvas.clientWidth / canvas.width; + let scaleY = canvas.clientHeight / canvas.height; + + let adjustedX = x / scaleX; + let adjustedY = y / scaleY; + + dotNetReference.invokeMethodAsync(methodName, adjustedX, adjustedY); + }); + } } \ No newline at end of file