1
0
mirror of https://github.com/chylex/Discord-History-Tracker.git synced 2024-10-18 20:42:51 +02:00

Compare commits

...

1 Commits

Author SHA1 Message Date
2875f5943b
Refactor integrated server management 2023-12-27 11:44:28 +01:00
13 changed files with 229 additions and 294 deletions

View File

@ -1,14 +1,19 @@
using System; using System;
using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading;
using DHT.Desktop.Dialogs.Message; using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Server; using DHT.Desktop.Server;
using DHT.Server; using DHT.Server;
using DHT.Server.Service; using DHT.Server.Service;
using DHT.Utils.Logging;
using DHT.Utils.Models; using DHT.Utils.Models;
namespace DHT.Desktop.Main.Controls; namespace DHT.Desktop.Main.Controls;
sealed class ServerConfigurationPanelModel : BaseModel, IDisposable { sealed class ServerConfigurationPanelModel : BaseModel, IDisposable {
private static readonly Log Log = Log.ForType<ServerConfigurationPanelModel>();
private string inputPort; private string inputPort;
public string InputPort { public string InputPort {
@ -29,7 +34,7 @@ sealed class ServerConfigurationPanelModel : BaseModel, IDisposable {
} }
} }
public bool HasMadeChanges => ServerManager.Port.ToString() != InputPort || ServerManager.Token != InputToken; public bool HasMadeChanges => ServerConfiguration.Port.ToString() != InputPort || ServerConfiguration.Token != InputToken;
private bool isToggleServerButtonEnabled = true; private bool isToggleServerButtonEnabled = true;
@ -38,59 +43,69 @@ sealed class ServerConfigurationPanelModel : BaseModel, IDisposable {
set => Change(ref isToggleServerButtonEnabled, value); set => Change(ref isToggleServerButtonEnabled, value);
} }
public string ToggleServerButtonText => serverManager.IsRunning ? "Stop Server" : "Start Server"; public string ToggleServerButtonText => server.IsRunning ? "Stop Server" : "Start Server";
public event EventHandler<StatusBarModel.Status>? ServerStatusChanged;
private readonly Window window; private readonly Window window;
private readonly ServerManager serverManager; private readonly ServerManager server;
[Obsolete("Designer")] [Obsolete("Designer")]
public ServerConfigurationPanelModel() : this(null!, new ServerManager(State.Dummy)) {} public ServerConfigurationPanelModel() : this(null!, State.Dummy) {}
public ServerConfigurationPanelModel(Window window, ServerManager serverManager) { public ServerConfigurationPanelModel(Window window, State state) {
this.window = window; this.window = window;
this.serverManager = serverManager; this.server = state.Server;
this.inputPort = ServerManager.Port.ToString(); this.inputPort = ServerConfiguration.Port.ToString();
this.inputToken = ServerManager.Token; this.inputToken = ServerConfiguration.Token;
}
server.StatusChanged += OnServerStatusChanged;
public void Initialize() {
ServerLauncher.ServerStatusChanged += ServerLauncherOnServerStatusChanged;
} }
public void Dispose() { public void Dispose() {
ServerLauncher.ServerStatusChanged -= ServerLauncherOnServerStatusChanged; server.StatusChanged -= OnServerStatusChanged;
} }
private void ServerLauncherOnServerStatusChanged(object? sender, EventArgs e) { private void OnServerStatusChanged(object? sender, ServerManager.Status e) {
ServerStatusChanged?.Invoke(this, serverManager.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped); Dispatcher.UIThread.InvokeAsync(UpdateServerStatus);
}
private void UpdateServerStatus() {
OnPropertyChanged(nameof(ToggleServerButtonText)); OnPropertyChanged(nameof(ToggleServerButtonText));
}
private async Task StartServer() {
IsToggleServerButtonEnabled = false;
try {
await server.Start(ServerConfiguration.Port, ServerConfiguration.Token);
} catch (Exception e) {
Log.Error(e);
await Dialog.ShowOk(window, "Internal Server Error", e.Message);
}
UpdateServerStatus();
IsToggleServerButtonEnabled = true; IsToggleServerButtonEnabled = true;
} }
private void BeforeServerStart() { private async Task StopServer() {
IsToggleServerButtonEnabled = false; IsToggleServerButtonEnabled = false;
ServerStatusChanged?.Invoke(this, StatusBarModel.Status.Starting);
try {
await server.Stop();
} catch (Exception e) {
Log.Error(e);
await Dialog.ShowOk(window, "Internal Server Error", e.Message);
}
UpdateServerStatus();
IsToggleServerButtonEnabled = true;
} }
private void StartServer() { public async Task OnClickToggleServerButton() {
BeforeServerStart(); if (server.IsRunning) {
serverManager.Launch(); await StopServer();
}
private void StopServer() {
IsToggleServerButtonEnabled = false;
ServerStatusChanged?.Invoke(this, StatusBarModel.Status.Stopping);
serverManager.Stop();
}
public void OnClickToggleServerButton() {
if (serverManager.IsRunning) {
StopServer();
} }
else { else {
StartServer(); await StartServer();
} }
} }
@ -98,19 +113,21 @@ sealed class ServerConfigurationPanelModel : BaseModel, IDisposable {
InputToken = ServerUtils.GenerateRandomToken(20); InputToken = ServerUtils.GenerateRandomToken(20);
} }
public async void OnClickApplyChanges() { public async Task OnClickApplyChanges() {
if (!ushort.TryParse(InputPort, out ushort port)) { if (!ushort.TryParse(InputPort, out ushort port)) {
await Dialog.ShowOk(window, "Invalid Port", "Port must be a number between 0 and 65535."); await Dialog.ShowOk(window, "Invalid Port", "Port must be a number between 0 and 65535.");
return; return;
} }
BeforeServerStart(); ServerConfiguration.Port = port;
serverManager.Relaunch(port, InputToken); ServerConfiguration.Token = inputToken;
await StartServer();
OnPropertyChanged(nameof(HasMadeChanges)); OnPropertyChanged(nameof(HasMadeChanges));
} }
public void OnClickCancelChanges() { public void OnClickCancelChanges() {
InputPort = ServerManager.Port.ToString(); InputPort = ServerConfiguration.Port.ToString();
InputToken = ServerManager.Token; InputToken = ServerConfiguration.Token;
} }
} }

View File

@ -40,7 +40,7 @@
<StackPanel Orientation="Horizontal" Margin="6 3"> <StackPanel Orientation="Horizontal" Margin="6 3">
<StackPanel Orientation="Vertical" Width="65"> <StackPanel Orientation="Vertical" Width="65">
<TextBlock Classes="label">Status</TextBlock> <TextBlock Classes="label">Status</TextBlock>
<TextBlock FontSize="12" Margin="0 3 0 0" Text="{Binding StatusText}" /> <TextBlock FontSize="12" Margin="0 3 0 0" Text="{Binding ServerStatusText}" />
</StackPanel> </StackPanel>
<Rectangle /> <Rectangle />
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">

View File

@ -1,45 +1,46 @@
using System; using System;
using Avalonia.Threading;
using DHT.Server;
using DHT.Server.Database; using DHT.Server.Database;
using DHT.Server.Service;
using DHT.Utils.Models; using DHT.Utils.Models;
namespace DHT.Desktop.Main.Controls; namespace DHT.Desktop.Main.Controls;
sealed class StatusBarModel : BaseModel { sealed class StatusBarModel : BaseModel, IDisposable {
public DatabaseStatistics DatabaseStatistics { get; } public DatabaseStatistics DatabaseStatistics { get; }
private Status status = Status.Stopped; private ServerManager.Status serverStatus;
public Status CurrentStatus { public string ServerStatusText => serverStatus switch {
get => status; ServerManager.Status.Starting => "STARTING",
set { ServerManager.Status.Started => "READY",
status = value; ServerManager.Status.Stopping => "STOPPING",
OnPropertyChanged(nameof(StatusText)); ServerManager.Status.Stopped => "STOPPED",
} _ => ""
} };
public string StatusText { private readonly State state;
get {
return CurrentStatus switch {
Status.Starting => "STARTING",
Status.Ready => "READY",
Status.Stopping => "STOPPING",
Status.Stopped => "STOPPED",
_ => ""
};
}
}
[Obsolete("Designer")] [Obsolete("Designer")]
public StatusBarModel() : this(new DatabaseStatistics()) {} public StatusBarModel() : this(State.Dummy) {}
public StatusBarModel(DatabaseStatistics databaseStatistics) { public StatusBarModel(State state) {
this.DatabaseStatistics = databaseStatistics; this.state = state;
this.DatabaseStatistics = state.Db.Statistics;
state.Server.StatusChanged += OnServerStatusChanged;
serverStatus = state.Server.IsRunning ? ServerManager.Status.Started : ServerManager.Status.Stopped;
} }
public enum Status { public void Dispose() {
Starting, state.Server.StatusChanged += OnServerStatusChanged;
Ready, }
Stopping,
Stopped private void OnServerStatusChanged(object? sender, ServerManager.Status e) {
Dispatcher.UIThread.InvokeAsync(() => {
serverStatus = e;
OnPropertyChanged(nameof(ServerStatusText));
});
} }
} }

View File

@ -8,12 +8,15 @@ using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Screens; using DHT.Desktop.Main.Screens;
using DHT.Desktop.Server; using DHT.Desktop.Server;
using DHT.Server; using DHT.Server;
using DHT.Utils.Logging;
using DHT.Utils.Models; using DHT.Utils.Models;
namespace DHT.Desktop.Main; namespace DHT.Desktop.Main;
sealed class MainWindowModel : BaseModel, IAsyncDisposable { sealed class MainWindowModel : BaseModel, IAsyncDisposable {
private const string DefaultTitle = "Discord History Tracker"; private const string DefaultTitle = "Discord History Tracker";
private static readonly Log Log = Log.ForType<MainWindowModel>();
public string Title { get; private set; } = DefaultTitle; public string Title { get; private set; } = DefaultTitle;
@ -63,11 +66,11 @@ sealed class MainWindowModel : BaseModel, IAsyncDisposable {
} }
if (args.ServerPort != null) { if (args.ServerPort != null) {
ServerManager.Port = args.ServerPort.Value; ServerConfiguration.Port = args.ServerPort.Value;
} }
if (args.ServerToken != null) { if (args.ServerToken != null) {
ServerManager.Token = args.ServerToken; ServerConfiguration.Token = args.ServerToken;
} }
} }
@ -82,15 +85,23 @@ sealed class MainWindowModel : BaseModel, IAsyncDisposable {
await state.DisposeAsync(); await state.DisposeAsync();
} }
state = welcomeScreenModel.Db == null ? null : new State(welcomeScreenModel.Db); if (welcomeScreenModel.Db == null) {
state = null;
if (state == null) {
Title = DefaultTitle; Title = DefaultTitle;
mainContentScreenModel = null; mainContentScreenModel = null;
mainContentScreen = null; mainContentScreen = null;
CurrentScreen = welcomeScreen; CurrentScreen = welcomeScreen;
} }
else { else {
state = new State(welcomeScreenModel.Db);
try {
await state.Server.Start(ServerConfiguration.Port, ServerConfiguration.Token);
} catch (Exception ex) {
Log.Error(ex);
await Dialog.ShowOk(window, "Internal Server Error", ex.Message);
}
Title = Path.GetFileName(state.Db.Path) + " - " + DefaultTitle; Title = Path.GetFileName(state.Db.Path) + " - " + DefaultTitle;
mainContentScreenModel = new MainContentScreenModel(window, state); mainContentScreenModel = new MainContentScreenModel(window, state);
await mainContentScreenModel.Initialize(); await mainContentScreenModel.Initialize();

View File

@ -2,7 +2,6 @@ using System;
using Avalonia.Controls; using Avalonia.Controls;
using DHT.Desktop.Dialogs.Message; using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Controls; using DHT.Desktop.Main.Controls;
using DHT.Desktop.Server;
using DHT.Server; using DHT.Server;
using DHT.Utils.Models; using DHT.Utils.Models;
@ -15,17 +14,13 @@ sealed class AdvancedPageModel : BaseModel, IDisposable {
private readonly State state; private readonly State state;
[Obsolete("Designer")] [Obsolete("Designer")]
public AdvancedPageModel() : this(null!, State.Dummy, new ServerManager(State.Dummy)) {} public AdvancedPageModel() : this(null!, State.Dummy) {}
public AdvancedPageModel(Window window, State state, ServerManager serverManager) { public AdvancedPageModel(Window window, State state) {
this.window = window; this.window = window;
this.state = state; this.state = state;
ServerConfigurationModel = new ServerConfigurationPanelModel(window, serverManager); ServerConfigurationModel = new ServerConfigurationPanelModel(window, state);
}
public void Initialize() {
ServerConfigurationModel.Initialize();
} }
public void Dispose() { public void Dispose() {

View File

@ -54,7 +54,7 @@ sealed class TrackingPageModel : BaseModel {
} }
public async Task<bool> OnClickCopyTrackingScript() { public async Task<bool> OnClickCopyTrackingScript() {
string url = $"http://127.0.0.1:{ServerManager.Port}/get-tracking-script?token={HttpUtility.UrlEncode(ServerManager.Token)}"; string url = $"http://127.0.0.1:{ServerConfiguration.Port}/get-tracking-script?token={HttpUtility.UrlEncode(ServerConfiguration.Token)}";
string script = (await Resources.ReadTextAsync("tracker-loader.js")).Trim().Replace("{url}", url); string script = (await Resources.ReadTextAsync("tracker-loader.js")).Trim().Replace("{url}", url);
var clipboard = window.Clipboard; var clipboard = window.Clipboard;

View File

@ -110,7 +110,7 @@ sealed class ViewerPageModel : BaseModel, IDisposable {
TemporaryFiles.Add(fullPath); TemporaryFiles.Add(fullPath);
Directory.CreateDirectory(rootPath); Directory.CreateDirectory(rootPath);
await WriteViewerFile(fullPath, new LiveViewerExportStrategy(ServerManager.Port, ServerManager.Token)); await WriteViewerFile(fullPath, new LiveViewerExportStrategy(ServerConfiguration.Port, ServerConfiguration.Token));
Process.Start(new ProcessStartInfo(fullPath) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(fullPath) { UseShellExecute = true });
} }

View File

@ -1,12 +1,9 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Controls; using DHT.Desktop.Main.Controls;
using DHT.Desktop.Main.Pages; using DHT.Desktop.Main.Pages;
using DHT.Desktop.Server;
using DHT.Server; using DHT.Server;
using DHT.Server.Service;
using DHT.Utils.Logging; using DHT.Utils.Logging;
namespace DHT.Desktop.Main.Screens; namespace DHT.Desktop.Main.Screens;
@ -49,18 +46,10 @@ sealed class MainContentScreenModel : IDisposable {
} }
} }
private readonly Window window;
private readonly ServerManager serverManager;
[Obsolete("Designer")] [Obsolete("Designer")]
public MainContentScreenModel() : this(null!, State.Dummy) {} public MainContentScreenModel() : this(null!, State.Dummy) {}
public MainContentScreenModel(Window window, State state) { public MainContentScreenModel(Window window, State state) {
this.window = window;
this.serverManager = new ServerManager(state);
ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught;
DatabasePageModel = new DatabasePageModel(window, state); DatabasePageModel = new DatabasePageModel(window, state);
DatabasePage = new DatabasePage { DataContext = DatabasePageModel }; DatabasePage = new DatabasePage { DataContext = DatabasePageModel };
@ -73,7 +62,7 @@ sealed class MainContentScreenModel : IDisposable {
ViewerPageModel = new ViewerPageModel(window, state); ViewerPageModel = new ViewerPageModel(window, state);
ViewerPage = new ViewerPage { DataContext = ViewerPageModel }; ViewerPage = new ViewerPage { DataContext = ViewerPageModel };
AdvancedPageModel = new AdvancedPageModel(window, state, serverManager); AdvancedPageModel = new AdvancedPageModel(window, state);
AdvancedPage = new AdvancedPage { DataContext = AdvancedPageModel }; AdvancedPage = new AdvancedPage { DataContext = AdvancedPageModel };
#if DEBUG #if DEBUG
@ -83,37 +72,17 @@ sealed class MainContentScreenModel : IDisposable {
DebugPage = null; DebugPage = null;
#endif #endif
StatusBarModel = new StatusBarModel(state.Db.Statistics); StatusBarModel = new StatusBarModel(state);
AdvancedPageModel.ServerConfigurationModel.ServerStatusChanged += OnServerStatusChanged;
DatabaseClosed += OnDatabaseClosed;
StatusBarModel.CurrentStatus = serverManager.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped;
} }
public async Task Initialize() { public async Task Initialize() {
await TrackingPageModel.Initialize(); await TrackingPageModel.Initialize();
AdvancedPageModel.Initialize();
serverManager.Launch();
} }
public void Dispose() { public void Dispose() {
ServerLauncher.ServerManagementExceptionCaught -= ServerLauncherOnServerManagementExceptionCaught;
AttachmentsPageModel.Dispose(); AttachmentsPageModel.Dispose();
ViewerPageModel.Dispose(); ViewerPageModel.Dispose();
serverManager.Dispose(); AdvancedPageModel.Dispose();
} StatusBarModel.Dispose();
private void OnServerStatusChanged(object? sender, StatusBarModel.Status e) {
StatusBarModel.CurrentStatus = e;
}
private void OnDatabaseClosed(object? sender, EventArgs e) {
serverManager.Stop();
}
private async void ServerLauncherOnServerManagementExceptionCaught(object? sender, Exception ex) {
Log.Error(ex);
await Dialog.ShowOk(window, "Internal Server Error", ex.Message);
} }
} }

View File

@ -0,0 +1,8 @@
using DHT.Server.Service;
namespace DHT.Desktop.Server;
static class ServerConfiguration {
public static ushort Port { get; set; } = ServerUtils.FindAvailablePort(50000, 60000);
public static string Token { get; set; } = ServerUtils.GenerateRandomToken(20);
}

View File

@ -1,50 +0,0 @@
using System;
using DHT.Server;
using DHT.Server.Service;
namespace DHT.Desktop.Server;
sealed class ServerManager : IDisposable {
public static ushort Port { get; set; } = ServerUtils.FindAvailablePort(50000, 60000);
public static string Token { get; set; } = ServerUtils.GenerateRandomToken(20);
private static ServerManager? instance;
public bool IsRunning => ServerLauncher.IsRunning;
private readonly State state;
public ServerManager(State state) {
if (state != State.Dummy) {
if (instance != null) {
throw new InvalidOperationException("Only one instance of ServerManager can exist at the same time!");
}
instance = this;
}
this.state = state;
}
public void Launch() {
ServerLauncher.Relaunch(Port, Token, state.Db);
}
public void Relaunch(ushort port, string token) {
Port = port;
Token = token;
Launch();
}
public void Stop() {
ServerLauncher.Stop();
}
public void Dispose() {
Stop();
if (instance == this) {
instance = null;
}
}
}

View File

@ -1,126 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using DHT.Server.Database;
using DHT.Utils.Logging;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
namespace DHT.Server.Service;
public static class ServerLauncher {
private static readonly Log Log = Log.ForType(typeof(ServerLauncher));
private static IWebHost? Server { get; set; } = null;
public static bool IsRunning { get; private set; }
public static event EventHandler? ServerStatusChanged;
public static event EventHandler<Exception>? ServerManagementExceptionCaught;
private static Thread? ManagementThread { get; set; } = null;
private static readonly Mutex ManagementThreadLock = new();
private static readonly BlockingCollection<IMessage> Messages = new(new ConcurrentQueue<IMessage>());
private static void EnqueueMessage(IMessage message) {
ManagementThreadLock.WaitOne();
try {
if (ManagementThread == null) {
ManagementThread = new Thread(RunManagementThread) {
Name = "DHT server management thread",
IsBackground = true
};
ManagementThread.Start();
}
Messages.Add(message);
} finally {
ManagementThreadLock.ReleaseMutex();
}
}
[SuppressMessage("ReSharper", "FunctionNeverReturns")]
private static void RunManagementThread() {
foreach (IMessage message in Messages.GetConsumingEnumerable()) {
try {
switch (message) {
case IMessage.StartServer start:
StopServerFromManagementThread();
StartServerFromManagementThread(start.Port, start.Token, start.Db);
break;
case IMessage.StopServer:
StopServerFromManagementThread();
break;
}
} catch (Exception e) {
ServerManagementExceptionCaught?.Invoke(null, e);
}
}
}
private static void StartServerFromManagementThread(ushort port, string token, IDatabaseFile db) {
Log.Info("Starting server on port " + port + "...");
void AddServices(IServiceCollection services) {
services.AddSingleton(typeof(IDatabaseFile), db);
services.AddSingleton(typeof(ServerParameters), new ServerParameters(port, token));
}
void SetKestrelOptions(KestrelServerOptions options) {
options.Limits.MaxRequestBodySize = null;
options.Limits.MinResponseDataRate = null;
options.ListenLocalhost(port, static listenOptions => listenOptions.Protocols = HttpProtocols.Http1);
}
Server = new WebHostBuilder()
.ConfigureServices(AddServices)
.UseKestrel(SetKestrelOptions)
.UseStartup<Startup>()
.Build();
Server.Start();
Log.Info("Server started");
IsRunning = true;
ServerStatusChanged?.Invoke(null, EventArgs.Empty);
}
private static void StopServerFromManagementThread() {
if (Server != null) {
Log.Info("Stopping server...");
Server.StopAsync().Wait();
Server.Dispose();
Server = null;
Log.Info("Server stopped");
IsRunning = false;
ServerStatusChanged?.Invoke(null, EventArgs.Empty);
}
}
public static void Relaunch(ushort port, string token, IDatabaseFile db) {
EnqueueMessage(new IMessage.StartServer(port, token, db));
}
public static void Stop() {
EnqueueMessage(new IMessage.StopServer());
}
private interface IMessage {
public sealed class StartServer : IMessage {
public ushort Port { get; }
public string Token { get; }
public IDatabaseFile Db { get; }
public StartServer(ushort port, string token, IDatabaseFile db) {
this.Port = port;
this.Token = token;
this.Db = db;
}
}
public sealed class StopServer : IMessage {}
}
}

View File

@ -0,0 +1,107 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using DHT.Server.Database;
using DHT.Utils.Logging;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
namespace DHT.Server.Service;
public sealed class ServerManager {
private static readonly Log Log = Log.ForType(typeof(ServerManager));
private IWebHost? server;
public bool IsRunning => server != null;
public event EventHandler<Status>? StatusChanged;
public enum Status {
Starting,
Started,
Stopping,
Stopped
}
private readonly IDatabaseFile db;
private readonly SemaphoreSlim semaphore = new (1, 1);
internal ServerManager(IDatabaseFile db) {
this.db = db;
}
public async Task Start(ushort port, string token) {
await semaphore.WaitAsync();
try {
await StartInternal(port, token);
} finally {
semaphore.Release();
}
}
public async Task Stop() {
await semaphore.WaitAsync();
try {
await StopInternal();
} finally {
semaphore.Release();
}
}
private async Task StartInternal(ushort port, string token) {
await StopInternal();
StatusChanged?.Invoke(this, Status.Starting);
void AddServices(IServiceCollection services) {
services.AddSingleton(typeof(IDatabaseFile), db);
services.AddSingleton(typeof(ServerParameters), new ServerParameters(port, token));
}
void SetKestrelOptions(KestrelServerOptions options) {
options.Limits.MaxRequestBodySize = null;
options.Limits.MinResponseDataRate = null;
options.ListenLocalhost(port, static listenOptions => listenOptions.Protocols = HttpProtocols.Http1);
}
var newServer = new WebHostBuilder()
.ConfigureServices(AddServices)
.UseKestrel(SetKestrelOptions)
.UseStartup<Startup>()
.Build();
Log.Info("Starting server on port " + port + "...");
try {
await newServer.StartAsync();
} catch (Exception) {
Log.Error("Server could not start");
StatusChanged?.Invoke(this, Status.Stopped);
throw;
}
Log.Info("Server started");
server = newServer;
StatusChanged?.Invoke(this, Status.Started);
}
private async Task StopInternal() {
if (server == null) {
return;
}
StatusChanged?.Invoke(this, Status.Stopping);
Log.Info("Stopping server...");
await server.StopAsync();
Log.Info("Server stopped");
server.Dispose();
server = null;
StatusChanged?.Invoke(this, Status.Stopped);
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using DHT.Server.Database; using DHT.Server.Database;
using DHT.Server.Download; using DHT.Server.Download;
using DHT.Server.Service;
namespace DHT.Server; namespace DHT.Server;
@ -10,10 +11,12 @@ public sealed class State : IAsyncDisposable {
public IDatabaseFile Db { get; } public IDatabaseFile Db { get; }
public Downloader Downloader { get; } public Downloader Downloader { get; }
public ServerManager Server { get; }
public State(IDatabaseFile db) { public State(IDatabaseFile db) {
Db = db; Db = db;
Downloader = new Downloader(db); Downloader = new Downloader(db);
Server = new ServerManager(db);
} }
public async ValueTask DisposeAsync() { public async ValueTask DisposeAsync() {