Server mode for Gui&Console

This commit is contained in:
2023-09-05 12:13:06 +02:00
parent 22ca9d7822
commit 0da5273c97
8 changed files with 134 additions and 86 deletions

View File

@@ -13,13 +13,7 @@ public class MainConfiguration
static MainConfiguration() static MainConfiguration()
{ {
var serverFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) Configuration = new();
? "FileTime.Server.exe"
: "FileTime.Server";
Configuration = new()
{
{AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.ServerExecutablePath), serverFileName},
};
PopulateDefaultEditorPrograms(Configuration); PopulateDefaultEditorPrograms(Configuration);
PopulateDefaultKeyBindings(Configuration, DefaultKeybindings.Value, PopulateDefaultKeyBindings(Configuration, DefaultKeybindings.Value,

View File

@@ -18,6 +18,7 @@
<ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" /> <ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />
<ProjectReference Include="..\..\Library\TerminalUI.DependencyInjection\TerminalUI.DependencyInjection.csproj" /> <ProjectReference Include="..\..\Library\TerminalUI.DependencyInjection\TerminalUI.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" /> <ProjectReference Include="..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Server\FileTime.Server\FileTime.Server.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" /> <ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" /> <ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -15,6 +15,12 @@ using TerminalUI.ConsoleDrivers;
using ITheme = FileTime.ConsoleUI.App.Styling.ITheme; using ITheme = FileTime.ConsoleUI.App.Styling.ITheme;
using Version = FileTime.ConsoleUI.InfoProviders.Version; using Version = FileTime.ConsoleUI.InfoProviders.Version;
if(args.Length > 0 && args[0] == "--server")
{
FileTime.Server.Program.Main(args.Skip(1).ToArray());
return;
}
Console.OutputEncoding = System.Text.Encoding.UTF8; Console.OutputEncoding = System.Text.Encoding.UTF8;
IConsoleDriver? driver = null; IConsoleDriver? driver = null;

View File

@@ -47,6 +47,7 @@
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Search\FileTime.App.Search.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.Search\FileTime.App.Search.csproj" />
<ProjectReference Include="..\..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" /> <ProjectReference Include="..\..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Server\FileTime.Server\FileTime.Server.csproj" />
<ProjectReference Include="..\..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" /> <ProjectReference Include="..\..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj" /> <ProjectReference Include="..\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.Font\FileTime.GuiApp.Font.csproj" /> <ProjectReference Include="..\FileTime.GuiApp.Font\FileTime.GuiApp.Font.csproj" />

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -56,6 +57,12 @@ public static class Program
[STAThread] [STAThread]
public static async Task Main(string[] args) public static async Task Main(string[] args)
{ {
if(args.Length > 0 && args[0] == "--server")
{
Server.Program.Main(args.Skip(1).ToArray());
return;
}
#if DEBUG #if DEBUG
(AppDataRoot, EnvironmentName) = Init.InitDevelopment(); (AppDataRoot, EnvironmentName) = Init.InitDevelopment();
#endif #endif

View File

@@ -3,7 +3,7 @@
public class AdminElevationConfiguration public class AdminElevationConfiguration
{ {
public const string SectionName = "AdminElevation"; public const string SectionName = "AdminElevation";
public string ServerExecutablePath { get; set; } public string? ServerExecutablePath { get; set; }
public string LinuxElevationTool { get; set; } public string LinuxElevationTool { get; set; }
public int? ServerPort { get; set; } public int? ServerPort { get; set; }
public bool? StartProcess { get; set; } public bool? StartProcess { get; set; }

View File

@@ -16,6 +16,7 @@ namespace FileTime.Providers.LocalAdmin;
public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChanged, IExitHandler public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChanged, IExitHandler
{ {
private const string AdminContentProviderName = "localAdminRemote"; private const string AdminContentProviderName = "localAdminRemote";
private class ConnectionInfo private class ConnectionInfo
{ {
public string? SignalRBaseUrl { get; init; } public string? SignalRBaseUrl { get; init; }
@@ -60,8 +61,6 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
public async Task CreateAdminInstanceIfNecessaryAsync(string? confirmationMessage = null) public async Task CreateAdminInstanceIfNecessaryAsync(string? confirmationMessage = null)
{ {
ArgumentNullException.ThrowIfNull(_configuration.CurrentValue.ServerExecutablePath, "ServerExecutablePath");
await _lock.WaitAsync(); await _lock.WaitAsync();
try try
{ {
@@ -74,11 +73,11 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
var port = _configuration.CurrentValue.ServerPort; var port = _configuration.CurrentValue.ServerPort;
_logger.LogTrace("Admin server port is {Port}", port is null ? "<not set>" : $"{port}"); _logger.LogTrace("Admin server port is {Port}", port is null ? "<not set>" : $"{port}");
if (StartProcess || port is null) if (StartProcess || port is null)
{ {
var portFileName = Path.GetTempFileName(); var portFileName = Path.GetTempFileName();
File.Delete(portFileName); File.Delete(portFileName);
var process = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) var process = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? CreateWindowsAdminProcess(portFileName) ? CreateWindowsAdminProcess(portFileName)
: CreateLinuxAdminProcess(portFileName); : CreateLinuxAdminProcess(portFileName);
process.Exited += ProcessExitHandler; process.Exited += ProcessExitHandler;
@@ -131,40 +130,74 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
} }
} }
private Process CreateWindowsAdminProcess(string portFileName) private Process CreateWindowsAdminProcess(string portFileName)
=> new() {
var (fileName, arguments) = GetServerPathAndArgs(portFileName);
var process = new Process
{ {
StartInfo = new() StartInfo = new()
{ {
FileName = _configuration.CurrentValue.ServerExecutablePath, FileName = fileName,
ArgumentList =
{
"--PortWriter:FileName",
portFileName
},
UseShellExecute = true, UseShellExecute = true,
Verb = "runas" Verb = "runas"
}, },
EnableRaisingEvents = true EnableRaisingEvents = true
}; };
foreach (var argument in arguments)
{
process.StartInfo.ArgumentList.Add(argument);
}
return process;
}
private Process CreateLinuxAdminProcess(string portFileName) private Process CreateLinuxAdminProcess(string portFileName)
{ {
return new Process var (fileName, arguments) = GetServerPathAndArgs(portFileName);
arguments = arguments.Prepend(fileName);
var process = new Process
{ {
StartInfo = new() StartInfo = new()
{ {
FileName = _configuration.CurrentValue.LinuxElevationTool, FileName = _configuration.CurrentValue.LinuxElevationTool,
ArgumentList =
{
_configuration.CurrentValue.ServerExecutablePath,
"--PortWriter:FileName",
portFileName
},
CreateNoWindow = true CreateNoWindow = true
}, },
EnableRaisingEvents = true EnableRaisingEvents = true
}; };
foreach (var argument in arguments)
{
process.StartInfo.ArgumentList.Add(argument);
}
return process;
}
private (string fileName, IEnumerable<string> arguments) GetServerPathAndArgs(string portFileName)
{
var selfStart = _configuration.CurrentValue.ServerExecutablePath is null;
var fileName = selfStart
? Process.GetCurrentProcess().MainModule?.FileName
: _configuration.CurrentValue.ServerExecutablePath;
if(fileName is null) throw new Exception("Could not get server executable path");
IEnumerable<string> arguments = new[]
{
"--PortWriter:FileName",
portFileName
};
if (selfStart)
{
arguments = arguments.Prepend("--server");
}
return (fileName, arguments);
} }
//Note: this does not have to return a task //Note: this does not have to return a task
@@ -172,7 +205,7 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
{ {
try try
{ {
if (_remoteContentProvider != null) return Task.FromResult((IRemoteContentProvider)_remoteContentProvider); if (_remoteContentProvider != null) return Task.FromResult((IRemoteContentProvider) _remoteContentProvider);
ArgumentNullException.ThrowIfNull(_connectionInfo); ArgumentNullException.ThrowIfNull(_connectionInfo);
//TODO: use other connections too (if there will be any) //TODO: use other connections too (if there will be any)
@@ -186,7 +219,7 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
AdminContentProviderName AdminContentProviderName
); );
return Task.FromResult((IRemoteContentProvider)_remoteContentProvider); return Task.FromResult((IRemoteContentProvider) _remoteContentProvider);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -2,83 +2,89 @@
using Autofac.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection;
using FileTime.App.DependencyInjection; using FileTime.App.DependencyInjection;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using FileTime.Server;
using FileTime.Server.App; using FileTime.Server.App;
using FileTime.Server.Common; using FileTime.Server.Common;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Serilog; using Serilog;
namespace FileTime.Server;
var applicationCancellation = new CancellationTokenSource(); public static class Program
var configurationRoot = CreateConfiguration(); {
public static void Main(string[] args)
{
var applicationCancellation = new CancellationTokenSource();
var configurationRoot = CreateConfiguration();
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
#if DEBUG #if DEBUG
.MinimumLevel.Verbose() .MinimumLevel.Verbose()
.ReadFrom.Configuration(configurationRoot) .ReadFrom.Configuration(configurationRoot)
#else #else
.MinimumLevel.Information() .MinimumLevel.Information()
#endif #endif
.WriteTo.Console() .WriteTo.Console()
.CreateLogger(); .CreateLogger();
var rootContainer = CreateRootDiContainer(configurationRoot); var rootContainer = CreateRootDiContainer(configurationRoot);
var handlerParameters = new ConnectionHandlerParameters( var handlerParameters = new ConnectionHandlerParameters(
args, args,
rootContainer, rootContainer,
configurationRoot, configurationRoot,
applicationCancellation.Token applicationCancellation.Token
); );
var webThread = CreateStartup(FileTime.Server.Web.Program.Start); var webThread = CreateStartup(FileTime.Server.Web.Program.Start);
webThread.Start(); webThread.Start();
Thread CreateStartup(Func<ConnectionHandlerParameters, Task> startup) Thread CreateStartup(Func<ConnectionHandlerParameters, Task> startup)
{ {
var thread = new Thread(() => { HandleStartup(() => startup(handlerParameters).Wait()); }); var thread = new Thread(() => { HandleStartup(() => startup(handlerParameters).Wait()); });
return thread; return thread;
} }
void HandleStartup(Action action) void HandleStartup(Action action)
{ {
try try
{ {
action(); action();
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e); Console.WriteLine(e);
throw; throw;
} }
} }
IConfigurationRoot CreateConfiguration() IConfigurationRoot CreateConfiguration()
{ {
var configurationBuilder = new ConfigurationBuilder(); var configurationBuilder = new ConfigurationBuilder();
#if DEBUG #if DEBUG
configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true); configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true);
configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true); configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true);
#endif #endif
configurationBuilder.AddCommandLine(args); configurationBuilder.AddCommandLine(args);
return configurationBuilder.Build(); return configurationBuilder.Build();
} }
IContainer CreateRootDiContainer(IConfigurationRoot configuration) IContainer CreateRootDiContainer(IConfigurationRoot configuration)
{ {
var serviceCollection = DependencyInjection var serviceCollection = DependencyInjection
.RegisterDefaultServices(configuration) .RegisterDefaultServices(configuration)
.AddLocalProviderServices() .AddLocalProviderServices()
.AddServerServices() .AddServerServices()
.AddServerCoreServices() .AddServerCoreServices()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog()); .AddLogging(loggingBuilder => loggingBuilder.AddSerilog());
serviceCollection.AddSingleton<IApplicationStopper>( serviceCollection.AddSingleton<IApplicationStopper>(
new ApplicationStopper(() => applicationCancellation.Cancel()) new ApplicationStopper(() => applicationCancellation.Cancel())
); );
var containerBuilder = new ContainerBuilder(); var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(serviceCollection); containerBuilder.Populate(serviceCollection);
return containerBuilder.Build(); return containerBuilder.Build();
}
}
} }