mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Add "update all" button on server config page.
This commit is contained in:
parent
1e1dd129c0
commit
adbab10f93
10
README.md
10
README.md
@ -69,7 +69,7 @@ The following steps will configure your Windows 10 machine for building the Remo
|
||||
* If the site will be public-facing, configure your bindings in IIS.
|
||||
* An SSL certificate for HTTPS is recommended. You can install one for free using Let's Encrypt.
|
||||
* Resources: https://letsencrypt.org/, https://certifytheweb.com/
|
||||
* Documentation for hosting in IIS can be found here: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index?view=aspnetcore-3.1
|
||||
* Documentation for hosting in IIS can be found here: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis
|
||||
* There is no default account. You must create the first one via the Register page, which will create an account that is both a server and organization admin.
|
||||
|
||||
## Hosting a Server (Ubuntu)
|
||||
@ -83,13 +83,13 @@ The following steps will configure your Windows 10 machine for building the Remo
|
||||
* More information: https://letsencrypt.org/, https://certbot.eff.org/
|
||||
* Alternatively, you can build from source (using RuntimeIdentifier "linux-x64" for the server) and copy the server files to the site folder.
|
||||
* Change values in appsettings.json for your environment. Make a copy named `appsettings.Production.json` (see Configuration section below).
|
||||
* Documentation for hosting behind Nginx can be found here: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1
|
||||
* Documentation for hosting behind Nginx can be found here: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx
|
||||
* There is no default account. You must create the first one via the Register page, which will create an account that is both a server and organization admin.
|
||||
|
||||
|
||||
## Hosting Scenarios
|
||||
There are countless ways to host an ASP.NET Core app, and I can't document or automate all of them. For hosting scenarios aside from the above two, please refer to Microsoft's documentation.
|
||||
- https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/?view=aspnetcore-3.1
|
||||
- https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/
|
||||
|
||||
## Admin Accounts
|
||||
The first account created will be an admin for both the server and the organization that's created for the account.
|
||||
@ -115,7 +115,7 @@ Likewise, `appsettings.Development.json` can be used while developing in Visual
|
||||
|
||||
Whenever there's a reference to `appsettings.json` in this document, it refers to whichever file is currently being used.
|
||||
|
||||
For more information on configuring ASP.NET Core, see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1.
|
||||
For more information on configuring ASP.NET Core, see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/.
|
||||
|
||||
* AllowApiLogin: Whether to allow logging in via the API controller. API access tokens are recommended over this approach.
|
||||
* BannedDevices: An array of device IDs, names, or IP addresses to ban. When they try to connect, an uninstall command will immediately be sent back.
|
||||
@ -155,7 +155,7 @@ You can change database by changing `DBProvider` in `ApplicationOptions` to `SQL
|
||||
* On Windows Servers, the above logs can also be written to the Windows Event Log.
|
||||
* This is enabled in appsettings.json by setting EnableWindowsEventLog to true.
|
||||
* You can configure logging levels and other settings in appsetttings.json.
|
||||
* More information: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1
|
||||
* More information: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/
|
||||
|
||||
## Remote Control Client Requirements
|
||||
* Windows: Only the latest version of Windows 10 is tested. Windows 7 and 8.1 should work, though performance will be reduced on Windows 7.
|
||||
|
||||
@ -33,12 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csp
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Desktop.Linux", "Desktop.Linux\Desktop.Linux.csproj", "{FF7FD66A-B3E2-4D39-A457-A00C94EDE9E6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution Folder", "{CF0B7B5D-5FAD-42C9-8803-123D2013EC13}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Desktop.Win", "Desktop.Win\Desktop.Win.csproj", "{6B726FC4-A907-4813-BF38-3342E02AA8D2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Agent.Installer.Win", "Agent.Installer.Win\Agent.Installer.Win.csproj", "{A3D0368C-0850-4614-B5B5-41B9D5135AA9}"
|
||||
@ -52,6 +46,8 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2176596E-12DA-4766-96E1-4D23EA7DBEC8}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
@if (Model.IsServerAdmin)
|
||||
{
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
<h5 class="mt-3">Server Info</h5>
|
||||
|
||||
<div class="row">
|
||||
@ -24,6 +26,11 @@
|
||||
<label>Devices Outdated</label>
|
||||
<br />
|
||||
<span class="badge badge-primary p-2">@(Model.OutdatedDevices?.Count() ?? 0)</span>
|
||||
<form class="d-inline-block" method="post" asp-page-handler="UpdateAll">
|
||||
<button class="btn btn-secondary btn-sm ml-1" type="submit" title="Update All">
|
||||
<span class="fas fa-sync"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="d-inline-block text-center mr-2">
|
||||
<label>Devices Total</label>
|
||||
@ -46,8 +53,6 @@
|
||||
|
||||
<h5>Application Settings</h5>
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Remotely.Server.Hubs;
|
||||
@ -20,17 +21,26 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class ServerConfigModel : PageModel
|
||||
{
|
||||
private readonly IHubContext<AgentHub> _agentHubContext;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IDataService _dataService;
|
||||
private readonly IEmailSenderEx _emailSender;
|
||||
private readonly IWebHostEnvironment _hostEnv;
|
||||
private readonly UserManager<RemotelyUser> _userManager;
|
||||
|
||||
public ServerConfigModel(IConfiguration configuration,
|
||||
IWebHostEnvironment hostEnv,
|
||||
IWebHostEnvironment hostEnv,
|
||||
UserManager<RemotelyUser> userManager,
|
||||
IDataService dataService,
|
||||
IEmailSenderEx emailSender)
|
||||
IEmailSenderEx emailSender,
|
||||
IHubContext<AgentHub> agentHubContext)
|
||||
{
|
||||
Configuration = configuration;
|
||||
HostEnv = hostEnv;
|
||||
UserManager = userManager;
|
||||
DataService = dataService;
|
||||
EmailSender = emailSender;
|
||||
_configuration = configuration;
|
||||
_hostEnv = hostEnv;
|
||||
_userManager = userManager;
|
||||
_dataService = dataService;
|
||||
_emailSender = emailSender;
|
||||
_agentHubContext = agentHubContext;
|
||||
}
|
||||
|
||||
public enum DBProviders
|
||||
@ -46,9 +56,8 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
[BindProperty]
|
||||
public ConnectionStringsModel ConnectionStrings { get; set; } = new ConnectionStringsModel();
|
||||
|
||||
public bool IsServerAdmin { get; set; }
|
||||
public string Environment { get; set; }
|
||||
|
||||
public bool IsServerAdmin { get; set; }
|
||||
public IEnumerable<string> OutdatedDevices { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
@ -57,39 +66,29 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
private IConfiguration Configuration { get; }
|
||||
private IDataService DataService { get; }
|
||||
private IEmailSenderEx EmailSender { get; }
|
||||
private IWebHostEnvironment HostEnv { get; }
|
||||
private UserManager<RemotelyUser> UserManager { get; }
|
||||
|
||||
public async Task<IActionResult> OnGet()
|
||||
{
|
||||
IsServerAdmin = (await UserManager.GetUserAsync(User)).IsServerAdmin;
|
||||
IsServerAdmin = (await _userManager.GetUserAsync(User)).IsServerAdmin;
|
||||
if (!IsServerAdmin)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var highestVersion = AgentHub.ServiceConnections.Values.Max(x => Version.TryParse(x.AgentVersion, out var result) ? result : default);
|
||||
OutdatedDevices = AgentHub.ServiceConnections.Values
|
||||
.Where(x => Version.TryParse(x.AgentVersion, out var result) ? result != highestVersion : false)
|
||||
.Select(x => x.ID);
|
||||
OutdatedDevices = GetOutdatedDevices();
|
||||
|
||||
Environment = HostEnv.EnvironmentName;
|
||||
Environment = _hostEnv.EnvironmentName;
|
||||
|
||||
Configuration.Bind("ApplicationOptions", AppSettingsInput);
|
||||
Configuration.Bind("ConnectionStrings", ConnectionStrings);
|
||||
ServerAdmins = DataService.GetServerAdmins();
|
||||
_configuration.Bind("ApplicationOptions", AppSettingsInput);
|
||||
_configuration.Bind("ConnectionStrings", ConnectionStrings);
|
||||
ServerAdmins = _dataService.GetServerAdmins();
|
||||
|
||||
return Page();
|
||||
}
|
||||
public async Task<IActionResult> OnPostSaveAndTestSmtpAsync()
|
||||
{
|
||||
var result = await OnPostSaveAsync();
|
||||
var user = DataService.GetUserByName(User.Identity.Name);
|
||||
var success = await EmailSender.SendEmailAsync(user.Email, "Remotely Test Email", "Congratulations! Your SMTP settings are working!", user.OrganizationID);
|
||||
var user = _dataService.GetUserByName(User.Identity.Name);
|
||||
var success = await _emailSender.SendEmailAsync(user.Email, "Remotely Test Email", "Congratulations! Your SMTP settings are working!", user.OrganizationID);
|
||||
if (success)
|
||||
{
|
||||
StatusMessage = "Test email sent. Check your inbox (including spam folder).";
|
||||
@ -102,7 +101,7 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
}
|
||||
public async Task<IActionResult> OnPostSaveAsync()
|
||||
{
|
||||
IsServerAdmin = (await UserManager.GetUserAsync(User)).IsServerAdmin;
|
||||
IsServerAdmin = (await _userManager.GetUserAsync(User)).IsServerAdmin;
|
||||
if (!IsServerAdmin)
|
||||
{
|
||||
return Unauthorized();
|
||||
@ -114,7 +113,7 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
}
|
||||
|
||||
var configReloaded = false;
|
||||
Configuration.GetReloadToken().RegisterChangeCallback((e) =>
|
||||
_configuration.GetReloadToken().RegisterChangeCallback((e) =>
|
||||
{
|
||||
configReloaded = true;
|
||||
}, null);
|
||||
@ -131,17 +130,39 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostUpdateAllAsync()
|
||||
{
|
||||
var outdatedDevices = GetOutdatedDevices();
|
||||
if (!outdatedDevices.Any())
|
||||
{
|
||||
StatusMessage = "No agents need updating.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
var agentConnections = AgentHub.ServiceConnections.Where(x => outdatedDevices.Contains(x.Value.ID));
|
||||
await _agentHubContext.Clients.Clients(agentConnections.Select(x => x.Key)).SendAsync("ReinstallAgent");
|
||||
StatusMessage = $"Update command sent to {agentConnections.Count()} agents.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetOutdatedDevices()
|
||||
{
|
||||
var highestVersion = AgentHub.ServiceConnections.Values.Max(x => Version.TryParse(x.AgentVersion, out var result) ? result : default);
|
||||
return AgentHub.ServiceConnections.Values
|
||||
.Where(x => Version.TryParse(x.AgentVersion, out var result) ? result != highestVersion : false)
|
||||
.Select(x => x.ID);
|
||||
}
|
||||
|
||||
private async Task SaveAppSettings()
|
||||
{
|
||||
string savePath;
|
||||
var prodSettings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Production.json");
|
||||
var stagingSettings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Staging.json");
|
||||
var settings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.json");
|
||||
if (HostEnv.IsProduction() && prodSettings.Exists)
|
||||
var prodSettings = _hostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Production.json");
|
||||
var stagingSettings = _hostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Staging.json");
|
||||
var settings = _hostEnv.ContentRootFileProvider.GetFileInfo("appsettings.json");
|
||||
if (_hostEnv.IsProduction() && prodSettings.Exists)
|
||||
{
|
||||
savePath = prodSettings.PhysicalPath;
|
||||
}
|
||||
else if (HostEnv.IsStaging() && stagingSettings.Exists)
|
||||
else if (_hostEnv.IsStaging() && stagingSettings.Exists)
|
||||
{
|
||||
savePath = stagingSettings.PhysicalPath;
|
||||
}
|
||||
@ -155,13 +176,13 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
}
|
||||
|
||||
var settingsJson = JsonSerializer.Deserialize<IDictionary<string, object>>(await System.IO.File.ReadAllTextAsync(savePath));
|
||||
AppSettingsInput.IceServers = Configuration.GetSection("ApplicationOptions:IceServers").Get<IceServerModel[]>();
|
||||
AppSettingsInput.IceServers = _configuration.GetSection("ApplicationOptions:IceServers").Get<IceServerModel[]>();
|
||||
settingsJson["ApplicationOptions"] = AppSettingsInput;
|
||||
settingsJson["ConnectionStrings"] = ConnectionStrings;
|
||||
|
||||
await System.IO.File.WriteAllTextAsync(savePath, JsonSerializer.Serialize(settingsJson, new JsonSerializerOptions() { WriteIndented = true }));
|
||||
|
||||
await DataService.UpdateServerAdmins(ServerAdmins, User.Identity.Name);
|
||||
await _dataService.UpdateServerAdmins(ServerAdmins, User.Identity.Name);
|
||||
}
|
||||
|
||||
public class AppSettingsModel
|
||||
|
||||
Loading…
Reference in New Issue
Block a user