(this, CircuitConnection.ConnectionId);
- GC.SuppressFinalize(this);
- }
protected override Task OnAfterRenderAsync(bool firstRender)
{
diff --git a/Server/Components/Layout/MainLayout.razor b/Server/Components/Layout/MainLayout.razor
index 82c4dbcde..c8a88b432 100644
--- a/Server/Components/Layout/MainLayout.razor
+++ b/Server/Components/Layout/MainLayout.razor
@@ -34,6 +34,12 @@
+
+ An unhandled error has occurred.
+
Reload
+
🗙
+
+
\ No newline at end of file
diff --git a/Server/Components/Layout/NavMenu.razor b/Server/Components/Layout/NavMenu.razor
index 773a95a86..3c016ea0e 100644
--- a/Server/Components/Layout/NavMenu.razor
+++ b/Server/Components/Layout/NavMenu.razor
@@ -1,7 +1,7 @@
@implements IDisposable
@inject NavigationManager NavigationManager
@inject IAuthService AuthService
-@inject IApplicationConfig AppConfig
+@inject IDataService DataService
@inject IDataService DataService
@@ -122,7 +122,7 @@
Log in
- @if (AppConfig.MaxOrganizationCount < 0 || DataService.GetOrganizationCount() < AppConfig.MaxOrganizationCount)
+ @if (_isRegistrationEnabled)
{
Register
@@ -139,7 +139,7 @@
@code {
private bool collapseNavMenu = true;
-
+ private bool _isRegistrationEnabled;
private RemotelyUser? _user;
private Organization? _organization;
private string? _currentUrl;
@@ -151,6 +151,9 @@
protected override async Task OnInitializedAsync()
{
+ var settings = await DataService.GetSettings();
+ _isRegistrationEnabled = settings.MaxOrganizationCount < 0 || DataService.GetOrganizationCount() < settings.MaxOrganizationCount;
+
await base.OnInitializedAsync();
_currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
diff --git a/Server/Components/Pages/Index.razor b/Server/Components/Pages/Index.razor
index 3385cf623..473d88b44 100644
--- a/Server/Components/Pages/Index.razor
+++ b/Server/Components/Pages/Index.razor
@@ -1,5 +1,4 @@
@page "/"
-@inject IApplicationConfig AppConfig
@inject IDataService DataService
@@ -13,7 +12,7 @@
Log In
- @if (AppConfig.MaxOrganizationCount < 0 || DataService.GetOrganizationCount() < AppConfig.MaxOrganizationCount)
+ @if (_isRegistrationEnabled)
{
Register
}
@@ -24,3 +23,15 @@
+
+@code
+{
+ private bool _isRegistrationEnabled;
+
+ protected override async Task OnInitializedAsync()
+ {
+ var settings = await DataService.GetSettings();
+ _isRegistrationEnabled = settings.MaxOrganizationCount < 0 || DataService.GetOrganizationCount() < settings.MaxOrganizationCount;
+ await base.OnInitializedAsync();
+ }
+}
\ No newline at end of file
diff --git a/Server/Components/Pages/ServerConfig.razor b/Server/Components/Pages/ServerConfig.razor
index 9b7220b5c..15427ae09 100644
--- a/Server/Components/Pages/ServerConfig.razor
+++ b/Server/Components/Pages/ServerConfig.razor
@@ -121,18 +121,6 @@
-
- Database Provider
-
-
- @foreach (var provider in Enum.GetValues())
- {
- @provider
- }
-
-
-
-
Enable Remote Control Recording
@@ -357,34 +345,6 @@
- Connection Strings
-
-
- PostgreSQL
-
-
-
-
-
-
-
- SQLite
-
-
-
-
-
-
-
-
- SQL Server
-
-
-
-
-
-
-
Save
diff --git a/Server/Components/Pages/ServerConfig.razor.cs b/Server/Components/Pages/ServerConfig.razor.cs
index 8429037dc..86078ca42 100644
--- a/Server/Components/Pages/ServerConfig.razor.cs
+++ b/Server/Components/Pages/ServerConfig.razor.cs
@@ -2,123 +2,18 @@
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.SignalR;
-using Remotely.Server.Components;
using Remotely.Server.Hubs;
+using Remotely.Server.Models;
using Remotely.Server.Services;
using Remotely.Shared.Entities;
-using Remotely.Shared.Enums;
using Remotely.Shared.Interfaces;
-using System.ComponentModel.DataAnnotations;
using System.Text.Json;
-using System.Text.Json.Serialization;
namespace Remotely.Server.Components.Pages;
-public class AppSettingsModel
-{
- [Display(Name = "Allow API Login")]
- public bool AllowApiLogin { get; set; }
-
- [Display(Name = "Banned Devices")]
- public List BannedDevices { get; set; } = new();
-
- [Display(Name = "Data Retention (days)")]
- public double DataRetentionInDays { get; set; }
-
- [Display(Name = "Database Provider")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
- public DbProvider DBProvider { get; set; }
-
- [Display(Name = "Enable Remote Control Recording")]
- public bool EnableRemoteControlRecording { get; set; }
-
- [Display(Name = "Enable Windows Event Log")]
- public bool EnableWindowsEventLog { get; set; }
-
- [Display(Name = "Enforce Attended Access")]
- public bool EnforceAttendedAccess { get; set; }
-
- [Display(Name = "Force Client HTTPS")]
- public bool ForceClientHttps { get; set; }
-
- [Display(Name = "Known Proxies")]
- public List KnownProxies { get; set; } = new();
-
- [Display(Name = "Max Concurrent Updates")]
- public int MaxConcurrentUpdates { get; set; }
-
- [Display(Name = "Max Organizations")]
- public int MaxOrganizationCount { get; set; }
- [Display(Name = "Message of the Day")]
- public string? MessageOfTheDay { get; set; }
-
- [Display(Name = "Redirect To HTTPS")]
- public bool RedirectToHttps { get; set; }
-
- [Display(Name = "Remote Control Notify User")]
- public bool RemoteControlNotifyUser { get; set; }
-
- [Display(Name = "Remote Control Requires Authentication")]
- public bool RemoteControlRequiresAuthentication { get; set; }
-
- [Display(Name = "Remote Control Session Limit")]
- public double RemoteControlSessionLimit { get; set; }
-
- [Display(Name = "Require 2FA")]
- public bool Require2FA { get; set; }
-
- [Display(Name = "SMTP Display Name")]
- public string? SmtpDisplayName { get; set; }
-
- [Display(Name = "SMTP Email")]
- [EmailAddress]
- public string? SmtpEmail { get; set; }
-
- [Display(Name = "SMTP Host")]
- public string? SmtpHost { get; set; }
- [Display(Name = "SMTP Local Domain")]
- public string? SmtpLocalDomain { get; set; }
-
- [Display(Name = "SMTP Check Certificate Revocation")]
- public bool SmtpCheckCertificateRevocation { get; set; }
-
- [Display(Name = "SMTP Password")]
- public string? SmtpPassword { get; set; }
-
- [Display(Name = "SMTP Port")]
- public int SmtpPort { get; set; }
-
- [Display(Name = "SMTP Username")]
- public string? SmtpUserName { get; set; }
-
- [Display(Name = "Theme")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
- public Theme Theme { get; set; }
-
- [Display(Name = "Trusted CORS Origins")]
- public List TrustedCorsOrigins { get; set; } = new();
-
- [Display(Name = "Use HSTS")]
- public bool UseHsts { get; set; }
-
- [Display(Name = "Use HTTP Logging")]
- public bool UseHttpLogging { get; set; }
-}
-
-public class ConnectionStringsModel
-{
- [Display(Name = "PostgreSQL")]
- public string? PostgreSQL { get; set; }
-
- [Display(Name = "SQLite")]
- public string? SQLite { get; set; }
-
- [Display(Name = "SQL Server")]
- public string? SQLServer { get; set; }
-}
-
public partial class ServerConfig : AuthComponentBase
{
+ private readonly List _userList = new();
private string? _alertMessage;
private string? _bannedDeviceSelected;
private string? _bannedDeviceToAdd;
@@ -126,54 +21,44 @@ public partial class ServerConfig : AuthComponentBase
private string? _knownProxySelected;
private string? _knownProxyToAdd;
- private bool _showMyOrgAdminsOnly = true;
private bool _showAdminsOnly;
-
+ private bool _showMyOrgAdminsOnly = true;
private string? _trustedCorsOriginSelected;
private string? _trustedCorsOriginToAdd;
- private readonly List _userList = new();
-
-
[Inject]
- private IHubContext AgentHubContext { get; init; } = null!;
+ public required IHubContext AgentHubContext { get; init; }
[Inject]
- private IConfiguration Configuration { get; init; } = null!;
-
- private ConnectionStringsModel ConnectionStrings { get; } = new();
+ public required ICircuitManager CircuitManager { get; init; }
[Inject]
- private IDataService DataService { get; init; } = null!;
+ public required IDataService DataService { get; init; }
[Inject]
- private IEmailSenderEx EmailSender { get; init; } = null!;
+ public required IEmailSenderEx EmailSender { get; init; }
[Inject]
- private IWebHostEnvironment HostEnv { get; init; } = null!;
+ public required IWebHostEnvironment HostEnv { get; init; }
[Inject]
- private ILogger Logger { get; init; } = null!;
+ public required ILogger Logger { get; init; }
[Inject]
- private IAgentHubSessionCache ServiceSessionCache { get; init; } = null!;
-
- private AppSettingsModel Input { get; } = new();
+ public required IModalService ModalService { get; init; }
[Inject]
- private IModalService ModalService { get; init; } = null!;
+ public required IAgentHubSessionCache ServiceSessionCache { get; init; }
[Inject]
- private IUpgradeService UpgradeService { get; init; } = null!;
+ public required IToastService ToastService { get; init; }
[Inject]
- private ICircuitManager CircuitManager { get; init; } = null!;
+ public required IUpgradeService UpgradeService { get; init; }
- private IEnumerable OutdatedDevices => GetOutdatedDevices();
-
- [Inject]
- private IToastService ToastService { get; init; } = null!;
+ private SettingsModel Input { get; set; } = new();
+ private IEnumerable OutdatedDevices => GetOutdatedDevices();
private int TotalDevices => DataService.GetTotalDevices();
private IEnumerable UserList
@@ -182,7 +67,7 @@ private IEnumerable UserList
{
if (User is null)
{
- return Enumerable.Empty();
+ return [];
}
EnsureUserSet();
@@ -202,8 +87,8 @@ protected override async Task OnInitializedAsync()
return;
}
- Configuration.Bind("ApplicationOptions", Input);
- Configuration.Bind("ConnectionStrings", ConnectionStrings);
+ Input = await DataService.GetSettings();
+
_userList.AddRange(DataService.GetAllUsersForServer().OrderBy(x => x.UserName));
}
@@ -320,25 +205,18 @@ private void RemoveTrustedCorsOrigin()
private async Task Save()
{
- var resetEvent = new ManualResetEventSlim();
-
- Configuration.GetReloadToken().RegisterChangeCallback((e) =>
- {
- resetEvent.Set();
- }, null);
-
- await SaveInputToAppSettings();
-
- resetEvent.Wait(5_000);
+
+ await DataService.SaveSettings(Input);
ToastService.ShowToast("Configuration saved.");
- _alertMessage = "Configuration saved.";
}
private async Task SaveAndTestSmtpSettings()
{
EnsureUserSet();
- await SaveInputToAppSettings();
+
+ await DataService.SaveSettings(Input);
+
if (string.IsNullOrWhiteSpace(User.Email))
{
ToastService.ShowToast2("User email is not set.", Enums.ToastType.Warning);
@@ -358,58 +236,6 @@ private async Task SaveAndTestSmtpSettings()
}
}
- private async Task SaveInputToAppSettings()
- {
- string savePath;
- var prodSettings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Production.json");
- var stagingSettings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Staging.json");
- var devSettings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.Development.json");
- var settings = HostEnv.ContentRootFileProvider.GetFileInfo("appsettings.json");
-
- if (HostEnv.IsProduction()
- && prodSettings.Exists &&
- !string.IsNullOrWhiteSpace(prodSettings.PhysicalPath))
- {
- savePath = prodSettings.PhysicalPath;
- }
- else if (
- HostEnv.IsStaging() &&
- stagingSettings.Exists &&
- !string.IsNullOrWhiteSpace(stagingSettings.PhysicalPath))
- {
- savePath = stagingSettings.PhysicalPath;
- }
- else if (
- HostEnv.IsDevelopment() &&
- devSettings.Exists &&
- !string.IsNullOrWhiteSpace(devSettings.PhysicalPath))
- {
- savePath = devSettings.PhysicalPath;
- }
- else if (settings.Exists && !string.IsNullOrWhiteSpace(settings.PhysicalPath))
- {
- savePath = settings.PhysicalPath;
- }
- else
- {
- return;
- }
-
- var settingsJson = JsonSerializer.Deserialize>(await File.ReadAllTextAsync(savePath));
- if (settingsJson is null)
- {
- return;
- }
- settingsJson["ApplicationOptions"] = Input;
- settingsJson["ConnectionStrings"] = ConnectionStrings;
-
- await File.WriteAllTextAsync(savePath, JsonSerializer.Serialize(settingsJson, new JsonSerializerOptions() { WriteIndented = true }));
-
- if (Configuration is IConfigurationRoot root)
- {
- root.Reload();
- }
- }
private void SetIsServerAdmin(ChangeEventArgs ev, RemotelyUser user)
{
if (ev.Value is not bool isAdmin)
diff --git a/Server/Components/_Imports.razor b/Server/Components/_Imports.razor
index 37615a39f..bbc5eac2e 100644
--- a/Server/Components/_Imports.razor
+++ b/Server/Components/_Imports.razor
@@ -24,4 +24,5 @@
@using Remotely.Server.Components.Scripts
@using Remotely.Server.Components.TreeView
@using Remotely.Server.Auth
-@using Remotely.Shared.Entities
\ No newline at end of file
+@using Remotely.Shared.Entities
+@using Remotely.Server.Models
\ No newline at end of file
diff --git a/Server/Data/AppDb.cs b/Server/Data/AppDb.cs
index 866b182b8..93b60adfa 100644
--- a/Server/Data/AppDb.cs
+++ b/Server/Data/AppDb.cs
@@ -1,17 +1,12 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Microsoft.Extensions.Hosting;
using Remotely.Server.Converters;
using Remotely.Shared.Entities;
using Remotely.Shared.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Text.Json;
namespace Remotely.Server.Data;
@@ -37,6 +32,7 @@ public AppDb(IWebHostEnvironment hostEnvironment)
public DbSet DeviceGroups { get; set; }
public DbSet Devices { get; set; }
public DbSet InviteLinks { get; set; }
+ public DbSet KeyValueRecords { get; set; }
public DbSet Organizations { get; set; }
public DbSet SavedScripts { get; set; }
public DbSet ScriptResults { get; set; }
@@ -45,7 +41,6 @@ public AppDb(IWebHostEnvironment hostEnvironment)
public DbSet SharedFiles { get; set; }
public new DbSet Users { get; set; }
-
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.ConfigureWarnings(x => x.Ignore(RelationalEventId.MultipleCollectionIncludeWarning));
@@ -284,13 +279,13 @@ private static string[] DeserializeStringArray(string value, JsonSerializerOptio
{
if (string.IsNullOrEmpty(value))
{
- return Array.Empty();
+ return [];
}
- return JsonSerializer.Deserialize(value, jsonOptions) ?? Array.Empty();
+ return JsonSerializer.Deserialize(value, jsonOptions) ?? [];
}
catch
{
- return Array.Empty();
+ return [];
}
}
diff --git a/Server/Data/AppDbFactory.cs b/Server/Data/AppDbFactory.cs
index 372987769..9d036de44 100644
--- a/Server/Data/AppDbFactory.cs
+++ b/Server/Data/AppDbFactory.cs
@@ -10,31 +10,10 @@ public interface IAppDbFactory
AppDb GetContext();
}
-public class AppDbFactory : IAppDbFactory
+public class AppDbFactory(IServiceProvider _services) : IAppDbFactory
{
- private readonly IApplicationConfig _appConfig;
- private readonly IConfiguration _configuration;
- private readonly IWebHostEnvironment _hostEnv;
-
- public AppDbFactory(
- IApplicationConfig appConfig,
- IConfiguration configuration,
- IWebHostEnvironment hostEnv)
- {
- _appConfig = appConfig;
- _configuration = configuration;
- _hostEnv = hostEnv;
- }
-
public AppDb GetContext()
{
- return _appConfig.DBProvider.ToLower() switch
- {
- "sqlite" => new SqliteDbContext(_configuration, _hostEnv),
- "sqlserver" => new SqlServerDbContext(_configuration, _hostEnv),
- "postgresql" => new PostgreSqlDbContext(_configuration, _hostEnv),
- "inmemory" => new TestingDbContext(_hostEnv),
- _ => throw new ArgumentException("Unknown DB provider."),
- };
+ return _services.GetRequiredService();
}
}
diff --git a/Server/Dockerfile b/Server/Dockerfile
index d3f93b486..4c72e5dea 100644
--- a/Server/Dockerfile
+++ b/Server/Dockerfile
@@ -1,22 +1,38 @@
-FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
-USER app
-
-SHELL ["/bin/bash", "-c"]
+RUN apt -y update && apt -y install curl
+RUN mkdir -p /app/AppData
+RUN chown app:app -R /app/AppData
+WORKDIR /app
EXPOSE 5000
EXPOSE 5001
-COPY /_immense.Remotely/Server/linux-x64/Server /app
-WORKDIR /app
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+ARG BUILD_CONFIGURATION=Release
+WORKDIR /src
+COPY ["Directory.Build.props", "."]
+COPY ["Server/Server.csproj", "Server/"]
+COPY ["Shared/Shared.csproj", "Shared/"]
+COPY ["submodules/Immense.RemoteControl/Immense.RemoteControl.Shared/Immense.RemoteControl.Shared.csproj", "submodules/Immense.RemoteControl/Immense.RemoteControl.Shared/"]
+COPY ["submodules/Immense.RemoteControl/Immense.RemoteControl.Server/Immense.RemoteControl.Server.csproj", "submodules/Immense.RemoteControl/Immense.RemoteControl.Server/"]
+RUN dotnet restore "./Server/./Server.csproj"
+COPY . .
+WORKDIR "/src/Server"
+
+RUN dotnet build "./Server.csproj" -c $BUILD_CONFIGURATION -o /app/build
-RUN \
- apt-get -y update && \
- apt-get -y install curl && \
- mkdir -p /remotely-data && \
- sed -i 's/DataSource=Remotely.db/DataSource=\/app\/AppData\/Remotely.db/' /app/appsettings.json
+FROM build AS publish
+ARG BUILD_CONFIGURATION=Release
+RUN dotnet publish "./Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+
+USER app
ENTRYPOINT ["dotnet", "Remotely_Server.dll"]
HEALTHCHECK --interval=5m --timeout=3s \
diff --git a/Server/Dockerfile.local b/Server/Dockerfile.pipelines
similarity index 77%
rename from Server/Dockerfile.local
rename to Server/Dockerfile.pipelines
index 72287a035..f26befcc4 100644
--- a/Server/Dockerfile.local
+++ b/Server/Dockerfile.pipelines
@@ -7,15 +7,15 @@ SHELL ["/bin/bash", "-c"]
EXPOSE 5000
EXPOSE 5001
-COPY ./bin/publish /app
+COPY /_immense.Remotely/Server/linux-x64/Server /app
WORKDIR /app
RUN \
apt-get -y update && \
apt-get -y install curl && \
- mkdir -p /remotely-data && \
- sed -i 's/DataSource=Remotely.db/DataSource=\/app\/AppData\/Remotely.db/' ./appsettings.json
+ mkdir -p /app/AppData && \
+ sed -i 's/DataSource=Remotely.db/DataSource=\/app\/AppData\/Remotely.db/' /app/appsettings.json
ENTRYPOINT ["dotnet", "Remotely_Server.dll"]
diff --git a/Server/Extensions/AppDbExtensions.cs b/Server/Extensions/AppDbExtensions.cs
new file mode 100644
index 000000000..2ced10982
--- /dev/null
+++ b/Server/Extensions/AppDbExtensions.cs
@@ -0,0 +1,31 @@
+using Remotely.Server.Data;
+using Remotely.Server.Models;
+using System.Text.Json;
+
+namespace Remotely.Server.Extensions;
+
+public static class AppDbExtensions
+{
+ public static async Task GetAppSettings(this AppDb dbContext)
+ {
+ var record = await dbContext.KeyValueRecords.FindAsync(SettingsModel.DbKey);
+ if (record is null)
+ {
+ record = new()
+ {
+ Key = SettingsModel.DbKey,
+ };
+ await dbContext.KeyValueRecords.AddAsync(record);
+ await dbContext.SaveChangesAsync();
+ }
+
+ if (string.IsNullOrWhiteSpace(record.Value))
+ {
+ var settings = new SettingsModel();
+ record.Value = JsonSerializer.Serialize(settings);
+ await dbContext.SaveChangesAsync();
+ }
+
+ return JsonSerializer.Deserialize(record.Value) ?? new();
+ }
+}
diff --git a/Server/Hubs/AgentHub.cs b/Server/Hubs/AgentHub.cs
index 32ee48604..25d862c26 100644
--- a/Server/Hubs/AgentHub.cs
+++ b/Server/Hubs/AgentHub.cs
@@ -23,9 +23,8 @@ namespace Remotely.Server.Hubs;
public class AgentHub : Hub
{
- private readonly IApplicationConfig _appConfig;
- private readonly ICircuitManager _circuitManager;
private readonly IDataService _dataService;
+ private readonly ICircuitManager _circuitManager;
private readonly IExpiringTokenService _expiringTokenService;
private readonly ILogger _logger;
private readonly IMessenger _messenger;
@@ -33,8 +32,8 @@ public class AgentHub : Hub
private readonly IAgentHubSessionCache _serviceSessionCache;
private readonly IHubContext _viewerHubContext;
- public AgentHub(IDataService dataService,
- IApplicationConfig appConfig,
+ public AgentHub(
+ IDataService dataService,
IAgentHubSessionCache serviceSessionCache,
IHubContext viewerHubContext,
ICircuitManager circuitManager,
@@ -46,7 +45,6 @@ public AgentHub(IDataService dataService,
_dataService = dataService;
_serviceSessionCache = serviceSessionCache;
_viewerHubContext = viewerHubContext;
- _appConfig = appConfig;
_circuitManager = circuitManager;
_expiringTokenService = expiringTokenService;
_remoteControlSessions = remoteControlSessionCache;
@@ -288,9 +286,10 @@ public Task DownloadFileProgress(int progressPercent, string requesterId)
return _messenger.Send(message, requesterId);
}
- public string GetServerUrl()
+ public async Task GetServerUrl()
{
- return _appConfig.ServerUrl;
+ var settings = await _dataService.GetSettings();
+ return settings.ServerUrl;
}
public string GetServerVerificationToken()
@@ -382,6 +381,7 @@ public Task TransferCompleted(string transferId, string requesterId)
private async Task CheckForDeviceBan(params string[] deviceIdNameOrIPs)
{
+ var settings = await _dataService.GetSettings();
foreach (var device in deviceIdNameOrIPs)
{
if (string.IsNullOrWhiteSpace(device))
@@ -389,7 +389,7 @@ private async Task CheckForDeviceBan(params string[] deviceIdNameOrIPs)
continue;
}
- if (_appConfig.BannedDevices.Any(x => !string.IsNullOrWhiteSpace(x) &&
+ if (settings.BannedDevices.Any(x => !string.IsNullOrWhiteSpace(x) &&
x.Equals(device, StringComparison.OrdinalIgnoreCase)))
{
_logger.LogWarning("Device ID/name/IP ({device}) is banned. Sending uninstall command.", device);
diff --git a/Server/Hubs/CircuitConnection.cs b/Server/Hubs/CircuitConnection.cs
index da55cb5be..6afd104d1 100644
--- a/Server/Hubs/CircuitConnection.cs
+++ b/Server/Hubs/CircuitConnection.cs
@@ -71,11 +71,10 @@ public interface ICircuitConnection
public class CircuitConnection : CircuitHandler, ICircuitConnection
{
private readonly IHubContext _agentHubContext;
- private readonly IApplicationConfig _appConfig;
+ private readonly IDataService _dataService;
private readonly ISelectedCardsStore _cardStore;
private readonly IAuthService _authService;
private readonly ICircuitManager _circuitManager;
- private readonly IDataService _dataService;
private readonly IRemoteControlSessionCache _remoteControlSessionCache;
private readonly IExpiringTokenService _expiringTokenService;
private readonly ILogger _logger;
@@ -89,7 +88,6 @@ public CircuitConnection(
IDataService dataService,
ISelectedCardsStore cardStore,
IHubContext agentHubContext,
- IApplicationConfig appConfig,
ICircuitManager circuitManager,
IToastService toastService,
IExpiringTokenService expiringTokenService,
@@ -101,7 +99,6 @@ public CircuitConnection(
_dataService = dataService;
_agentHubContext = agentHubContext;
_cardStore = cardStore;
- _appConfig = appConfig;
_authService = authService;
_circuitManager = circuitManager;
_toastService = toastService;
@@ -228,6 +225,8 @@ public async Task ReinstallAgents(string[] deviceIDs)
public async Task> RemoteControl(string deviceId, bool viewOnly)
{
+ var settings = await _dataService.GetSettings();
+
if (!_agentSessionCache.TryGetByDeviceId(deviceId, out var targetDevice))
{
var message = new DisplayNotificationMessage(
@@ -256,7 +255,7 @@ public async Task> RemoteControl(string deviceId,
.OfType()
.Count(x => x.OrganizationId == User.OrganizationID);
- if (sessionCount >= _appConfig.RemoteControlSessionLimit)
+ if (sessionCount >= settings.RemoteControlSessionLimit)
{
var message = new DisplayNotificationMessage(
"There are already the maximum amount of active remote control sessions for your organization.",
@@ -290,8 +289,8 @@ public async Task> RemoteControl(string deviceId,
DeviceId = deviceId,
ViewOnly = viewOnly,
OrganizationId = User.OrganizationID,
- RequireConsent = _appConfig.EnforceAttendedAccess,
- NotifyUserOnStart = _appConfig.RemoteControlNotifyUser
+ RequireConsent = settings.EnforceAttendedAccess,
+ NotifyUserOnStart = settings.RemoteControlNotifyUser
};
_remoteControlSessionCache.AddOrUpdate($"{sessionId}", session);
diff --git a/Server/Models/SettingsModel.cs b/Server/Models/SettingsModel.cs
new file mode 100644
index 000000000..78546dbae
--- /dev/null
+++ b/Server/Models/SettingsModel.cs
@@ -0,0 +1,39 @@
+using Remotely.Shared.Enums;
+
+namespace Remotely.Server.Models;
+
+public class SettingsModel
+{
+ public static Guid DbKey { get; } = Guid.Parse("a35d6212-c0b7-49b2-89e1-7ba497f94a35");
+
+ public bool AllowApiLogin { get; set; }
+ public List BannedDevices { get; set; } = [];
+ public double DataRetentionInDays { get; set; } = 90;
+ public string DbProvider { get; set; } = "SQLite";
+ public bool EnableRemoteControlRecording { get; set; }
+ public bool EnableWindowsEventLog { get; set; }
+ public bool EnforceAttendedAccess { get; set; }
+ public bool ForceClientHttps { get; set; }
+ public List KnownProxies { get; set; } = [];
+ public int MaxConcurrentUpdates { get; set; } = 10;
+ public int MaxOrganizationCount { get; set; } = 1;
+ public string MessageOfTheDay { get; set; } = string.Empty;
+ public bool RedirectToHttps { get; set; } = true;
+ public bool RemoteControlNotifyUser { get; set; } = true;
+ public bool RemoteControlRequiresAuthentication { get; set; } = true;
+ public int RemoteControlSessionLimit { get; set; } = 5;
+ public bool Require2FA { get; set; }
+ public string ServerUrl { get; set; } = string.Empty;
+ public bool SmtpCheckCertificateRevocation { get; set; } = true;
+ public string SmtpDisplayName { get; set; } = string.Empty;
+ public string SmtpEmail { get; set; } = string.Empty;
+ public string SmtpHost { get; set; } = string.Empty;
+ public string SmtpLocalDomain { get; set; } = string.Empty;
+ public string SmtpPassword { get; set; } = string.Empty;
+ public int SmtpPort { get; set; } = 587;
+ public string SmtpUserName { get; set; } = string.Empty;
+ public Theme Theme { get; set; } = Theme.Dark;
+ public List TrustedCorsOrigins { get; set; } = [];
+ public bool UseHsts { get; set; }
+ public bool UseHttpLogging { get; set; }
+}
diff --git a/Server/Options/ApplicationOptions.cs b/Server/Options/ApplicationOptions.cs
new file mode 100644
index 000000000..1a11cc3b8
--- /dev/null
+++ b/Server/Options/ApplicationOptions.cs
@@ -0,0 +1,7 @@
+namespace Remotely.Server.Options;
+
+public class ApplicationOptions
+{
+ public const string SectionKey = "ApplicationOptions";
+ public string DbProvider { get; set; } = "SQLite";
+}
diff --git a/Server/Program.cs b/Server/Program.cs
index 239139837..3627d352c 100644
--- a/Server/Program.cs
+++ b/Server/Program.cs
@@ -23,6 +23,9 @@
using Remotely.Server.Services.Stores;
using Remotely.Server.Components.Account;
using Remotely.Server.Components;
+using Remotely.Server.Options;
+using Remotely.Server.Extensions;
+using Remotely.Server.Models;
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
@@ -30,6 +33,9 @@
configuration.AddEnvironmentVariables("Remotely_");
+services.Configure(
+ configuration.GetSection(ApplicationOptions.SectionKey));
+
services
.AddRazorComponents()
.AddInteractiveServerComponents();
@@ -41,21 +47,29 @@
services.AddScoped();
services.AddScoped();
-ConfigureSerilog(builder);
-
-builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
-
-if (OperatingSystem.IsWindows() &&
- bool.TryParse(builder.Configuration["ApplicationOptions:EnableWindowsEventLog"], out var enableEventLog) &&
- enableEventLog)
-{
- builder.Logging.AddEventLog();
-}
+var dbProvider = configuration["ApplicationOptions:DbProvider"]?.ToLower();
-var dbProvider = configuration["ApplicationOptions:DBProvider"]?.ToLower();
-if (string.IsNullOrWhiteSpace(dbProvider))
+switch (dbProvider)
{
- throw new InvalidOperationException("DBProvider is missing from appsettings.json.");
+ case "sqlite":
+ services.AddDbContext(
+ contextLifetime: ServiceLifetime.Transient,
+ optionsLifetime: ServiceLifetime.Transient);
+ break;
+ case "sqlserver":
+ services.AddDbContext(
+ contextLifetime: ServiceLifetime.Transient,
+ optionsLifetime: ServiceLifetime.Transient);
+ break;
+ case "postgresql":
+ services.AddDbContext(
+ contextLifetime: ServiceLifetime.Transient,
+ optionsLifetime: ServiceLifetime.Transient);
+ break;
+ default:
+ throw new InvalidOperationException(
+ $"Invalid DBProvider: {dbProvider}. Ensure a valid value " +
+ $"is set in appsettings.json or environment variables.");
}
if (dbProvider == "sqlite")
@@ -71,6 +85,26 @@
services.AddDbContext();
}
+using AppDb appDb = dbProvider switch
+{
+ "sqlite" => new SqliteDbContext(builder.Configuration, builder.Environment),
+ "sqlserver" => new SqlServerDbContext(builder.Configuration, builder.Environment),
+ "postgresql" => new PostgreSqlDbContext(builder.Configuration, builder.Environment),
+ _ => throw new InvalidOperationException($"Invalid DBProvider: {dbProvider}")
+};
+
+await appDb.Database.MigrateAsync();
+var settings = await appDb.GetAppSettings();
+
+ConfigureSerilog(builder, settings);
+
+builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
+
+if (OperatingSystem.IsWindows() && settings.EnableWindowsEventLog)
+{
+ builder.Logging.AddEventLog();
+}
+
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
@@ -113,8 +147,7 @@
services.AddDatabaseDeveloperPageExceptionFilter();
-if (bool.TryParse(configuration["ApplicationOptions:UseHttpLogging"], out var useHttpLogging) &&
- useHttpLogging)
+if (settings.UseHttpLogging)
{
services.AddHttpLogging(options =>
{
@@ -128,14 +161,12 @@
});
}
-var trustedOrigins = configuration.GetSection("ApplicationOptions:TrustedCorsOrigins").Get();
-
services.AddCors(options =>
{
- if (trustedOrigins != null)
+ if (settings.TrustedCorsOrigins is { Count: > 0} trustedOrigins)
{
options.AddPolicy("TrustedOriginPolicy", builder => builder
- .WithOrigins(trustedOrigins)
+ .WithOrigins(trustedOrigins.ToArray())
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
@@ -144,7 +175,6 @@
});
-var knownProxies = configuration.GetSection("ApplicationOptions:KnownProxies").Get();
services.Configure(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
@@ -153,7 +183,7 @@
// Default Docker host. We want to allow forwarded headers from this address.
options.KnownProxies.Add(IPAddress.Parse("172.17.0.1"));
- if (knownProxies?.Any() == true)
+ if (settings.KnownProxies is { Count: >0 } knownProxies)
{
foreach (var proxy in knownProxies)
{
@@ -180,13 +210,10 @@
{
clOptions.QueueLimit = int.MaxValue;
- var concurrentPermits = configuration.GetSection("ApplicationOptions:MaxConcurrentUpdates").Get();
- if (concurrentPermits <= 0)
- {
- concurrentPermits = 10;
- }
-
- clOptions.PermitLimit = concurrentPermits;
+ clOptions.PermitLimit =
+ settings.MaxConcurrentUpdates <= 0 ?
+ 10 :
+ settings.MaxConcurrentUpdates;
});
});
services.AddHttpClient();
@@ -202,7 +229,6 @@
}
services.AddScoped();
services.AddTransient();
-services.AddSingleton();
services.AddScoped();
services.AddScoped();
services.AddScoped();
@@ -246,9 +272,7 @@
app.UseRateLimiter();
-var appConfig = app.Services.GetRequiredService();
-
-if (appConfig.UseHttpLogging)
+if (settings.UseHttpLogging)
{
app.UseHttpLogging();
}
@@ -265,11 +289,11 @@
else
{
app.UseExceptionHandler("/Error");
- if (bool.TryParse(app.Configuration["ApplicationOptions:UseHsts"], out var hsts) && hsts)
+ if (settings.UseHsts)
{
app.UseHsts();
}
- if (bool.TryParse(app.Configuration["ApplicationOptions:RedirectToHttps"], out var redirect) && redirect)
+ if (settings.RedirectToHttps)
{
app.UseHttpsRedirection();
}
@@ -297,14 +321,8 @@
using (var scope = app.Services.CreateScope())
{
- using var context = scope.ServiceProvider.GetRequiredService();
var dataService = scope.ServiceProvider.GetRequiredService();
- if (context.Database.IsRelational())
- {
- await context.Database.MigrateAsync();
- }
-
await dataService.SetAllDevicesNotOnline();
await dataService.CleanupOldRecords();
}
@@ -348,16 +366,17 @@ void ConfigureStaticFiles()
}
}
-void ConfigureSerilog(WebApplicationBuilder webAppBuilder)
+void ConfigureSerilog(WebApplicationBuilder webAppBuilder, SettingsModel settings)
{
try
{
- var dataRetentionDays = 7;
- if (int.TryParse(webAppBuilder.Configuration["ApplicationOptions:DataRetentionInDays"], out var retentionSetting))
+
+ var dataRetentionDays = settings.DataRetentionInDays;
+ if (dataRetentionDays <= 0)
{
- dataRetentionDays = retentionSetting;
+ dataRetentionDays = 7;
}
-
+
var logPath = LogsManager.DefaultLogsDirectory;
void ApplySharedLoggerConfig(LoggerConfiguration loggerConfiguration)
@@ -366,8 +385,8 @@ void ApplySharedLoggerConfig(LoggerConfiguration loggerConfiguration)
.Enrich.FromLogContext()
.Enrich.WithThreadId()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties}{NewLine}{Exception}")
- .WriteTo.File($"{logPath}/Remotely_Server.log",
- rollingInterval: RollingInterval.Day,
+ .WriteTo.File($"{logPath}/Remotely_Server.log",
+ rollingInterval: RollingInterval.Day,
retainedFileTimeLimit: TimeSpan.FromDays(dataRetentionDays),
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj} {Properties}{NewLine}{Exception}",
shared: true);
diff --git a/Server/Services/ApplicationConfig.cs b/Server/Services/ApplicationConfig.cs
deleted file mode 100644
index 9fd583882..000000000
--- a/Server/Services/ApplicationConfig.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Remotely.Shared.Enums;
-using Remotely.Shared.Models;
-using System;
-
-namespace Remotely.Server.Services;
-
-public interface IApplicationConfig
-{
- bool AllowApiLogin { get; }
- string[] BannedDevices { get; }
- double DataRetentionInDays { get; }
- string DBProvider { get; }
- bool EnableRemoteControlRecording { get; }
- bool EnableWindowsEventLog { get; }
- bool EnforceAttendedAccess { get; }
- bool ForceClientHttps { get; }
- string[] KnownProxies { get; }
- int MaxConcurrentUpdates { get; }
- int MaxOrganizationCount { get; }
- string MessageOfTheDay { get; }
- bool RedirectToHttps { get; }
- bool RemoteControlNotifyUser { get; }
- bool RemoteControlRequiresAuthentication { get; }
- int RemoteControlSessionLimit { get; }
- bool Require2FA { get; }
- string ServerUrl { get; }
- bool SmtpCheckCertificateRevocation { get; }
- string SmtpDisplayName { get; }
- string SmtpEmail { get; }
- string SmtpHost { get; }
- string SmtpLocalDomain { get; }
- string SmtpPassword { get; }
- int SmtpPort { get; }
- string SmtpUserName { get; }
- Theme Theme { get; }
- string[] TrustedCorsOrigins { get; }
- bool UseHsts { get; }
- bool UseHttpLogging { get; }
-}
-
-public class ApplicationConfig : IApplicationConfig
-{
- private readonly IConfiguration _config;
-
- public ApplicationConfig(IConfiguration config)
- {
- _config = config;
- }
-
- public bool AllowApiLogin => bool.TryParse(_config["ApplicationOptions:AllowApiLogin"], out var result) && result;
- public string[] BannedDevices => _config.GetSection("ApplicationOptions:BannedDevices").Get() ?? System.Array.Empty();
- public double DataRetentionInDays => double.TryParse(_config["ApplicationOptions:DataRetentionInDays"], out var result) ? result : 30;
- public string DBProvider => _config["ApplicationOptions:DBProvider"] ?? "SQLite";
- public bool EnableRemoteControlRecording => bool.TryParse(_config["ApplicationOptions:EnableRemoteControlRecording"], out var result) && result;
- public bool EnableWindowsEventLog => bool.TryParse(_config["ApplicationOptions:EnableWindowsEventLog"], out var result) && result;
- public bool EnforceAttendedAccess => bool.TryParse(_config["ApplicationOptions:EnforceAttendedAccess"], out var result) && result;
- public bool ForceClientHttps => bool.TryParse(_config["ApplicationOptions:ForceClientHttps"], out var result) && result;
- public string[] KnownProxies => _config.GetSection("ApplicationOptions:KnownProxies").Get() ?? System.Array.Empty();
- public int MaxConcurrentUpdates => int.TryParse(_config["ApplicationOptions:MaxConcurrentUpdates"], out var result) ? result : 10;
- public int MaxOrganizationCount => int.TryParse(_config["ApplicationOptions:MaxOrganizationCount"], out var result) ? result : 1;
- public string MessageOfTheDay => _config["ApplicationOptions:MessageOfTheDay"] ?? string.Empty;
- public bool RedirectToHttps => bool.TryParse(_config["ApplicationOptions:RedirectToHttps"], out var result) && result;
- public bool RemoteControlNotifyUser => bool.TryParse(_config["ApplicationOptions:RemoteControlNotifyUser"], out var result) && result;
- public bool RemoteControlRequiresAuthentication => bool.TryParse(_config["ApplicationOptions:RemoteControlRequiresAuthentication"], out var result) && result;
- public int RemoteControlSessionLimit => int.TryParse(_config["ApplicationOptions:RemoteControlSessionLimit"], out var result) ? result : 3;
- public bool Require2FA => bool.TryParse(_config["ApplicationOptions:Require2FA"], out var result) && result;
- public string ServerUrl => _config["ApplicationOptions:ServerUrl"] ?? string.Empty;
- public bool SmtpCheckCertificateRevocation => !bool.TryParse(_config["ApplicationOptions:SmtpCheckCertificateRevocation"], out var result) || result;
- public string SmtpDisplayName => _config["ApplicationOptions:SmtpDisplayName"] ?? string.Empty;
- public string SmtpEmail => _config["ApplicationOptions:SmtpEmail"] ?? string.Empty;
- public string SmtpHost => _config["ApplicationOptions:SmtpHost"] ?? string.Empty;
- public string SmtpLocalDomain => _config["ApplicationOptions:SmtpLocalDomain"] ?? string.Empty;
- public string SmtpPassword => _config["ApplicationOptions:SmtpPassword"] ?? string.Empty;
- public int SmtpPort => int.TryParse(_config["ApplicationOptions:SmtpPort"], out var result) ? result : 25;
- public string SmtpUserName => _config["ApplicationOptions:SmtpUserName"] ?? string.Empty;
- public Theme Theme => Enum.TryParse(_config["ApplicationOptions:Theme"], out var result) ? result : Theme.Dark;
- public string[] TrustedCorsOrigins => _config.GetSection("ApplicationOptions:TrustedCorsOrigins").Get() ?? System.Array.Empty();
- public bool UseHsts => bool.TryParse(_config["ApplicationOptions:UseHsts"], out var result) && result;
- public bool UseHttpLogging => bool.TryParse(_config["ApplicationOptions:UseHttpLogging"], out var result) && result;
-}
diff --git a/Server/Services/DataCleanupService.cs b/Server/Services/DataCleanupService.cs
index 6ef1cf658..47a5bc41b 100644
--- a/Server/Services/DataCleanupService.cs
+++ b/Server/Services/DataCleanupService.cs
@@ -19,17 +19,17 @@ public class DataCleanupService : BackgroundService, IDisposable
private readonly ILogger _logger;
private readonly IServiceScopeFactory _scopeFactory;
private readonly ISystemTime _systemTime;
- private readonly IApplicationConfig _appConfig;
+ private readonly IDataService _dataService;
public DataCleanupService(
IServiceScopeFactory scopeFactory,
ISystemTime systemTime,
- IApplicationConfig appConfig,
+ IDataService dataService,
ILogger logger)
{
_scopeFactory = scopeFactory;
_systemTime = systemTime;
- _appConfig = appConfig;
+ _dataService = dataService;
_logger = logger;
}
@@ -76,14 +76,18 @@ private async Task RemoveExpiredDbRecords()
await dataService.CleanupOldRecords();
}
- private Task RemoveExpiredRecordings()
+ private async Task RemoveExpiredRecordings()
{
+ using var scope = _scopeFactory.CreateScope();
+ var dataService = scope.ServiceProvider.GetRequiredService();
+ var settings = await dataService.GetSettings();
+
if (!Directory.Exists(SessionRecordingSink.RecordingsDirectory))
{
- return Task.CompletedTask;
+ return;
}
- var expirationDate = _systemTime.Now.UtcDateTime - TimeSpan.FromDays(_appConfig.DataRetentionInDays);
+ var expirationDate = _systemTime.Now.UtcDateTime - TimeSpan.FromDays(settings.DataRetentionInDays);
var files = Directory
.GetFiles(
@@ -106,7 +110,5 @@ private Task RemoveExpiredRecordings()
_logger.LogError(ex, "Error while deleting expired recording: {file}", file);
}
}
-
- return Task.CompletedTask;
}
}
diff --git a/Server/Services/DataService.cs b/Server/Services/DataService.cs
index 1ad7d7c7d..9afc921a5 100644
--- a/Server/Services/DataService.cs
+++ b/Server/Services/DataService.cs
@@ -21,6 +21,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.Json;
using System.Threading.Tasks;
namespace Remotely.Server.Services;
@@ -171,6 +172,8 @@ Task> GetDeviceGroup(
List GetServerAdmins();
+ Task GetSettings();
+
Task> GetSharedFiled(string fileId);
int GetTotalDevices();
@@ -193,6 +196,8 @@ Task> GetUserByName(
Task ResetBranding(string organizationId);
+ Task SaveSettings(SettingsModel settings);
+
Task SetAllDevicesNotOnline();
Task SetDisplayName(RemotelyUser user, string displayName);
@@ -224,18 +229,16 @@ Task UpdateBrandingInfo(
public class DataService : IDataService
{
- private readonly IApplicationConfig _appConfig;
private readonly IAppDbFactory _appDbFactory;
private readonly IHostEnvironment _hostEnvironment;
private readonly ILogger _logger;
+ private readonly SemaphoreSlim _settingsLock = new(1, 1);
public DataService(
- IApplicationConfig appConfig,
IHostEnvironment hostEnvironment,
IAppDbFactory appDbFactory,
ILogger logger)
{
- _appConfig = appConfig;
_hostEnvironment = hostEnvironment;
_appDbFactory = appDbFactory;
_logger = logger;
@@ -639,14 +642,15 @@ public async Task ChangeUserIsAdmin(string organizationId, string targetUserId,
public async Task CleanupOldRecords()
{
+ var settings = await GetSettings();
using var dbContext = _appDbFactory.GetContext();
- if (_appConfig.DataRetentionInDays < 0)
+ if (settings.DataRetentionInDays < 0)
{
return;
}
- var expirationDate = DateTimeOffset.Now - TimeSpan.FromDays(_appConfig.DataRetentionInDays);
+ var expirationDate = DateTimeOffset.Now - TimeSpan.FromDays(settings.DataRetentionInDays);
var scriptRuns = await dbContext.ScriptRuns
.Include(x => x.Results)
@@ -1711,6 +1715,25 @@ public List GetServerAdmins()
.ToList();
}
+ public async Task GetSettings()
+ {
+ await _settingsLock.WaitAsync();
+ try
+ {
+ using var dbContext = _appDbFactory.GetContext();
+ return await dbContext.GetAppSettings();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error while getting settings from database.");
+ return new();
+ }
+ finally
+ {
+ _settingsLock.Release();
+ }
+ }
+
public async Task> GetSharedFiled(string fileId)
{
using var dbContext = _appDbFactory.GetContext();
@@ -1930,6 +1953,35 @@ public async Task ResetBranding(string organizationId)
await dbContext.SaveChangesAsync();
}
+ public async Task SaveSettings(SettingsModel settings)
+ {
+ await _settingsLock.WaitAsync();
+ try
+ {
+ using var dbContext = _appDbFactory.GetContext();
+ var record = await dbContext.KeyValueRecords.FindAsync(SettingsModel.DbKey);
+ if (record is null)
+ {
+ record = new()
+ {
+ Key = SettingsModel.DbKey,
+ };
+ await dbContext.KeyValueRecords.AddAsync(record);
+ await dbContext.SaveChangesAsync();
+ }
+ record.Value = JsonSerializer.Serialize(settings);
+ await dbContext.SaveChangesAsync();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error while saving settings to database.");
+ }
+ finally
+ {
+ _settingsLock.Release();
+ }
+ }
+
public async Task SetAllDevicesNotOnline()
{
using var dbContext = _appDbFactory.GetContext();
@@ -2188,14 +2240,15 @@ public async Task ValidateApiKey(string keyId, string apiSecret, string re
}
private async Task AddSharedFileImpl(
- string fileName,
+ string fileName,
byte[] fileContents,
string contentType,
string organizationId)
{
+ var settings = await GetSettings();
using var dbContext = _appDbFactory.GetContext();
- var expirationDate = DateTimeOffset.Now.AddDays(-_appConfig.DataRetentionInDays);
+ var expirationDate = DateTimeOffset.Now.AddDays(-settings.DataRetentionInDays);
var expiredFiles = dbContext.SharedFiles.Where(x => x.Timestamp < expirationDate);
dbContext.RemoveRange(expiredFiles);
diff --git a/Server/Services/EmailSender.cs b/Server/Services/EmailSender.cs
index c2a5a1662..55d9d1b26 100644
--- a/Server/Services/EmailSender.cs
+++ b/Server/Services/EmailSender.cs
@@ -6,6 +6,7 @@
using Microsoft.Identity.Client;
using MimeKit;
using MimeKit.Text;
+using NuGet.Configuration;
using System;
using System.Net;
using System.Threading.Tasks;
@@ -35,14 +36,14 @@ public Task SendEmailAsync(string email, string subject, string htmlMessage)
public class EmailSenderEx : IEmailSenderEx
{
- private readonly IApplicationConfig _appConfig;
+ private readonly IDataService _dataService;
private readonly ILogger _logger;
public EmailSenderEx(
- IApplicationConfig appConfig,
+ IDataService dataService,
ILogger logger)
{
- _appConfig = appConfig;
+ _dataService = dataService;
_logger = logger;
}
public async Task SendEmailAsync(
@@ -54,8 +55,10 @@ public async Task SendEmailAsync(
{
try
{
+ var settings = await _dataService.GetSettings();
+
var message = new MimeMessage();
- message.From.Add(new MailboxAddress(_appConfig.SmtpDisplayName, _appConfig.SmtpEmail));
+ message.From.Add(new MailboxAddress(settings.SmtpDisplayName, settings.SmtpEmail));
message.To.Add(MailboxAddress.Parse(toEmail));
message.ReplyTo.Add(MailboxAddress.Parse(replyTo));
message.Subject = subject;
@@ -66,19 +69,19 @@ public async Task SendEmailAsync(
using var client = new SmtpClient();
- if (!string.IsNullOrWhiteSpace(_appConfig.SmtpLocalDomain))
+ if (!string.IsNullOrWhiteSpace(settings.SmtpLocalDomain))
{
- client.LocalDomain = _appConfig.SmtpLocalDomain;
+ client.LocalDomain = settings.SmtpLocalDomain;
}
- client.CheckCertificateRevocation = _appConfig.SmtpCheckCertificateRevocation;
+ client.CheckCertificateRevocation = settings.SmtpCheckCertificateRevocation;
- await client.ConnectAsync(_appConfig.SmtpHost, _appConfig.SmtpPort);
+ await client.ConnectAsync(settings.SmtpHost, settings.SmtpPort);
- if (!string.IsNullOrWhiteSpace(_appConfig.SmtpUserName) &&
- !string.IsNullOrWhiteSpace(_appConfig.SmtpPassword))
+ if (!string.IsNullOrWhiteSpace(settings.SmtpUserName) &&
+ !string.IsNullOrWhiteSpace(settings.SmtpPassword))
{
- await client.AuthenticateAsync(_appConfig.SmtpUserName, _appConfig.SmtpPassword);
+ await client.AuthenticateAsync(settings.SmtpUserName, settings.SmtpPassword);
}
await client.SendAsync(message);
@@ -95,9 +98,10 @@ public async Task SendEmailAsync(
}
}
- public Task SendEmailAsync(string email, string subject, string htmlMessage, string? organizationID = null)
+ public async Task SendEmailAsync(string email, string subject, string htmlMessage, string? organizationID = null)
{
- return SendEmailAsync(email, _appConfig.SmtpEmail, subject, htmlMessage, organizationID);
+ var settings = await _dataService.GetSettings();
+ return await SendEmailAsync(email, settings.SmtpEmail, subject, htmlMessage, organizationID);
}
}
public class EmailSenderFake(ILogger _logger) : IEmailSenderEx
diff --git a/Server/Services/RcImplementations/ViewerAuthorizer.cs b/Server/Services/RcImplementations/ViewerAuthorizer.cs
index e4efc6124..5f664e2ef 100644
--- a/Server/Services/RcImplementations/ViewerAuthorizer.cs
+++ b/Server/Services/RcImplementations/ViewerAuthorizer.cs
@@ -10,35 +10,36 @@ namespace Remotely.Server.Services.RcImplementations;
public class ViewerAuthorizer : IViewerAuthorizer
{
- private readonly IApplicationConfig _appConfig;
+ private readonly IDataService _dataService;
private readonly IOtpProvider _otpProvider;
- public ViewerAuthorizer(IApplicationConfig appConfig, IOtpProvider otpProvider)
+ public ViewerAuthorizer(IDataService dataService, IOtpProvider otpProvider)
{
- _appConfig = appConfig;
+ _dataService = dataService;
_otpProvider = otpProvider;
}
public string UnauthorizedRedirectUrl { get; } = "/Account/Login";
- public Task IsAuthorized(AuthorizationFilterContext context)
+ public async Task IsAuthorized(AuthorizationFilterContext context)
{
- if (!_appConfig.RemoteControlRequiresAuthentication)
+ var settings = await _dataService.GetSettings();
+ if (!settings.RemoteControlRequiresAuthentication)
{
- return Task.FromResult(true);
+ return true;
}
if (context.HttpContext.User.Identity?.IsAuthenticated == true)
{
- return Task.FromResult(true);
+ return true;
}
if (context.HttpContext.Request.Query.TryGetValue("otp", out var otp) &&
_otpProvider.Exists($"{otp}"))
{
- return Task.FromResult(true);
+ return true;
}
- return Task.FromResult(false);
+ return false;
}
}
diff --git a/Server/Services/RcImplementations/ViewerOptionsProvider.cs b/Server/Services/RcImplementations/ViewerOptionsProvider.cs
index ac78424ab..3e7e35d99 100644
--- a/Server/Services/RcImplementations/ViewerOptionsProvider.cs
+++ b/Server/Services/RcImplementations/ViewerOptionsProvider.cs
@@ -8,18 +8,19 @@ namespace Remotely.Server.Services.RcImplementations;
public class ViewerOptionsProvider : IViewerOptionsProvider
{
- private readonly IApplicationConfig _appConfig;
+ private readonly IDataService _dataService;
- public ViewerOptionsProvider(IApplicationConfig appConfig)
+ public ViewerOptionsProvider(IDataService dataService)
{
- _appConfig = appConfig;
+ _dataService = dataService;
}
- public Task GetViewerOptions()
+ public async Task GetViewerOptions()
{
+ var settings = await _dataService.GetSettings();
var options = new RemoteControlViewerOptions()
{
- ShouldRecordSession = _appConfig.EnableRemoteControlRecording
+ ShouldRecordSession = settings.EnableRemoteControlRecording
};
- return Task.FromResult(options);
+ return options;
}
}
diff --git a/Server/Services/RcImplementations/ViewerPageDataProvider.cs b/Server/Services/RcImplementations/ViewerPageDataProvider.cs
index 31df87996..7368b1925 100644
--- a/Server/Services/RcImplementations/ViewerPageDataProvider.cs
+++ b/Server/Services/RcImplementations/ViewerPageDataProvider.cs
@@ -12,12 +12,10 @@ namespace Remotely.Server.Services.RcImplementations;
public class ViewerPageDataProvider : IViewerPageDataProvider
{
- private readonly IApplicationConfig _appConfig;
private readonly IDataService _dataService;
- public ViewerPageDataProvider(IDataService dataService, IApplicationConfig appConfig)
+ public ViewerPageDataProvider(IDataService dataService)
{
_dataService = dataService;
- _appConfig = appConfig;
}
public Task GetFaviconUrl(PageModel viewerModel)
diff --git a/Server/Services/ThemeProvider.cs b/Server/Services/ThemeProvider.cs
index 3e9106197..c58c2df55 100644
--- a/Server/Services/ThemeProvider.cs
+++ b/Server/Services/ThemeProvider.cs
@@ -12,25 +12,26 @@ public interface IThemeProvider
public class ThemeProvider : IThemeProvider
{
private readonly IAuthService _authService;
- private readonly IApplicationConfig _appConfig;
+ private readonly IDataService _dataService;
- public ThemeProvider(IAuthService authService, IApplicationConfig appConfig)
+ public ThemeProvider(IAuthService authService, IDataService dataService)
{
_authService = authService;
- _appConfig = appConfig;
+ _dataService = dataService;
}
public async Task GetEffectiveTheme()
{
+ var settings = await _dataService.GetSettings();
if (await _authService.IsAuthenticated())
{
var userResult = await _authService.GetUser();
if (userResult.IsSuccess)
{
- return userResult.Value.UserOptions?.Theme ?? _appConfig.Theme;
+ return userResult.Value.UserOptions?.Theme ?? settings.Theme;
}
}
- return _appConfig.Theme;
+ return settings.Theme;
}
}
diff --git a/Server/appsettings.json b/Server/appsettings.json
index da6d238e5..97d0af750 100644
--- a/Server/appsettings.json
+++ b/Server/appsettings.json
@@ -20,34 +20,6 @@
}
},
"ApplicationOptions": {
- "AllowApiLogin": false,
- "BannedDevices": [],
- "DataRetentionInDays": 90,
- "DBProvider": "SQLite",
- "EnableRemoteControlRecording": false,
- "EnableWindowsEventLog": false,
- "EnforceAttendedAccess": false,
- "ForceClientHttps": false,
- "KnownProxies": [],
- "MaxConcurrentUpdates": 10,
- "MaxOrganizationCount": 1,
- "MessageOfTheDay": "",
- "RedirectToHttps": true,
- "RemoteControlNotifyUser": true,
- "RemoteControlRequiresAuthentication": true,
- "RemoteControlSessionLimit": 3,
- "Require2FA": false,
- "SmtpDisplayName": "",
- "SmtpEmail": "",
- "SmtpHost": "",
- "SmtpLocalDomain": "",
- "SmtpCheckCertificateRevocation": true,
- "SmtpPassword": "",
- "SmtpPort": 587,
- "SmtpUserName": "",
- "Theme": "Dark",
- "TrustedCorsOrigins": [],
- "UseHsts": false,
- "UseHttpLogging": false
+ "DBProvider": "SQLite"
}
-}
+}
\ No newline at end of file
diff --git a/Shared/Entities/KeyValueRecord.cs b/Shared/Entities/KeyValueRecord.cs
new file mode 100644
index 000000000..8c261f10a
--- /dev/null
+++ b/Shared/Entities/KeyValueRecord.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Remotely.Shared.Entities;
+
+public class KeyValueRecord
+{
+ [Key]
+ public Guid Key { get; set; }
+ public string? Value { get; set; }
+}
diff --git a/Tests/Server.Tests/AgentHubTests.cs b/Tests/Server.Tests/AgentHubTests.cs
index b1646af7f..772001a44 100644
--- a/Tests/Server.Tests/AgentHubTests.cs
+++ b/Tests/Server.Tests/AgentHubTests.cs
@@ -8,6 +8,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Remotely.Server.Hubs;
+using Remotely.Server.Models;
using Remotely.Server.Services;
using Remotely.Shared.Extensions;
using Remotely.Shared.Interfaces;
@@ -32,7 +33,6 @@ public async Task DeviceCameOnline_BannedByName()
var circuitConnection = new Mock();
circuitManager.Setup(x => x.Connections).Returns(new[] { circuitConnection.Object });
circuitConnection.Setup(x => x.User).Returns(_testData.Org1Admin1);
- var appConfig = new Mock();
var viewerHub = new Mock>();
var expiringTokenService = new Mock();
var serviceSessionCache = new Mock();
@@ -40,11 +40,12 @@ public async Task DeviceCameOnline_BannedByName()
var messenger = new Mock();
var logger = new Mock>();
- appConfig.Setup(x => x.BannedDevices).Returns(new string[] { $"{_testData.Org1Device1.DeviceName}" });
+ var settings = await _dataService.GetSettings();
+ settings.BannedDevices = [_testData.Org1Device1.DeviceName!];
+ await _dataService.SaveSettings(settings);
var hub = new AgentHub(
_dataService,
- appConfig.Object,
serviceSessionCache.Object,
viewerHub.Object,
circuitManager.Object,
@@ -58,7 +59,8 @@ public async Task DeviceCameOnline_BannedByName()
hubClients.Setup(x => x.Caller).Returns(caller.Object);
hub.Clients = hubClients.Object;
- Assert.IsFalse(await hub.DeviceCameOnline(_testData.Org1Device1.ToDto()));
+ var result = await hub.DeviceCameOnline(_testData.Org1Device1.ToDto());
+ Assert.IsFalse(result);
hubClients.Verify(x => x.Caller, Times.Once);
caller.Verify(x => x.UninstallAgent(), Times.Once);
}
@@ -73,7 +75,6 @@ public async Task DeviceCameOnline_BannedById()
var circuitConnection = new Mock();
circuitManager.Setup(x => x.Connections).Returns(new[] { circuitConnection.Object });
circuitConnection.Setup(x => x.User).Returns(_testData.Org1Admin1);
- var appConfig = new Mock();
var viewerHub = new Mock>();
var expiringTokenService = new Mock();
var serviceSessionCache = new Mock();
@@ -81,11 +82,13 @@ public async Task DeviceCameOnline_BannedById()
var messenger = new Mock();
var logger = new Mock>();
- appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Org1Device1.ID });
+
+ var settings = await _dataService.GetSettings();
+ settings.BannedDevices = [$"{_testData.Org1Device1.ID}"];
+ await _dataService.SaveSettings(settings);
var hub = new AgentHub(
_dataService,
- appConfig.Object,
serviceSessionCache.Object,
viewerHub.Object,
circuitManager.Object,
@@ -99,7 +102,8 @@ public async Task DeviceCameOnline_BannedById()
hubClients.Setup(x => x.Caller).Returns(caller.Object);
hub.Clients = hubClients.Object;
- Assert.IsFalse(await hub.DeviceCameOnline(_testData.Org1Device1.ToDto()));
+ var result = await hub.DeviceCameOnline(_testData.Org1Device1.ToDto());
+ Assert.IsFalse(result);
hubClients.Verify(x => x.Caller, Times.Once);
caller.Verify(x => x.UninstallAgent(), Times.Once);
}
@@ -117,23 +121,4 @@ public async Task TestInit()
await _testData.Init();
_dataService = IoCActivator.ServiceProvider.GetRequiredService();
}
-
- private class CallerContext : HubCallerContext
- {
- public override string ConnectionId => "test-id";
-
- public override string? UserIdentifier => null;
- public override ClaimsPrincipal? User => null;
-
- public override IDictionary Items { get; } = new Dictionary();
-
- public override IFeatureCollection Features { get; } = new FeatureCollection();
-
- public override CancellationToken ConnectionAborted => CancellationToken.None;
-
- public override void Abort()
- {
-
- }
- }
}
diff --git a/Tests/Server.Tests/CircuitConnectionTests.cs b/Tests/Server.Tests/CircuitConnectionTests.cs
index a462a3a15..fdd6d6951 100644
--- a/Tests/Server.Tests/CircuitConnectionTests.cs
+++ b/Tests/Server.Tests/CircuitConnectionTests.cs
@@ -24,7 +24,6 @@ public class CircuitConnectionTests
private Mock _authService;
private Mock _clientAppState;
private HubContextFixture _agentHubContextFixture;
- private Mock _appConfig;
private Mock _circuitManager;
private Mock _toastService;
private Mock _expiringTokenService;
@@ -45,7 +44,6 @@ public async Task Init()
_authService = new Mock();
_clientAppState = new Mock();
_agentHubContextFixture = new HubContextFixture();
- _appConfig = new Mock();
_circuitManager = new Mock();
_toastService = new Mock();
_expiringTokenService = new Mock();
@@ -59,7 +57,6 @@ public async Task Init()
_dataService,
_clientAppState.Object,
_agentHubContextFixture.HubContextMock.Object,
- _appConfig.Object,
_circuitManager.Object,
_toastService.Object,
_expiringTokenService.Object,
diff --git a/Tests/Server.Tests/IoCActivator.cs b/Tests/Server.Tests/IoCActivator.cs
index 6a3466fb3..e905efb14 100644
--- a/Tests/Server.Tests/IoCActivator.cs
+++ b/Tests/Server.Tests/IoCActivator.cs
@@ -22,54 +22,29 @@ namespace Remotely.Server.Tests;
public class IoCActivator
{
public static IServiceProvider ServiceProvider { get; set; } = null!;
- private static IWebHostBuilder? _builder;
-
- public static void Activate()
- {
- if (_builder is null)
- {
- _builder = WebHost.CreateDefaultBuilder()
- .UseStartup()
- .CaptureStartupErrors(true)
- .ConfigureAppConfiguration(config =>
- {
- config.AddInMemoryCollection(new Dictionary()
- {
- ["ApplicationOptions:DBProvider"] = "InMemory"
- });
- });
-
- _builder.Build();
- }
- }
-
+ private static WebApplicationBuilder? _builder;
+ private static WebApplication? _webApp;
[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
- Activate();
- }
-}
+ _builder = WebApplication.CreateBuilder();
-public class Startup
-{
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDbContext();
-
- services.AddIdentity(options => options.Stores.MaxLengthForKeys = 128)
- .AddEntityFrameworkStores()
- .AddDefaultTokenProviders();
+ _builder.Services.AddDbContext(
+ contextLifetime: ServiceLifetime.Transient,
+ optionsLifetime: ServiceLifetime.Transient);
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
+ _builder.Services.
+ AddIdentity(options => options.Stores.MaxLengthForKeys = 128)
+ .AddEntityFrameworkStores()
+ .AddDefaultTokenProviders();
- IoCActivator.ServiceProvider = services.BuildServiceProvider();
- }
+ _builder.Services.AddTransient();
+ _builder.Services.AddTransient();
+ _builder.Services.AddTransient();
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
+ _webApp = _builder.Build();
+ ServiceProvider = _webApp.Services;
}
}
+
diff --git a/Utilities/Publish.ps1 b/Utilities/Publish.ps1
index e4eb184af..75c8ba726 100644
--- a/Utilities/Publish.ps1
+++ b/Utilities/Publish.ps1
@@ -103,7 +103,7 @@ if ((Test-Path -Path "$Root\Agent\bin\publish\linux-x64") -eq $true) {
}
-# Publish Core clients.
+# Publish agents.
dotnet publish /p:Version=$CurrentVersion /p:FileVersion=$CurrentVersion --runtime win-x64 --self-contained --configuration Release --output "$Root\Agent\bin\publish\win-x64" "$Root\Agent"
dotnet publish /p:Version=$CurrentVersion /p:FileVersion=$CurrentVersion --runtime linux-x64 --self-contained --configuration Release --output "$Root\Agent\bin\publish\linux-x64" "$Root\Agent"
dotnet publish /p:Version=$CurrentVersion /p:FileVersion=$CurrentVersion --runtime win-x86 --self-contained --configuration Release --output "$Root\Agent\bin\publish\win-x86" "$Root\Agent"
diff --git a/.dockerignore b/docker-compose/.dockerignore
similarity index 100%
rename from .dockerignore
rename to docker-compose/.dockerignore
diff --git a/docker-compose/docker-compose.dcproj b/docker-compose/docker-compose.dcproj
index 6be91d1de..cfbe7b521 100644
--- a/docker-compose/docker-compose.dcproj
+++ b/docker-compose/docker-compose.dcproj
@@ -2,13 +2,13 @@
2.1
- remotely
+ remotely
Linux
False
90ec49b2-b56a-4ecd-8f63-2162dd140f7c
LaunchBrowser
{Scheme}://localhost:{ServicePort}
- server
+ remotely
diff --git a/docker-compose/docker-compose.override.yml b/docker-compose/docker-compose.override.yml
index 0cde41cf8..f9d111eab 100644
--- a/docker-compose/docker-compose.override.yml
+++ b/docker-compose/docker-compose.override.yml
@@ -1,51 +1,14 @@
version: '3.4'
services:
- server:
- build:
- context: ../Server
- dockerfile: Dockerfile.local
+ remotely:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=5000
- ASPNETCORE_HTTPS_PORTS=5001
- - Remotely_ApplicationOptions__AllowApiLogin=false,
- #- Remotely_ApplicationOptions__BannedDevices__0=,
- - Remotely_ApplicationOptions__DataRetentionInDays=90,
- - Remotely_ApplicationOptions__DBProvider=SQLite,
- - Remotely_ApplicationOptions__EnableRemoteControlRecording=false,
- - Remotely_ApplicationOptions__EnableWindowsEventLog=false,
- - Remotely_ApplicationOptions__EnforceAttendedAccess=false,
- - Remotely_ApplicationOptions__ForceClientHttps=false,
- #- Remotely_ApplicationOptions__KnownProxies__0=,
- - Remotely_ApplicationOptions__MaxConcurrentUpdates=10,
- - Remotely_ApplicationOptions__MaxOrganizationCount=1,
- - Remotely_ApplicationOptions__MessageOfTheDay=,
- - Remotely_ApplicationOptions__RedirectToHttps=true,
- - Remotely_ApplicationOptions__RemoteControlNotifyUser=true,
- - Remotely_ApplicationOptions__RemoteControlRequiresAuthentication=true,
- - Remotely_ApplicationOptions__RemoteControlSessionLimit=3,
- - Remotely_ApplicationOptions__Require2FA=false,
- - Remotely_ApplicationOptions__SmtpDisplayName=,
- - Remotely_ApplicationOptions__SmtpEmail=,
- - Remotely_ApplicationOptions__SmtpHost=,
- - Remotely_ApplicationOptions__SmtpLocalDomain=,
- - Remotely_ApplicationOptions__SmtpCheckCertificateRevocation=true,
- - Remotely_ApplicationOptions__SmtpPassword=,
- - Remotely_ApplicationOptions__SmtpPort=587,
- - Remotely_ApplicationOptions__SmtpUserName=,
- - Remotely_ApplicationOptions__Theme=Dark,
- #- Remotely_ApplicationOptions__TrustedCorsOrigins__0=,
- - Remotely_ApplicationOptions__UseHsts=false,
- - Remotely_ApplicationOptions__UseHttpLogging=false
ports:
- - "5000"
- - "5001"
+ - "5000:5000"
+ - "5001:5001"
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
- - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
- - remotely-data:/app/AppData
-
-volumes:
- remotely-data:
- name: remotely-data
\ No newline at end of file
+ - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
\ No newline at end of file
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
index 5ce84f900..be573190a 100644
--- a/docker-compose/docker-compose.yml
+++ b/docker-compose/docker-compose.yml
@@ -1,45 +1,62 @@
version: '3.4'
+volumes:
+ remotely-data:
+ name: remotely-data
+
services:
remotely:
- image: immybot/remotely
+ image: immybot/remotely:latest
volumes:
- - /remotely-data:/app/AppData
+ - remotely-data:/app/AppData
build:
- context: ../Server
- dockerfile: Dockerfile
+ context: ../
+ dockerfile: Server/Dockerfile
ports:
- "5000:5000"
+ - "5001:5001"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_HTTP_PORTS=5000
- ASPNETCORE_HTTPS_PORTS=5001
- - Remotely_ApplicationOptions__AllowApiLogin=false,
- #- Remotely_ApplicationOptions__BannedDevices__0=,
- - Remotely_ApplicationOptions__DataRetentionInDays=90,
- - Remotely_ApplicationOptions__DBProvider=SQLite,
- - Remotely_ApplicationOptions__EnableRemoteControlRecording=false,
- - Remotely_ApplicationOptions__EnableWindowsEventLog=false,
- - Remotely_ApplicationOptions__EnforceAttendedAccess=false,
- - Remotely_ApplicationOptions__ForceClientHttps=false,
- #- Remotely_ApplicationOptions__KnownProxies__0=,
- - Remotely_ApplicationOptions__MaxConcurrentUpdates=10,
- - Remotely_ApplicationOptions__MaxOrganizationCount=1,
- - Remotely_ApplicationOptions__MessageOfTheDay=,
- - Remotely_ApplicationOptions__RedirectToHttps=true,
- - Remotely_ApplicationOptions__RemoteControlNotifyUser=true,
- - Remotely_ApplicationOptions__RemoteControlRequiresAuthentication=true,
- - Remotely_ApplicationOptions__RemoteControlSessionLimit=3,
- - Remotely_ApplicationOptions__Require2FA=false,
- - Remotely_ApplicationOptions__SmtpDisplayName=,
- - Remotely_ApplicationOptions__SmtpEmail=,
- - Remotely_ApplicationOptions__SmtpHost=,
- - Remotely_ApplicationOptions__SmtpLocalDomain=,
- - Remotely_ApplicationOptions__SmtpCheckCertificateRevocation=true,
- - Remotely_ApplicationOptions__SmtpPassword=,
- - Remotely_ApplicationOptions__SmtpPort=587,
- - Remotely_ApplicationOptions__SmtpUserName=,
- - Remotely_ApplicationOptions__Theme=Dark,
- #- Remotely_ApplicationOptions__TrustedCorsOrigins__0=,
- - Remotely_ApplicationOptions__UseHsts=false,
+ # Other ASP.NET Core configurations can be overridden here, such as Logging.
+ # See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-8.0
+
+ # Values for DbProvider are SQLite, SQLServer, and PostgreSQL.
+ - Remotely_ApplicationOptions__DbProvider=SQLite
+ # This path shouldn't be changed. It points to the Docker volume.
+ - Remotely_ConnectionStrings__SQLite=Data Source=/app/AppData/Remotely.db
+ # If using SQL Server, change the connection string to point to your SQL Server instance.
+ - Remotely_ConnectionStrings__SQLServer=Server=(localdb)\\mssqllocaldb;Database=Remotely-Server-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true
+ # If using PostgreSQL, change the connection string to point to your PostgreSQL instance.
+ - Remotely_ConnectionStrings__PostgreSQL=Server=Host=localhost;Database=Remotely;Username=postgres;
+
+ - Remotely_ApplicationOptions__AllowApiLogin=false
+ #- Remotely_ApplicationOptions__BannedDevices__0=
+ - Remotely_ApplicationOptions__DataRetentionInDays=90
+ - Remotely_ApplicationOptions__DBProvider=SQLite
+ - Remotely_ApplicationOptions__EnableRemoteControlRecording=false
+ - Remotely_ApplicationOptions__EnableWindowsEventLog=false
+ - Remotely_ApplicationOptions__EnforceAttendedAccess=false
+ - Remotely_ApplicationOptions__ForceClientHttps=false
+ #- Remotely_ApplicationOptions__KnownProxies__0=
+ - Remotely_ApplicationOptions__MaxConcurrentUpdates=10
+ - Remotely_ApplicationOptions__MaxOrganizationCount=1
+ - Remotely_ApplicationOptions__MessageOfTheDay=
+ - Remotely_ApplicationOptions__RedirectToHttps=true
+ - Remotely_ApplicationOptions__RemoteControlNotifyUser=true
+ - Remotely_ApplicationOptions__RemoteControlRequiresAuthentication=true
+ - Remotely_ApplicationOptions__RemoteControlSessionLimit=3
+ - Remotely_ApplicationOptions__Require2FA=false
+ - Remotely_ApplicationOptions__SmtpDisplayName=
+ - Remotely_ApplicationOptions__SmtpEmail=
+ - Remotely_ApplicationOptions__SmtpHost=
+ - Remotely_ApplicationOptions__SmtpLocalDomain=
+ - Remotely_ApplicationOptions__SmtpCheckCertificateRevocation=true
+ - Remotely_ApplicationOptions__SmtpPassword=
+ - Remotely_ApplicationOptions__SmtpPort=587
+ - Remotely_ApplicationOptions__SmtpUserName=
+ - Remotely_ApplicationOptions__Theme=Dark
+ #- Remotely_ApplicationOptions__TrustedCorsOrigins__0=
+ - Remotely_ApplicationOptions__UseHsts=false
- Remotely_ApplicationOptions__UseHttpLogging=false
\ No newline at end of file