+ Copy and paste on a remote computer to install the agent.
+
+
+
+
+
+
+ Windows 10/11 (32-Bit and 64-Bit)
+
+
+
+
+
+
+
+
+
+
+ Ubuntu (64-Bit)
+
+
+
+
+
+
+
+
+
+
+ Manjaro (64-Bit)
+
+
+
+
+
+
+
+
+
+
+ Attended Support
+
+
+ You can upload custom versions of the attended support client (i.e. "Remotely_Desktop.exe")
+ and send the download link to end-users when they need support. For example, you could sign
+ the EXE with a commercial certificate so the end-user (hopefully) doesn't see a SmartScreen
+ warning about the file.
+
+
+ NOTE: These binaries must be manually updated each time a new Docker image is pulled.
+
+
+
+
+
+ Windows 10/11 (64-Bit)
+
+
+ @if (_winX64File?.Exists != true)
+ {
+
+ Note: A custom binary for this file hasn't been uploaded yet.
+
+ }
+ else if (_winX64File?.PhysicalPath is not null)
+ {
+
+ Created Date: @(_winX64CreatedDate) UTC
+
+ }
+
+ @if (_isServerAdmin)
+ {
+
+
+
+
+ Upload File
+
+
+
+
+ Note: Only server admins will have this button.
+
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+ Windows 10/11 (32-Bit)
+
+
+ @if (_winX86File?.Exists != true)
+ {
+
+ Note: A custom binary for this file hasn't been uploaded yet.
+
+ }
+ else if (_winX86File?.PhysicalPath is not null)
+ {
+
+ Created Date: @(_winX86CreatedDate) UTC
+
+ }
+
+ @if (_isServerAdmin)
+ {
+
+
+
+
+ Upload File
+
+
+
+
+ Note: Only server admins will have this button.
+
+
+
+ }
+
+
+
+
+
+
+
+
+@code {
+ private static readonly SemaphoreSlim _writeLock = new(1, 1);
+ private string _organizationId = string.Empty;
+ private string _windowsScript = string.Empty;
+ private string _ubuntuScript = string.Empty;
+ private string _manjaroScript = string.Empty;
+ private string _appDataDir = string.Empty;
+ private IFileInfo? _winX64File;
+ private IFileInfo? _winX86File;
+ private string _winX64Uri = string.Empty;
+ private string _winX86Uri = string.Empty;
+ private bool _isLoading = false;
+ private string _loadingMessage = string.Empty;
+ private bool _isServerAdmin;
+ private DateTime _winX64CreatedDate;
+ private DateTime _winX86CreatedDate;
+
+ protected override async Task OnInitializedAsync()
+ {
+ var userResult = await Auth.GetUser();
+ if (!userResult.IsSuccess)
+ {
+ NavMan.NavigateTo("/", false);
+ return;
+ }
+
+ _isServerAdmin = userResult.Value.IsServerAdmin;
+ _organizationId = userResult.Value.OrganizationID;
+ _appDataDir = Path.Combine(HostEnv.ContentRootPath, "AppData");
+ _winX64Uri = $"{NavMan.BaseUri}api/custom-binaries/win-x64/desktop/{_organizationId}";
+ _winX86Uri = $"{NavMan.BaseUri}api/custom-binaries/win-x86/desktop/{_organizationId}";
+
+ SetFileInfos();
+
+ SetScriptContent();
+
+ await base.OnInitializedAsync();
+ }
+
+ private async Task CopyScriptToClipboard(string script)
+ {
+ try
+ {
+ var result = await JsInterop.SetClipboardText(script);
+ if (result)
+ {
+ Toasts.ShowToast2("Script copied to clipboard", ToastType.Success);
+ return;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Error while copying script to clipboard.");
+ }
+ Toasts.ShowToast2("Failed to set clipboard content", ToastType.Error);
+ }
+
+ private async Task CopyUriToClipboard(string uri)
+ {
+ try
+ {
+ var result = await JsInterop.SetClipboardText(uri);
+ if (result)
+ {
+ Toasts.ShowToast2("URI copied to clipboard", ToastType.Success);
+ return;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Error while copying URI to clipboard.");
+ }
+ Toasts.ShowToast2("Failed to set clipboard content", ToastType.Error);
+ }
+
+ private string GetLinuxScript(string platformId)
+ {
+ return
+ $"sudo rm -f /tmp/Install-Remotely.sh && " +
+ $"sudo wget -q -O /tmp/Install-Remotely.sh {NavMan.BaseUri}api/ClientDownloads/{platformId}/{_organizationId} && " +
+ $"sudo chmod +x /tmp/Install-Remotely.sh && " +
+ $"sudo /tmp/Install-Remotely.sh";
+ }
+
+
+ private async Task HandleWin64FileChanged(InputFileChangeEventArgs ev)
+ {
+ await TrySaveFile(_winX64File, ev);
+ }
+
+ private async Task HandleWin86FileChanged(InputFileChangeEventArgs ev)
+ {
+ await TrySaveFile(_winX86File, ev);
+ }
+
+ private void SetFileInfos()
+ {
+ _winX64File = HostEnv.ContentRootFileProvider.GetFileInfo("AppData/Win-x64/Remotely_Desktop.exe");
+ if (_winX64File?.Exists == true && _winX64File?.PhysicalPath is not null)
+ {
+ var fileInfo = new FileInfo(_winX64File.PhysicalPath);
+ _winX64CreatedDate = fileInfo.CreationTimeUtc;
+ }
+
+ _winX86File = HostEnv.ContentRootFileProvider.GetFileInfo("AppData/Win-x86/Remotely_Desktop.exe");
+ if (_winX86File?.Exists == true && _winX86File?.PhysicalPath is not null)
+ {
+ var fileInfo = new FileInfo(_winX86File.PhysicalPath);
+ _winX86CreatedDate = fileInfo.CreationTimeUtc;
+ }
+ }
+
+ private void SetScriptContent()
+ {
+ _windowsScript =
+ $"Invoke-WebRequest -Uri '{NavMan.BaseUri}api/ClientDownloads/WindowsInstaller/{_organizationId}' -OutFile \"${{env:TEMP}}\\Install-Remotely.ps1\" -UseBasicParsing;" +
+ "Start-Process -FilePath 'powershell.exe' -ArgumentList (\"-executionpolicy\", \"bypass\", \"-f\", \"${env:TEMP}\\Install-Remotely.ps1\") -Verb RunAs;";
+
+ _ubuntuScript = GetLinuxScript("UbuntuInstaller-x64");
+
+ _manjaroScript = GetLinuxScript("ManjaroInstaller-x64");
+ }
+
+ private async Task TrySaveFile(IFileInfo? fileInfo, InputFileChangeEventArgs ev)
+ {
+ // Since this is server-side Blazor, it would be impossible to
+ // get to this point without being a server admin. But we'll add
+ // it here to prevent any issues caused by future changes.
+ if (!_isServerAdmin)
+ {
+ return;
+ }
+
+ await _writeLock.WaitAsync();
+ try
+ {
+ if (fileInfo?.PhysicalPath is null)
+ {
+ Toasts.ShowToast2("Unable to find save path", ToastType.Error);
+ return;
+ }
+
+ _loadingMessage = "Uploading file";
+ _isLoading = true;
+ await InvokeAsync(StateHasChanged);
+
+ _ = Directory.CreateDirectory(Path.GetDirectoryName(fileInfo.PhysicalPath)!);
+ await using var rs = ev.File.OpenReadStream(500_000_000);
+ await using var fs = File.Open(fileInfo.PhysicalPath, FileMode.Create);
+ await rs.CopyToAsync(fs);
+ Toasts.ShowToast2("Custom binary uploaded successfully", ToastType.Success);
+ SetFileInfos();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Error while uploading custom binary.");
+ Toasts.ShowToast2("Failed to upload custom binary", ToastType.Error);
+ }
+ finally
+ {
+ _writeLock.Release();
+ _isLoading = false;
+ await InvokeAsync(StateHasChanged);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Server/Components/Pages/Downloads.razor b/Server/Components/Pages/Downloads.razor
index 93c8f74ca..48668e813 100644
--- a/Server/Components/Pages/Downloads.razor
+++ b/Server/Components/Pages/Downloads.razor
@@ -9,6 +9,14 @@
@inject NavigationManager NavManager
@inject ILogger Logger
+@if (_isAuthenticated)
+{
+
+ Check out the Deploy page for a copy-and-paste deploy script!
+