Remotely/Server/Components/Devices/DeviceCard.razor.cs
Jared Goodwin 3f8d63c2c0
Azure Pipelines, Docker, and embedded server data. (#543)
* 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.
2022-12-31 19:10:40 -08:00

302 lines
10 KiB
C#

using Immense.RemoteControl.Server.Abstractions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Remotely.Server.Auth;
using Remotely.Server.Enums;
using Remotely.Server.Hubs;
using Remotely.Server.Models;
using Remotely.Server.Services;
using Remotely.Shared.Enums;
using Remotely.Shared.Models;
using Remotely.Shared.Utilities;
using Remotely.Shared.ViewModels;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace Remotely.Server.Components.Devices
{
public partial class DeviceCard : AuthComponentBase, IDisposable
{
private readonly ConcurrentDictionary<string, double> _fileUploadProgressLookup = new();
private ElementReference _card;
private Theme _theme;
private Version _currentVersion = new();
[Parameter]
public Device Device { get; set; }
[CascadingParameter]
public DevicesFrame ParentFrame { get; set; }
[Inject]
private IClientAppState AppState { get; set; }
[Inject]
private ICircuitConnection CircuitConnection { get; set; }
[Inject]
private IServiceHubSessionCache ServiceSessionCache { get; init; }
[Inject]
private IUpgradeService UpgradeService { get; init; }
[Inject]
private IDataService DataService { get; set; }
private bool IsExpanded => GetCardState() == DeviceCardState.Expanded;
private bool IsOutdated =>
Version.TryParse(Device.AgentVersion, out var result) &&
result < _currentVersion;
private bool IsSelected => AppState.DevicesFrameSelectedDevices.Contains(Device.ID);
[Inject]
private IJsInterop JsInterop { get; set; }
[Inject]
private IModalService ModalService { get; set; }
[Inject]
private IToastService ToastService { get; set; }
public void Dispose()
{
AppState.PropertyChanged -= AppState_PropertyChanged;
CircuitConnection.MessageReceived -= CircuitConnection_MessageReceived;
GC.SuppressFinalize(this);
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_theme = await AppState.GetEffectiveTheme();
_currentVersion = UpgradeService.GetCurrentVersion();
AppState.PropertyChanged += AppState_PropertyChanged;
CircuitConnection.MessageReceived += CircuitConnection_MessageReceived;
}
private void AppState_PropertyChanged(object sender, System.ComponentModel.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 e)
{
switch (e.EventName)
{
case CircuitEventName.DeviceUpdate:
case CircuitEventName.DeviceWentOffline:
{
if (e.Params?.FirstOrDefault() is Device device &&
device.ID == Device?.ID)
{
Device = device;
InvokeAsync(StateHasChanged);
}
break;
}
default:
break;
}
}
private void ContextMenuOpening(MouseEventArgs args)
{
if (GetCardState() == DeviceCardState.Normal)
{
JsInterop.OpenWindow($"/device-details/{Device.ID}", "_blank");
}
}
private async Task ExpandCard(MouseEventArgs args)
{
if (AppState.DevicesFrameFocusedDevice == Device.ID)
{
if (AppState.DevicesFrameFocusedCardState == DeviceCardState.Normal)
{
AppState.DevicesFrameFocusedCardState = DeviceCardState.Expanded;
}
return;
}
AppState.DevicesFrameFocusedDevice = Device.ID;
AppState.DevicesFrameFocusedCardState = DeviceCardState.Expanded;
JsInterop.ScrollToElement(_card);
await CircuitConnection.TriggerHeartbeat(Device.ID);
}
private DeviceCardState GetCardState()
{
if (AppState.DevicesFrameFocusedDevice == Device.ID)
{
return AppState.DevicesFrameFocusedCardState;
}
return DeviceCardState.Normal;
}
private string GetCardStateClass(Device device)
{
if (AppState.DevicesFrameFocusedDevice == device.ID)
{
return AppState.DevicesFrameFocusedCardState.ToString().ToLower();
}
return string.Empty;
}
private string GetProgressMessage(string key)
{
if (_fileUploadProgressLookup.TryGetValue(key, out var value))
{
return $"{MathHelper.GetFormattedPercent(value)} - {key}";
}
return string.Empty;
}
private void HandleHeaderClick()
{
if (IsExpanded)
{
SetCardStateNormal();
}
}
private async Task HandleValidSubmit()
{
DataService.UpdateDevice(Device.ID,
Device.Tags,
Device.Alias,
Device.DeviceGroupID,
Device.Notes);
ToastService.ShowToast("Device settings saved.");
await CircuitConnection.TriggerHeartbeat(Device.ID);
}
private async Task OnFileInputChanged(InputFileChangeEventArgs args)
{
ToastService.ShowToast("File upload started.");
var fileId = await DataService.AddSharedFile(args.File, User.OrganizationID, OnFileInputProgress);
var transferId = Guid.NewGuid().ToString();
var result = await CircuitConnection.TransferFileFromBrowserToAgent(Device.ID, transferId, new[] { fileId });
if (!result)
{
ToastService.ShowToast("Device not found.", classString: "bg-warning");
}
else
{
ToastService.ShowToast("File upload completed.");
}
}
private void OnFileInputProgress(double percentComplete, string fileName)
{
if (_fileUploadProgressLookup.TryGetValue(fileName, out var existingValue) &&
percentComplete < 1 &&
percentComplete - existingValue < .05)
{
// Avoid too frequent of updates.
return;
}
_fileUploadProgressLookup.AddOrUpdate(fileName, percentComplete, (k, v) => percentComplete);
InvokeAsync(StateHasChanged);
}
private void OpenDeviceDetails()
{
JsInterop.OpenWindow($"/device-details/{Device.ID}", "_blank");
}
private void SetCardStateNormal()
{
AppState.DevicesFrameFocusedDevice = null;
AppState.DevicesFrameFocusedCardState = DeviceCardState.Normal;
}
private void ShowAllDisks()
{
var disksString = JsonSerializer.Serialize(Device.Drives, JsonSerializerHelper.IndentedOptions);
void modalBody(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)
{
builder.AddMarkupContent(0, $"<div style='white-space: pre'>{disksString}</div>");
}
ModalService.ShowModal($"All Disks for {Device.DeviceName}", modalBody);
}
private void StartChat()
{
var existingSession = AppState.DevicesFrameChatSessions.FirstOrDefault(x => x.DeviceId == Device.ID);
if (existingSession is null)
{
AppState.DevicesFrameChatSessions.Add(new ChatSession()
{
DeviceId = Device.ID,
DeviceName = Device.DeviceName,
IsExpanded = true
});
}
else
{
existingSession.IsExpanded = true;
}
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameChatSessions));
}
private void StartRemoteControl(bool viewOnly)
{
if (!ServiceSessionCache.TryGetConnectionId(Device.ID, out var connectionId))
{
ToastService.ShowToast("Device connection not found", classString: "bg-danger");
return;
}
CircuitConnection.RemoteControl(Device.ID, viewOnly);
}
private void ToggleIsSelected(ChangeEventArgs args)
{
var isSelected = (bool)args.Value;
if (isSelected)
{
AppState.DevicesFrameSelectedDevices.Add(Device.ID);
}
else
{
AppState.DevicesFrameSelectedDevices.Remove(Device.ID);
}
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameSelectedDevices));
}
private async Task UninstallAgent()
{
var result = await JsInterop.Confirm("Are you sure you want to uninstall this agent? This is permanent!");
if (result)
{
await CircuitConnection.UninstallAgents(new[] { Device.ID });
AppState.DevicesFrameFocusedDevice = null;
AppState.DevicesFrameFocusedCardState = DeviceCardState.Normal;
ParentFrame.Refresh();
}
}
}
}