blob: fc0680e40fabbbe41349b1a9b8239564d62c0691 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Networking.Manager;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using SQLitePCL;
namespace Jellyfin.Server.ServerSetupApp;
/// <summary>
/// Creates a fake application pipeline that will only exist for as long as the main app is not started.
/// </summary>
public sealed class SetupServer : IDisposable
{
private IHost? _startupServer;
private bool _disposed;
/// <summary>
/// Starts the Bind-All Setup aspcore server to provide a reflection on the current core setup.
/// </summary>
/// <param name="networkManagerFactory">The networkmanager.</param>
/// <param name="applicationPaths">The application paths.</param>
/// <returns>A Task.</returns>
public async Task RunAsync(Func<INetworkManager?> networkManagerFactory, IApplicationPaths applicationPaths)
{
ThrowIfDisposed();
_startupServer = Host.CreateDefaultBuilder()
.UseConsoleLifetime()
.ConfigureServices(serv =>
{
serv.AddHealthChecks()
.AddCheck<SetupHealthcheck>("StartupCheck");
})
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder
.UseKestrel()
.Configure(app =>
{
app.UseHealthChecks("/health");
app.Map("/startup/logger", loggerRoute =>
{
loggerRoute.Run(async context =>
{
var networkManager = networkManagerFactory();
if (context.Connection.RemoteIpAddress is null || networkManager is null || !networkManager.IsInLocalNetwork(context.Connection.RemoteIpAddress))
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return;
}
var logfilePath = Directory.EnumerateFiles(applicationPaths.LogDirectoryPath).Select(e => new FileInfo(e)).OrderBy(f => f.CreationTimeUtc).FirstOrDefault()?.FullName;
if (logfilePath is not null)
{
await context.Response.SendFileAsync(logfilePath, CancellationToken.None).ConfigureAwait(false);
}
});
});
app.Run((context) =>
{
context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
context.Response.Headers.RetryAfter = new Microsoft.Extensions.Primitives.StringValues("60");
context.Response.WriteAsync("<p>Jellyfin Server still starting. Please wait.</p>");
var networkManager = networkManagerFactory();
if (networkManager is not null && context.Connection.RemoteIpAddress is not null && networkManager.IsInLocalNetwork(context.Connection.RemoteIpAddress))
{
context.Response.WriteAsync("<p>You can download the current logfiles <a href='/startup/logger'>here</a>.</p>");
}
return Task.CompletedTask;
});
});
})
.Build();
await _startupServer.StartAsync().ConfigureAwait(false);
}
/// <summary>
/// Stops the Setup server.
/// </summary>
/// <returns>A task. Duh.</returns>
public async Task StopAsync()
{
ThrowIfDisposed();
if (_startupServer is null)
{
throw new InvalidOperationException("Tried to stop a non existing startup server");
}
await _startupServer.StopAsync().ConfigureAwait(false);
}
/// <inheritdoc/>
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
_startupServer?.Dispose();
}
private void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
private class SetupHealthcheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
return Task.FromResult(HealthCheckResult.Degraded("Server is still starting up."));
}
}
}
|