From e70bc3643a9718fb287b348bdc8ef27b417b5cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Wed, 26 Jul 2023 08:12:53 +0200 Subject: [PATCH] Server create container&element --- .../FileTime.Server.App.Abstractions.csproj | 9 +++ .../DummyAdminElevationManager.cs | 14 ++++ .../FileTime.Server.App.csproj | 18 +++++ src/Server/FileTime.Server.App/Startup.cs | 13 +++ ...FileTime.Server.Common.Abstractions.csproj | 14 ++++ .../IApplicationStopper.cs | 6 ++ .../IRemoteConnection.cs | 10 +++ .../ApplicationStopper.cs | 15 ++++ .../Connections/SignalR/ISignalRClient.cs | 6 ++ .../Connections/SignalR/ISignalRHub.cs | 8 ++ .../Connections/SignalR/SignalRConnection.cs | 42 ++++++++++ .../FileTime.Server.Common.csproj | 21 +++++ src/Server/FileTime.Server.Common/Startup.cs | 14 ++++ .../FileTime.Server.Web/ConnectionHub.cs | 45 +++++++++++ .../FileTime.Server.Web.csproj | 22 ++++++ src/Server/FileTime.Server.Web/Main.cs | 53 +++++++++++++ .../PortWriterConfiguration.cs | 7 ++ .../FileTime.Server.Web/PortWriterService.cs | 79 +++++++++++++++++++ .../FileTime.Server/FileTime.Server.csproj | 23 ++++++ src/Server/FileTime.Server/Program.cs | 64 +++++++++++++++ 20 files changed, 483 insertions(+) create mode 100644 src/Server/FileTime.Server.App.Abstractions/FileTime.Server.App.Abstractions.csproj create mode 100644 src/Server/FileTime.Server.App/DummyAdminElevationManager.cs create mode 100644 src/Server/FileTime.Server.App/FileTime.Server.App.csproj create mode 100644 src/Server/FileTime.Server.App/Startup.cs create mode 100644 src/Server/FileTime.Server.Common.Abstractions/FileTime.Server.Common.Abstractions.csproj create mode 100644 src/Server/FileTime.Server.Common.Abstractions/IApplicationStopper.cs create mode 100644 src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs create mode 100644 src/Server/FileTime.Server.Common/ApplicationStopper.cs create mode 100644 src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRClient.cs create mode 100644 src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs create mode 100644 src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs create mode 100644 src/Server/FileTime.Server.Common/FileTime.Server.Common.csproj create mode 100644 src/Server/FileTime.Server.Common/Startup.cs create mode 100644 src/Server/FileTime.Server.Web/ConnectionHub.cs create mode 100644 src/Server/FileTime.Server.Web/FileTime.Server.Web.csproj create mode 100644 src/Server/FileTime.Server.Web/Main.cs create mode 100644 src/Server/FileTime.Server.Web/PortWriterConfiguration.cs create mode 100644 src/Server/FileTime.Server.Web/PortWriterService.cs create mode 100644 src/Server/FileTime.Server/FileTime.Server.csproj create mode 100644 src/Server/FileTime.Server/Program.cs diff --git a/src/Server/FileTime.Server.App.Abstractions/FileTime.Server.App.Abstractions.csproj b/src/Server/FileTime.Server.App.Abstractions/FileTime.Server.App.Abstractions.csproj new file mode 100644 index 0000000..a1ed5b3 --- /dev/null +++ b/src/Server/FileTime.Server.App.Abstractions/FileTime.Server.App.Abstractions.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/src/Server/FileTime.Server.App/DummyAdminElevationManager.cs b/src/Server/FileTime.Server.App/DummyAdminElevationManager.cs new file mode 100644 index 0000000..c7c2ddf --- /dev/null +++ b/src/Server/FileTime.Server.App/DummyAdminElevationManager.cs @@ -0,0 +1,14 @@ +using FileTime.Providers.LocalAdmin; +using FileTime.Server.Common; + +namespace FileTime.Server.App; + +public class DummyAdminElevationManager : IAdminElevationManager +{ + public bool IsAdminInstanceRunning => throw new NotImplementedException(); + public Task CreateConnectionAsync() => throw new NotImplementedException(); + + public string ProviderName => throw new NotImplementedException(); + public Task CreateAdminInstanceIfNecessaryAsync(string? confirmationMessage = null) => throw new NotImplementedException(); + public bool IsAdminModeSupported => false; +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.App/FileTime.Server.App.csproj b/src/Server/FileTime.Server.App/FileTime.Server.App.csproj new file mode 100644 index 0000000..b4c49ac --- /dev/null +++ b/src/Server/FileTime.Server.App/FileTime.Server.App.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + diff --git a/src/Server/FileTime.Server.App/Startup.cs b/src/Server/FileTime.Server.App/Startup.cs new file mode 100644 index 0000000..a240091 --- /dev/null +++ b/src/Server/FileTime.Server.App/Startup.cs @@ -0,0 +1,13 @@ +using FileTime.Providers.LocalAdmin; +using Microsoft.Extensions.DependencyInjection; + +namespace FileTime.Server.App; + +public static class Startup +{ + public static IServiceCollection AddServerServices(this IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + return serviceCollection; + } +} \ 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 new file mode 100644 index 0000000..aa2bde0 --- /dev/null +++ b/src/Server/FileTime.Server.Common.Abstractions/FileTime.Server.Common.Abstractions.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + FileTime.Server.Common + + + + + + + diff --git a/src/Server/FileTime.Server.Common.Abstractions/IApplicationStopper.cs b/src/Server/FileTime.Server.Common.Abstractions/IApplicationStopper.cs new file mode 100644 index 0000000..7a8fcb2 --- /dev/null +++ b/src/Server/FileTime.Server.Common.Abstractions/IApplicationStopper.cs @@ -0,0 +1,6 @@ +namespace FileTime.Server.Common; + +public interface IApplicationStopper +{ + void Stop(); +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs new file mode 100644 index 0000000..ee3e79e --- /dev/null +++ b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs @@ -0,0 +1,10 @@ +using FileTime.Core.Models; + +namespace FileTime.Server.Common; + +public interface IRemoteConnection +{ + Task Exit(); + Task CreateContainerAsync(string contentProviderId, FullName fullName); + Task CreateElementAsync(string contentProviderId, FullName fullName); +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.Common/ApplicationStopper.cs b/src/Server/FileTime.Server.Common/ApplicationStopper.cs new file mode 100644 index 0000000..7a55bcc --- /dev/null +++ b/src/Server/FileTime.Server.Common/ApplicationStopper.cs @@ -0,0 +1,15 @@ +namespace FileTime.Server.Common; + +public class ApplicationStopper : IApplicationStopper +{ + private readonly Action _stopAction; + + public ApplicationStopper(Action stopAction) + { + ArgumentNullException.ThrowIfNull(stopAction); + _stopAction = stopAction; + } + + + public void Stop() => _stopAction(); +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRClient.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRClient.cs new file mode 100644 index 0000000..bd55598 --- /dev/null +++ b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRClient.cs @@ -0,0 +1,6 @@ +namespace FileTime.Server.Common.Connections.SignalR; + +public interface ISignalRClient +{ + +} \ 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 new file mode 100644 index 0000000..7487070 --- /dev/null +++ b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs @@ -0,0 +1,8 @@ +namespace FileTime.Server.Common.Connections.SignalR; + +public interface ISignalRHub +{ + Task Exit(); + Task CreateContainerAsync(string contentProviderId, string fullName); + Task CreateElementAsync(string contentProviderId, string fullName); +} \ 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 new file mode 100644 index 0000000..0993da5 --- /dev/null +++ b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs @@ -0,0 +1,42 @@ +using FileTime.Core.Models; +using InitableService; +using Microsoft.AspNetCore.SignalR.Client; +using TypedSignalR.Client; + +namespace FileTime.Server.Common.Connections.SignalR; + +public class SignalRConnection : IRemoteConnection, IAsyncInitable +{ + private string _baseUrl = null!; + private HubConnection _connection = null!; + + private ISignalRHub CreateClient() => _connection.CreateHubProxy(); + + public async Task InitAsync(string baseUrl) + { + _baseUrl = baseUrl; + + _connection = new HubConnectionBuilder() + .WithUrl(_baseUrl) + .Build(); + await _connection.StartAsync(); + } + + public async Task Exit() + { + var client = CreateClient(); + await client.Exit(); + } + + public async Task CreateContainerAsync(string contentProviderId, FullName fullName) + { + var client = CreateClient(); + await client.CreateContainerAsync(contentProviderId, fullName.Path); + } + + public async Task CreateElementAsync(string contentProviderId, FullName fullName) + { + var client = CreateClient(); + await client.CreateElementAsync(contentProviderId, fullName.Path); + } +} \ 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 new file mode 100644 index 0000000..5908eec --- /dev/null +++ b/src/Server/FileTime.Server.Common/FileTime.Server.Common.csproj @@ -0,0 +1,21 @@ + + + + net7.0 + enable + enable + + + + + + + + + + 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 new file mode 100644 index 0000000..4c34e58 --- /dev/null +++ b/src/Server/FileTime.Server.Common/Startup.cs @@ -0,0 +1,14 @@ +using FileTime.Server.Common.Connections.SignalR; +using Microsoft.Extensions.DependencyInjection; + +namespace FileTime.Server.Common; + +public static class Startup +{ + public static IServiceCollection AddRemoteServices(this IServiceCollection services) + { + services.AddTransient(); + 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 new file mode 100644 index 0000000..99b8032 --- /dev/null +++ b/src/Server/FileTime.Server.Web/ConnectionHub.cs @@ -0,0 +1,45 @@ +using FileTime.Core.ContentAccess; +using FileTime.Core.Models; +using FileTime.Server.Common; +using FileTime.Server.Common.Connections.SignalR; +using Microsoft.AspNetCore.SignalR; + +namespace FileTime.Server.Web; + +public class ConnectionHub : Hub, ISignalRHub +{ + private readonly IContentProviderRegistry _contentProviderRegistry; + private readonly IContentAccessorFactory _contentAccessorFactory; + private readonly IApplicationStopper _applicationStopper; + + public ConnectionHub( + IContentProviderRegistry contentProviderRegistry, + IContentAccessorFactory contentAccessorFactory, + IApplicationStopper applicationStopper) + { + _contentProviderRegistry = contentProviderRegistry; + _contentAccessorFactory = contentAccessorFactory; + _applicationStopper = applicationStopper; + } + + public Task Exit() + { + _applicationStopper.Stop(); + return Task.CompletedTask; + } + + public async Task CreateContainerAsync(string contentProviderId, string fullName) + { + //TODO handle no content provider with id + var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); + var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider); + await itemCreator.CreateContainerAsync(contentProvider, new FullName(fullName)); + } + + public async Task CreateElementAsync(string contentProviderId, string fullName) + { + var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); + var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider); + await itemCreator.CreateElementAsync(contentProvider, new FullName(fullName)); + } +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.Web/FileTime.Server.Web.csproj b/src/Server/FileTime.Server.Web/FileTime.Server.Web.csproj new file mode 100644 index 0000000..01f9af4 --- /dev/null +++ b/src/Server/FileTime.Server.Web/FileTime.Server.Web.csproj @@ -0,0 +1,22 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/src/Server/FileTime.Server.Web/Main.cs b/src/Server/FileTime.Server.Web/Main.cs new file mode 100644 index 0000000..3c62f9c --- /dev/null +++ b/src/Server/FileTime.Server.Web/Main.cs @@ -0,0 +1,53 @@ +using System.Net; +using Autofac; +using Autofac.Extensions.DependencyInjection; +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.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace FileTime.Server.Web; + +public class Program +{ + public static async Task Start(string[] args, IContainer rootContainer, CancellationToken applicationExit) + { + var builder = WebApplication.CreateBuilder(args); + + var configuration = builder.Configuration; + + builder.Host.UseServiceProviderFactory( + new AutofacChildLifetimeScopeServiceProviderFactory(rootContainer.BeginLifetimeScope("WebScope")) + ); + + builder.Host.UseSerilog(); + builder.WebHost.ConfigureKestrel((buildContext, serverOptions) => + { + var port = buildContext.Configuration.GetValue("WebPort") ?? 0; + serverOptions.Listen(new IPEndPoint(IPAddress.Loopback, port)); + }); + + builder.Services.AddSignalR(); + builder.Services.AddHealthChecks(); + builder.Services.AddHostedService(); + + builder.Services.AddOptions() + .Bind(configuration.GetSection(PortWriterConfiguration.SectionName)); + + var app = builder.Build(); + + if (!app.Environment.IsDevelopment()) + { + } + + app.MapHub("/RemoteHub"); + app.UseHealthChecks("/health"); + + await app.RunAsync(applicationExit); + } +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.Web/PortWriterConfiguration.cs b/src/Server/FileTime.Server.Web/PortWriterConfiguration.cs new file mode 100644 index 0000000..277871c --- /dev/null +++ b/src/Server/FileTime.Server.Web/PortWriterConfiguration.cs @@ -0,0 +1,7 @@ +namespace FileTime.Server.Web; + +public class PortWriterConfiguration +{ + public const string SectionName = "PortWriter"; + public string? Filename { get; set; } +} \ No newline at end of file diff --git a/src/Server/FileTime.Server.Web/PortWriterService.cs b/src/Server/FileTime.Server.Web/PortWriterService.cs new file mode 100644 index 0000000..609dd9e --- /dev/null +++ b/src/Server/FileTime.Server.Web/PortWriterService.cs @@ -0,0 +1,79 @@ +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace FileTime.Server.Web; + +public class PortWriterService : IHostedService +{ + private readonly IServer _server; + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly IOptions _configuration; + private readonly ILogger _logger; + + public PortWriterService( + IServer server, + IHostApplicationLifetime hostApplicationLifetime, + IOptions configuration, + ILogger logger) + { + _server = server; + _hostApplicationLifetime = hostApplicationLifetime; + _configuration = configuration; + _logger = logger; + } + + + public Task StartAsync(CancellationToken cancellationToken) + { + _hostApplicationLifetime.ApplicationStarted.Register(WritePort); + + return Task.CompletedTask; + } + + private void WritePort() + { + try + { + var filename = _configuration.Value.Filename; + if (filename is null) + { + _logger.LogWarning("Could not save port to file as there were no file name given"); + return; + } + + using var tempFileStream = File.CreateText(filename); + var address = GetAddress(); + if (address is null) + { + _logger.LogError("Could not get address"); + return; + } + + var couldParsePort = int.TryParse(address.Split(':').LastOrDefault(), out var port); + if (!couldParsePort) + { + _logger.LogError("Could not parse port from address {Address}", address); + return; + } + + tempFileStream.Write(port.ToString()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not save port to file"); + } + } + + private string? GetAddress() + { + var features = _server.Features; + var addresses = features.Get(); + var address = addresses?.Addresses.FirstOrDefault(); + return address; + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} \ No newline at end of file diff --git a/src/Server/FileTime.Server/FileTime.Server.csproj b/src/Server/FileTime.Server/FileTime.Server.csproj new file mode 100644 index 0000000..d87b452 --- /dev/null +++ b/src/Server/FileTime.Server/FileTime.Server.csproj @@ -0,0 +1,23 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/src/Server/FileTime.Server/Program.cs b/src/Server/FileTime.Server/Program.cs new file mode 100644 index 0000000..f64e150 --- /dev/null +++ b/src/Server/FileTime.Server/Program.cs @@ -0,0 +1,64 @@ +using Autofac; +using Autofac.Extensions.DependencyInjection; +using FileTime.App.DependencyInjection; +using FileTime.Providers.Local; +using FileTime.Server.App; +using FileTime.Server.Common; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Serilog; + + +var applicationCancellation = new CancellationTokenSource(); +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + +var bootstrapConfiguration = CreateConfiguration(); + +var rootContainer = CreateRootDiContainer(bootstrapConfiguration); + +var webThread = CreateStartup(FileTime.Server.Web.Program.Start); +webThread.Start(); + +Thread CreateStartup(Func startup) +{ + var thread = new Thread(() => { HandleStartup(() => startup(args, rootContainer, applicationCancellation.Token).Wait()); }); + return thread; +} + +void HandleStartup(Action action) +{ + try + { + action(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } +} + +IConfigurationRoot CreateConfiguration() +{ + var configurationBuilder = new ConfigurationBuilder(); + return configurationBuilder.Build(); +} + +IContainer CreateRootDiContainer(IConfigurationRoot configuration) +{ + var serviceCollection = DependencyInjection + .RegisterDefaultServices(configuration) + .AddLocalProviderServices() + .AddServerServices(); + + serviceCollection.TryAddSingleton( + new ApplicationStopper(() => applicationCancellation.Cancel()) + ); + + var containerBuilder = new ContainerBuilder(); + containerBuilder.Populate(serviceCollection); + return containerBuilder.Build(); +} \ No newline at end of file