mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
* Add Azure Pipelines yml. * Remove Server Installer. * Update Release Build.yml for Azure Pipelines * Update Release Build.yml * Update Release Build.yml for Azure Pipelines * Update Release Build.yml for Azure Pipelines * Update Release Build.yml for Azure Pipelines * Update Release Build.yml for Azure Pipelines * Update signtool.exe * Update Release Build.yml * Update Publish.ps1 * Update pipeline and Dockerfile. * Move docker files. * Update Dockerfile * Update Dockerfile * Update Dockerfile * Update Dockerfile * Move files. * Update Dockerfile * Update Dockerfile * Create RewritableStream. * Finish implementation of rewritable stream and embedded data searcher. * Remove RelayCode. * Get branding from default org if orgId is missing. * Update README.md * Remove AppConstants.ServerUrl. Fix main module file path. * Update submodule. * Extract embedded data in Program.cs. * Add logging. Update submodule. * Remove size block. BinaryWriter prefixes size. * Remove unused async * Update Immense.RemoteControl * Update Release Build.yml for Azure Pipelines * Update Release Build.yml for Azure Pipelines * Use UpgradeService for determining out of date clients.
350 lines
12 KiB
C#
350 lines
12 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using Remotely.Server.Enums;
|
|
using Remotely.Server.Hubs;
|
|
using Remotely.Server.Models;
|
|
using Remotely.Server.Services;
|
|
using Remotely.Shared.Attributes;
|
|
using Remotely.Shared.Models;
|
|
using Remotely.Shared.Utilities;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Remotely.Server.Components.Devices
|
|
{
|
|
[Authorize]
|
|
public partial class DevicesFrame : AuthComponentBase, IDisposable
|
|
{
|
|
private readonly List<Device> _allDevices = new();
|
|
private readonly string _deviceGroupAll = Guid.NewGuid().ToString();
|
|
private readonly string _deviceGroupNone = Guid.NewGuid().ToString();
|
|
private readonly List<DeviceGroup> _deviceGroups = new();
|
|
private readonly List<Device> _devicesForPage = new();
|
|
private readonly object _devicesLock = new();
|
|
private readonly List<Device> _filteredDevices = new();
|
|
private readonly List<PropertyInfo> _sortableProperties = new();
|
|
private int _currentPage = 1;
|
|
private int _devicesPerPage = 25;
|
|
private string _filter;
|
|
private bool _hideOfflineDevices = true;
|
|
private string _selectedGroupId;
|
|
private string _selectedSortProperty = "DeviceName";
|
|
private ListSortDirection _sortDirection;
|
|
|
|
[Inject]
|
|
private IClientAppState AppState { get; set; }
|
|
|
|
[Inject]
|
|
private ICircuitConnection CircuitConnection { get; set; }
|
|
|
|
[Inject]
|
|
private IDataService DataService { get; set; }
|
|
|
|
[Inject]
|
|
private IJsInterop JsInterop { get; set; }
|
|
|
|
[Inject]
|
|
private ILogger<DevicesFrame> Logger { get; set; }
|
|
|
|
[Inject]
|
|
private IToastService ToastService { get; set; }
|
|
|
|
private int TotalPages => (int)Math.Max(1, Math.Ceiling((decimal)_filteredDevices.Count / _devicesPerPage));
|
|
|
|
public void Dispose()
|
|
{
|
|
CircuitConnection.MessageReceived -= CircuitConnection_MessageReceived;
|
|
AppState.PropertyChanged -= AppState_PropertyChanged;
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
LoadDevices();
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await base.OnInitializedAsync();
|
|
|
|
CircuitConnection.MessageReceived += CircuitConnection_MessageReceived;
|
|
AppState.PropertyChanged += AppState_PropertyChanged;
|
|
|
|
_deviceGroups.Clear();
|
|
|
|
_deviceGroups.AddRange(DataService.GetDeviceGroups(User.UserName));
|
|
|
|
_selectedGroupId = _deviceGroupAll;
|
|
|
|
var sortableProperties = typeof(Device)
|
|
.GetProperties()
|
|
.Where(x => x.CustomAttributes.Any(x => x.AttributeType == typeof(SortableAttribute)));
|
|
|
|
_sortableProperties.AddRange(sortableProperties);
|
|
|
|
LoadDevices();
|
|
}
|
|
|
|
protected override bool ShouldRender()
|
|
{
|
|
var shouldRender = base.ShouldRender();
|
|
if (shouldRender)
|
|
{
|
|
FilterDevices();
|
|
}
|
|
return shouldRender;
|
|
}
|
|
|
|
private void AddScriptResult(ScriptResult result)
|
|
{
|
|
var device = DataService.GetDevice(result.DeviceID);
|
|
AppState.AddTerminalLine($"{device?.DeviceName} @ {result.TimeStamp}", "font-weight-bold");
|
|
|
|
foreach (var line in result.StandardOutput)
|
|
{
|
|
AppState.AddTerminalLine(line, "text-info");
|
|
}
|
|
foreach (var line in result.ErrorOutput)
|
|
{
|
|
AppState.AddTerminalLine(line, "text-danger");
|
|
}
|
|
AppState.InvokePropertyChanged(nameof(AppState.TerminalLines));
|
|
}
|
|
|
|
private void AppState_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == nameof(AppState.DevicesFrameFocusedCardState) ||
|
|
e.PropertyName == nameof(AppState.DevicesFrameFocusedDevice) ||
|
|
e.PropertyName == nameof(AppState.DevicesFrameSelectedDevices))
|
|
{
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
}
|
|
|
|
private void CircuitConnection_MessageReceived(object sender, CircuitEvent args)
|
|
{
|
|
switch (args.EventName)
|
|
{
|
|
case CircuitEventName.DeviceUpdate:
|
|
case CircuitEventName.DeviceWentOffline:
|
|
{
|
|
if (args.Params?.FirstOrDefault() is Device device)
|
|
{
|
|
lock (_devicesLock)
|
|
{
|
|
var index = _allDevices.FindIndex(x => x.ID == device.ID);
|
|
if (index > -1)
|
|
{
|
|
_allDevices[index] = device;
|
|
}
|
|
|
|
index = _devicesForPage.FindIndex(x => x.ID == device.ID);
|
|
if (index > -1)
|
|
{
|
|
_devicesForPage[index] = device;
|
|
}
|
|
}
|
|
Debouncer.Debounce(TimeSpan.FromSeconds(2), Refresh);
|
|
}
|
|
break;
|
|
}
|
|
case CircuitEventName.DisplayMessage:
|
|
{
|
|
var terminalMessage = (string)args.Params[0];
|
|
var toastMessage = (string)args.Params[1];
|
|
var className = (string)args.Params[2];
|
|
AppState.AddTerminalLine(terminalMessage);
|
|
ToastService.ShowToast(toastMessage, classString: className);
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
break;
|
|
case CircuitEventName.ScriptResult:
|
|
{
|
|
var result = (ScriptResult)args.Params[0];
|
|
AddScriptResult(result);
|
|
}
|
|
break;
|
|
case CircuitEventName.UnattendedSessionReady:
|
|
{
|
|
var sessionId = (string)args.Params[0];
|
|
var accessKey = (string)args.Params[1];
|
|
var deviceId = (string)args.Params[2];
|
|
var viewOnly = (bool)args.Params[3];
|
|
|
|
JsInterop.OpenWindow($"/RemoteControl/Viewer?mode=Unattended&sessionId={sessionId}&accessKey={accessKey}&viewonly={viewOnly}", "_blank");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ClearSelectedCard()
|
|
{
|
|
AppState.DevicesFrameFocusedDevice = null;
|
|
AppState.DevicesFrameFocusedCardState = DeviceCardState.Normal;
|
|
}
|
|
|
|
private void FilterDevices()
|
|
{
|
|
lock (_devicesLock)
|
|
{
|
|
_filteredDevices.Clear();
|
|
var appendDevices = new List<Device>();
|
|
|
|
foreach (var device in _allDevices)
|
|
{
|
|
if (AppState.DevicesFrameSelectedDevices.Contains(device.ID))
|
|
{
|
|
appendDevices.Add(device);
|
|
}
|
|
|
|
if (!device.IsOnline && _hideOfflineDevices)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(_filter) &&
|
|
device.Alias?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
|
|
device.CurrentUser?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
|
|
device.DeviceName?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
|
|
device.Notes?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
|
|
device.Platform?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
|
|
device.Tags?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_selectedGroupId == _deviceGroupAll ||
|
|
_selectedGroupId == device.DeviceGroupID ||
|
|
(
|
|
_selectedGroupId == _deviceGroupNone &&
|
|
string.IsNullOrWhiteSpace(device.DeviceGroupID
|
|
)))
|
|
{
|
|
_filteredDevices.Add(device);
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(_selectedSortProperty))
|
|
{
|
|
var direction = _sortDirection == ListSortDirection.Ascending ? 1 : -1;
|
|
_filteredDevices.Sort((a, b) =>
|
|
{
|
|
if (a.IsOnline != b.IsOnline)
|
|
{
|
|
return b.IsOnline.CompareTo(a.IsOnline);
|
|
}
|
|
|
|
var propInfo = _sortableProperties.Find(x => x.Name == _selectedSortProperty);
|
|
|
|
var valueA = propInfo.GetValue(a);
|
|
var valueB = propInfo.GetValue(b);
|
|
|
|
return Comparer.Default.Compare(valueA, valueB) * direction;
|
|
});
|
|
}
|
|
|
|
var skipCount = (_currentPage - 1) * _devicesPerPage;
|
|
var devicesForPage = _filteredDevices
|
|
.Except(appendDevices)
|
|
.Skip(skipCount)
|
|
.Take(_devicesPerPage);
|
|
|
|
_devicesForPage.Clear();
|
|
_devicesForPage.AddRange(appendDevices.Concat(devicesForPage));
|
|
}
|
|
|
|
}
|
|
|
|
private string GetDisplayName(PropertyInfo propInfo)
|
|
{
|
|
return propInfo.GetCustomAttribute<DisplayAttribute>()?.Name ?? propInfo.Name;
|
|
}
|
|
|
|
private string GetSortIcon()
|
|
{
|
|
return $"oi-sort-{_sortDirection.ToString().ToLower()}";
|
|
}
|
|
|
|
private void HandleRefreshClicked()
|
|
{
|
|
Refresh();
|
|
ToastService.ShowToast("Devices refreshed.");
|
|
}
|
|
|
|
private void LoadDevices()
|
|
{
|
|
lock (_devicesLock)
|
|
{
|
|
_allDevices.Clear();
|
|
|
|
var devices = DataService.GetDevicesForUser(Username)
|
|
.OrderByDescending(x => x.IsOnline)
|
|
.ToList();
|
|
|
|
_allDevices.AddRange(devices);
|
|
}
|
|
|
|
FilterDevices();
|
|
}
|
|
private void PageDown()
|
|
{
|
|
if (_currentPage > 1)
|
|
{
|
|
_currentPage--;
|
|
}
|
|
}
|
|
|
|
private void PageUp()
|
|
{
|
|
if (_currentPage < TotalPages)
|
|
{
|
|
_currentPage++;
|
|
}
|
|
}
|
|
|
|
private void SelectAllCards()
|
|
{
|
|
if (AppState.DevicesFrameSelectedDevices.Any())
|
|
{
|
|
AppState.DevicesFrameSelectedDevices.Clear();
|
|
}
|
|
else
|
|
{
|
|
foreach (var device in _filteredDevices)
|
|
{
|
|
if (!AppState.DevicesFrameSelectedDevices.Contains(device.ID))
|
|
{
|
|
AppState.DevicesFrameSelectedDevices.Add(device.ID);
|
|
}
|
|
};
|
|
}
|
|
|
|
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameSelectedDevices));
|
|
}
|
|
|
|
private void ToggleSortDirection()
|
|
{
|
|
if (_sortDirection == ListSortDirection.Ascending)
|
|
{
|
|
_sortDirection = ListSortDirection.Descending;
|
|
}
|
|
else
|
|
{
|
|
_sortDirection = ListSortDirection.Ascending;
|
|
}
|
|
}
|
|
}
|
|
}
|