diff --git a/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj b/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj
index 7b6d24c..50c2c46 100644
--- a/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj
+++ b/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj
@@ -5,6 +5,7 @@
+
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs
index 3cc81c6..5ce3207 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs
@@ -21,7 +21,7 @@ public class App : Application
var configuration = Startup.CreateConfiguration();
DI.ServiceProvider = DependencyInjection
.RegisterDefaultServices(configuration: configuration)
- .AddRemoteServices()
+ .AddServerCoreServices()
.AddFrequencyNavigation()
.AddCommandPalette()
.AddSearch()
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj b/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj
index 8842942..ce029e4 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj
@@ -36,6 +36,7 @@
+
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json b/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json
index cca6606..1a159eb 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json
@@ -4,7 +4,9 @@
"Default": "Verbose",
"Override": {
"Microsoft": "Information",
- "System": "Warning"
+ "System": "Warning",
+ "Microsoft.AspNetCore.SignalR": "Verbose",
+ "Microsoft.AspNetCore.Http.Connections": "Verbose"
}
}
}
diff --git a/src/Providers/FileTime.Providers.Local/LocalContentWriterFactory.cs b/src/Providers/FileTime.Providers.Local/LocalContentWriterFactory.cs
index f2e0c4a..66696a0 100644
--- a/src/Providers/FileTime.Providers.Local/LocalContentWriterFactory.cs
+++ b/src/Providers/FileTime.Providers.Local/LocalContentWriterFactory.cs
@@ -1,12 +1,34 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
+using FileTime.Providers.LocalAdmin;
+using Microsoft.Extensions.Logging;
namespace FileTime.Providers.Local;
public class LocalContentWriterFactory : IContentWriterFactory
{
- public Task CreateContentWriterAsync(IElement element)
+ private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
+ private readonly ILogger _logger;
+
+ public LocalContentWriterFactory(
+ IAdminContentAccessorFactory adminContentAccessorFactory,
+ ILogger logger)
{
- return Task.FromResult((IContentWriter) new LocalContentWriter(File.OpenWrite(element.NativePath!.Path)));
+ _adminContentAccessorFactory = adminContentAccessorFactory;
+ _logger = logger;
+ }
+
+ public async Task CreateContentWriterAsync(IElement element)
+ {
+ try
+ {
+ return new LocalContentWriter(File.OpenWrite(element.NativePath!.Path));
+ }
+ catch (UnauthorizedAccessException e)
+ {
+ _logger.LogDebug(e, "Failed to write to element with path {Path}", element.NativePath);
+ var adminContentWriter = await _adminContentAccessorFactory.CreateContentWriterAsync(element.NativePath!);
+ return adminContentWriter;
+ }
}
}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs
index 8e6e70c..664c5a6 100644
--- a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs
+++ b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs
@@ -1,4 +1,5 @@
-using FileTime.Providers.Remote;
+using FileTime.Core.Models;
+using FileTime.Providers.Remote;
namespace FileTime.Providers.LocalAdmin;
@@ -8,4 +9,5 @@ public interface IAdminContentAccessorFactory
Task CreateAdminItemCreatorAsync();
Task CreateAdminItemDeleterAsync();
Task CreateAdminItemMoverAsync();
+ Task CreateContentWriterAsync(NativePath nativePath);
}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs b/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs
index e3e2bc3..e277870 100644
--- a/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs
+++ b/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs
@@ -1,7 +1,9 @@
using System.Diagnostics;
+using FileTime.Core.Models;
using FileTime.Providers.Remote;
using FileTime.Server.Common;
using InitableService;
+using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Providers.LocalAdmin;
@@ -13,7 +15,7 @@ public class AdminContentAccessorFactory : IAdminContentAccessorFactory
public AdminContentAccessorFactory(
IAdminElevationManager adminElevationManager,
IServiceProvider serviceProvider
- )
+ )
{
_adminElevationManager = adminElevationManager;
_serviceProvider = serviceProvider;
@@ -27,19 +29,33 @@ public class AdminContentAccessorFactory : IAdminContentAccessorFactory
public async Task CreateAdminItemDeleterAsync()
=> await CreateHelperAsync();
- public async Task CreateAdminItemMoverAsync()
+ public async Task CreateAdminItemMoverAsync()
=> await CreateHelperAsync();
+ public async Task CreateContentWriterAsync(NativePath nativePath)
+ {
+ await _adminElevationManager.CreateAdminInstanceIfNecessaryAsync();
+ var connection = await _adminElevationManager.CreateConnectionAsync();
+ var contentWriter = _serviceProvider.GetInitableResolver(
+ connection,
+ _adminElevationManager.ProviderName,
+ nativePath,
+ Guid.NewGuid()
+ ).GetRequiredService();
+
+ return contentWriter;
+ }
+
private async Task CreateHelperAsync()
where T : class, IInitable
{
await _adminElevationManager.CreateAdminInstanceIfNecessaryAsync();
var connection = await _adminElevationManager.CreateConnectionAsync();
-
+
Debug.Assert(connection != null);
-
+
var helper = _serviceProvider.GetInitableResolver(
- connection,
+ connection,
_adminElevationManager.ProviderName)
.GetRequiredService();
return helper;
diff --git a/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs b/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs
index 98beae6..7a558f7 100644
--- a/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs
+++ b/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs
@@ -89,8 +89,6 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
process.Start();
_adminProcess = process;
- IsAdminInstanceRunning = true;
-
//TODO: timeout
while (!File.Exists(portFileName) || new FileInfo(portFileName).Length == 0)
await Task.Delay(10);
@@ -109,6 +107,8 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
}
}
+ IsAdminInstanceRunning = true;
+
var connectionInfo = new ConnectionInfo
{
SignalRBaseUrl = $"http://localhost:{port}/RemoteHub"
diff --git a/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentWriter.cs b/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentWriter.cs
new file mode 100644
index 0000000..3df1127
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentWriter.cs
@@ -0,0 +1,12 @@
+using FileTime.Core.ContentAccess;
+using FileTime.Core.Models;
+using FileTime.Server.Common;
+using InitableService;
+
+namespace FileTime.Providers.Remote;
+
+public interface IRemoteContentWriter :
+ IContentWriter,
+ IInitable
+{
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs b/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs
new file mode 100644
index 0000000..eb3a90a
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs
@@ -0,0 +1,51 @@
+using FileTime.Core.Models;
+using FileTime.Server.Common;
+
+namespace FileTime.Providers.Remote;
+
+public class RemoteContentWriter : IRemoteContentWriter
+{
+ private IRemoteConnection _remoteConnection = null!;
+ private string _remoteContentProviderId = null!;
+ private NativePath _nativePath = null!;
+ private string _transactionId = null!;
+ private bool _isRemoteWriterInitialized;
+
+ public void Init(
+ IRemoteConnection remoteConnection,
+ string remoteContentProviderId,
+ NativePath nativePath,
+ Guid transactionId)
+ {
+ _remoteConnection = remoteConnection;
+ _remoteContentProviderId = remoteContentProviderId;
+ _nativePath = nativePath;
+ _transactionId = transactionId.ToString();
+ }
+
+ public void Dispose()
+ {
+ if (!_isRemoteWriterInitialized) return;
+ _remoteConnection.CloseWriterAsync(_transactionId);
+ }
+
+ public int PreferredBufferSize => 10 * 1024;
+
+ public async Task WriteBytesAsync(byte[] data, int? index = null)
+ {
+ if (!_isRemoteWriterInitialized) await InitializeRemoteWriter(_nativePath);
+ await _remoteConnection.WriteBytesAsync(_transactionId, data, index);
+ }
+
+ public async Task FlushAsync()
+ {
+ if (!_isRemoteWriterInitialized) return;
+ await _remoteConnection.FlushWriterAsync(_transactionId);
+ }
+
+ private async Task InitializeRemoteWriter(NativePath nativePath)
+ {
+ _isRemoteWriterInitialized = true;
+ await _remoteConnection.InitializeRemoteWriter(_remoteContentProviderId, _transactionId, nativePath);
+ }
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Remote/Startup.cs b/src/Providers/FileTime.Providers.Remote/Startup.cs
index 6be7b19..9e77315 100644
--- a/src/Providers/FileTime.Providers.Remote/Startup.cs
+++ b/src/Providers/FileTime.Providers.Remote/Startup.cs
@@ -11,6 +11,7 @@ public static class Startup
serviceCollection.TryAddTransient();
serviceCollection.TryAddTransient();
serviceCollection.TryAddTransient();
+ serviceCollection.TryAddTransient();
return serviceCollection;
}
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common.Abstractions/ConnectionHandlerParameters.cs b/src/Server/FileTime.Server.Common.Abstractions/ConnectionHandlerParameters.cs
new file mode 100644
index 0000000..22f1d5e
--- /dev/null
+++ b/src/Server/FileTime.Server.Common.Abstractions/ConnectionHandlerParameters.cs
@@ -0,0 +1,11 @@
+using Autofac;
+using Microsoft.Extensions.Configuration;
+
+namespace FileTime.Server.Common;
+
+public record ConnectionHandlerParameters(
+ string[] Args,
+ IContainer RootContainer,
+ IConfigurationRoot ConfigurationRoot,
+ CancellationToken ApplicationExit
+);
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs b/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs
new file mode 100644
index 0000000..6d8d93e
--- /dev/null
+++ b/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs
@@ -0,0 +1,10 @@
+using FileTime.Core.ContentAccess;
+
+namespace FileTime.Server.Common.ContentAccess;
+
+public interface IContentAccessManager
+{
+ void AddContentWriter(string transactionId, IContentWriter contentWriter);
+ IContentWriter GetContentWriter(string transactionId);
+ void RemoveContentWriter(string transactionId);
+}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common.Abstractions/FileTime.Server.Common.Abstractions.csproj b/src/Server/FileTime.Server.Common.Abstractions/FileTime.Server.Common.Abstractions.csproj
index aa2bde0..9f55574 100644
--- a/src/Server/FileTime.Server.Common.Abstractions/FileTime.Server.Common.Abstractions.csproj
+++ b/src/Server/FileTime.Server.Common.Abstractions/FileTime.Server.Common.Abstractions.csproj
@@ -11,4 +11,9 @@
+
+
+
+
+
diff --git a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs
index c99c791..01c9419 100644
--- a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs
+++ b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs
@@ -9,4 +9,8 @@ public interface IRemoteConnection
Task CreateElementAsync(string contentProviderId, FullName fullName);
Task DeleteItemAsync(string contentProviderId, FullName fullName);
Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath);
+ Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath);
+ Task WriteBytesAsync(string transactionId, byte[] data, int? index);
+ Task FlushWriterAsync(string transactionId);
+ Task CloseWriterAsync(string transactionId);
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs
index 91d1fdd..a2d5283 100644
--- a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs
+++ b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs
@@ -7,4 +7,8 @@ public interface ISignalRHub
Task CreateElementAsync(string contentProviderId, string fullName);
Task DeleteItemAsync(string contentProviderId, string fullName);
Task MoveItemAsync(string contentProviderId, string fullName, string newPath);
+ Task FlushWriterAsync(string transactionId);
+ Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath);
+ Task WriteBytesAsync(string transactionId, string data, int index);
+ Task CloseWriterAsync(string transactionId);
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs
index 1a4047c..9659a4a 100644
--- a/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs
+++ b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs
@@ -1,6 +1,9 @@
-using FileTime.Core.Models;
+using System.Text;
+using FileTime.Core.Models;
using InitableService;
using Microsoft.AspNetCore.SignalR.Client;
+using Microsoft.Extensions.Logging;
+using Serilog;
using TypedSignalR.Client;
namespace FileTime.Server.Common.Connections.SignalR;
@@ -8,8 +11,8 @@ namespace FileTime.Server.Common.Connections.SignalR;
public class SignalRConnection : IRemoteConnection, IAsyncInitable
{
private static readonly Dictionary Connections = new();
- private static readonly object ConnectionsLock = new();
-
+ private static readonly object ConnectionsLock = new();
+
private string _baseUrl = null!;
private HubConnection _connection = null!;
private ISignalRHub _client = null!;
@@ -18,13 +21,15 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable
{
_baseUrl = baseUrl;
- _connection = new HubConnectionBuilder()
- .WithUrl(_baseUrl)
- .Build();
+ var connectionBuilder = new HubConnectionBuilder()
+ .ConfigureLogging(logging => { logging.AddSerilog(); })
+ .WithUrl(_baseUrl);
+
+ _connection = connectionBuilder.Build();
await _connection.StartAsync();
_client = _connection.CreateHubProxy();
}
-
+
public static async Task GetOrCreateForAsync(string baseUrl)
{
SignalRConnection? connection;
@@ -62,4 +67,16 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable
public async Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath)
=> await _client.MoveItemAsync(contentProviderId, fullName.Path, newPath.Path);
+
+ public async Task WriteBytesAsync(string transactionId, byte[] data, int? index)
+ => await _client.WriteBytesAsync(transactionId, Encoding.UTF8.GetString(data), index ?? -1);
+
+ public async Task FlushWriterAsync(string transactionId)
+ => await _client.FlushWriterAsync(transactionId);
+
+ public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath)
+ => await _client.InitializeRemoteWriter(contentProviderId, transactionId, nativePath.Path);
+
+ public async Task CloseWriterAsync(string transactionId)
+ => await _client.CloseWriterAsync(transactionId);
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs b/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs
new file mode 100644
index 0000000..d3a6f6d
--- /dev/null
+++ b/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs
@@ -0,0 +1,13 @@
+using FileTime.Core.ContentAccess;
+
+namespace FileTime.Server.Common.ContentAccess;
+
+public class ContentAccessManager : IContentAccessManager
+{
+ private readonly Dictionary _contentWriters = new();
+ public void AddContentWriter(string transactionId, IContentWriter contentWriter)
+ => _contentWriters.Add(transactionId, contentWriter);
+
+ public IContentWriter GetContentWriter(string transactionId) => _contentWriters[transactionId];
+ public void RemoveContentWriter(string transactionId) => _contentWriters.Remove(transactionId);
+}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common/FileTime.Server.Common.csproj b/src/Server/FileTime.Server.Common/FileTime.Server.Common.csproj
index 5908eec..cdd9ad8 100644
--- a/src/Server/FileTime.Server.Common/FileTime.Server.Common.csproj
+++ b/src/Server/FileTime.Server.Common/FileTime.Server.Common.csproj
@@ -12,6 +12,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Server/FileTime.Server.Common/Startup.cs b/src/Server/FileTime.Server.Common/Startup.cs
index 4c34e58..c419b2f 100644
--- a/src/Server/FileTime.Server.Common/Startup.cs
+++ b/src/Server/FileTime.Server.Common/Startup.cs
@@ -1,14 +1,17 @@
using FileTime.Server.Common.Connections.SignalR;
+using FileTime.Server.Common.ContentAccess;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Server.Common;
public static class Startup
{
- public static IServiceCollection AddRemoteServices(this IServiceCollection services)
+ public static IServiceCollection AddServerCoreServices(this IServiceCollection services)
{
services.AddTransient();
- services.AddSingleton();
+ services.TryAddSingleton();
+ services.AddSingleton();
return services;
}
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Web/ConnectionHub.cs b/src/Server/FileTime.Server.Web/ConnectionHub.cs
index 16cf313..a07cf31 100644
--- a/src/Server/FileTime.Server.Web/ConnectionHub.cs
+++ b/src/Server/FileTime.Server.Web/ConnectionHub.cs
@@ -1,7 +1,10 @@
-using FileTime.Core.ContentAccess;
+using System.Text;
+using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
+using FileTime.Core.Timeline;
using FileTime.Server.Common;
using FileTime.Server.Common.Connections.SignalR;
+using FileTime.Server.Common.ContentAccess;
using Microsoft.AspNetCore.SignalR;
namespace FileTime.Server.Web;
@@ -11,17 +14,20 @@ public class ConnectionHub : Hub, ISignalRHub
private readonly IContentProviderRegistry _contentProviderRegistry;
private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly IApplicationStopper _applicationStopper;
+ private readonly IContentAccessManager _contentAccessManager;
public ConnectionHub(
IContentProviderRegistry contentProviderRegistry,
IContentAccessorFactory contentAccessorFactory,
- IApplicationStopper applicationStopper)
+ IApplicationStopper applicationStopper,
+ IContentAccessManager contentAccessManager)
{
_contentProviderRegistry = contentProviderRegistry;
_contentAccessorFactory = contentAccessorFactory;
_applicationStopper = applicationStopper;
+ _contentAccessManager = contentAccessManager;
}
-
+
public Task Exit()
{
_applicationStopper.Stop();
@@ -56,4 +62,29 @@ public class ConnectionHub : Hub, ISignalRHub
var itemDeleter = _contentAccessorFactory.GetItemMover(contentProvider);
await itemDeleter.RenameAsync(contentProvider, new FullName(fullName), new FullName(newPath));
}
+
+ public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath)
+ {
+ var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
+ var item = await contentProvider.GetItemByNativePathAsync(new NativePath(nativePath), PointInTime.Present);
+ if (item is not IElement element)
+ throw new FileNotFoundException("Item is not an element", nativePath);
+
+ var contentWriter = await _contentAccessorFactory.GetContentWriterFactory(contentProvider).CreateContentWriterAsync(element);
+ _contentAccessManager.AddContentWriter(transactionId, contentWriter);
+ }
+
+ public async Task WriteBytesAsync(string transactionId, string data, int index)
+ => await _contentAccessManager.GetContentWriter(transactionId).WriteBytesAsync(Encoding.UTF8.GetBytes(data), index == -1 ? null : index);
+
+ public async Task FlushWriterAsync(string transactionId)
+ => await _contentAccessManager.GetContentWriter(transactionId).FlushAsync();
+
+
+ public Task CloseWriterAsync(string transactionId)
+ {
+ _contentAccessManager.GetContentWriter(transactionId).Dispose();
+ _contentAccessManager.RemoveContentWriter(transactionId);
+ return Task.CompletedTask;
+ }
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Web/Main.cs b/src/Server/FileTime.Server.Web/Main.cs
index 3c62f9c..4767fd1 100644
--- a/src/Server/FileTime.Server.Web/Main.cs
+++ b/src/Server/FileTime.Server.Web/Main.cs
@@ -1,11 +1,13 @@
using System.Net;
using Autofac;
using Autofac.Extensions.DependencyInjection;
+using FileTime.Server.Common;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -15,14 +17,23 @@ namespace FileTime.Server.Web;
public class Program
{
- public static async Task Start(string[] args, IContainer rootContainer, CancellationToken applicationExit)
+ public static async Task Start(ConnectionHandlerParameters parameters)
{
- var builder = WebApplication.CreateBuilder(args);
+ var builder = WebApplication.CreateBuilder(parameters.Args);
var configuration = builder.Configuration;
+ //Note: Use app wide configuration instead of the default ASP.NET Core configuration
+#pragma warning disable ASP0013
+ builder.Host.ConfigureAppConfiguration((_, configurationBuilder) =>
+ {
+ configurationBuilder.Sources.Clear();
+ configurationBuilder.AddConfiguration(parameters.ConfigurationRoot);
+ });
+#pragma warning restore ASP0013
+
builder.Host.UseServiceProviderFactory(
- new AutofacChildLifetimeScopeServiceProviderFactory(rootContainer.BeginLifetimeScope("WebScope"))
+ new AutofacChildLifetimeScopeServiceProviderFactory(parameters.RootContainer.BeginLifetimeScope("WebScope"))
);
builder.Host.UseSerilog();
@@ -32,6 +43,7 @@ public class Program
serverOptions.Listen(new IPEndPoint(IPAddress.Loopback, port));
});
+ builder.Services.AddHttpLogging(options => options.LoggingFields = HttpLoggingFields.All);
builder.Services.AddSignalR();
builder.Services.AddHealthChecks();
builder.Services.AddHostedService();
@@ -43,11 +55,12 @@ public class Program
if (!app.Environment.IsDevelopment())
{
+ app.UseHttpLogging();
}
app.MapHub("/RemoteHub");
app.UseHealthChecks("/health");
- await app.RunAsync(applicationExit);
+ await app.RunAsync(parameters.ApplicationExit);
}
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server/FileTime.Server.csproj b/src/Server/FileTime.Server/FileTime.Server.csproj
index d87b452..fa9c6fe 100644
--- a/src/Server/FileTime.Server/FileTime.Server.csproj
+++ b/src/Server/FileTime.Server/FileTime.Server.csproj
@@ -17,7 +17,15 @@
+
+
+
+
+
+
+
+
diff --git a/src/Server/FileTime.Server/Program.cs b/src/Server/FileTime.Server/Program.cs
index 07c4fee..f7cfba8 100644
--- a/src/Server/FileTime.Server/Program.cs
+++ b/src/Server/FileTime.Server/Program.cs
@@ -11,21 +11,33 @@ using Serilog;
var applicationCancellation = new CancellationTokenSource();
+var configurationRoot = CreateConfiguration();
+
Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Debug()
+#if DEBUG
+ .MinimumLevel.Verbose()
+ .ReadFrom.Configuration(configurationRoot)
+#else
+ .MinimumLevel.Information()
+#endif
.WriteTo.Console()
.CreateLogger();
-var bootstrapConfiguration = CreateConfiguration();
+var rootContainer = CreateRootDiContainer(configurationRoot);
-var rootContainer = CreateRootDiContainer(bootstrapConfiguration);
+var handlerParameters = new ConnectionHandlerParameters(
+ args,
+ rootContainer,
+ configurationRoot,
+ applicationCancellation.Token
+);
var webThread = CreateStartup(FileTime.Server.Web.Program.Start);
webThread.Start();
-Thread CreateStartup(Func startup)
+Thread CreateStartup(Func startup)
{
- var thread = new Thread(() => { HandleStartup(() => startup(args, rootContainer, applicationCancellation.Token).Wait()); });
+ var thread = new Thread(() => { HandleStartup(() => startup(handlerParameters).Wait()); });
return thread;
}
@@ -45,6 +57,11 @@ void HandleStartup(Action action)
IConfigurationRoot CreateConfiguration()
{
var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddCommandLine(args);
+#if DEBUG
+ configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true);
+ configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true);
+#endif
return configurationBuilder.Build();
}
@@ -54,9 +71,10 @@ IContainer CreateRootDiContainer(IConfigurationRoot configuration)
.RegisterDefaultServices(configuration)
.AddLocalProviderServices()
.AddServerServices()
+ .AddServerCoreServices()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog());
- serviceCollection.TryAddSingleton(
+ serviceCollection.AddSingleton(
new ApplicationStopper(() => applicationCancellation.Cancel())
);
diff --git a/src/Server/FileTime.Server/appsettings.Development.json b/src/Server/FileTime.Server/appsettings.Development.json
new file mode 100644
index 0000000..b13aca4
--- /dev/null
+++ b/src/Server/FileTime.Server/appsettings.Development.json
@@ -0,0 +1,13 @@
+{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Verbose",
+ "Override": {
+ "System": "Information",
+ "Microsoft": "Information",
+ "Microsoft.AspNetCore.SignalR": "Debug",
+ "Microsoft.AspNetCore.Http.Connections": "Debug"
+ }
+ }
+ }
+}
\ No newline at end of file