diff options
| author | crobibero <cody@robibe.ro> | 2020-09-02 08:03:15 -0600 |
|---|---|---|
| committer | crobibero <cody@robibe.ro> | 2020-09-02 08:03:15 -0600 |
| commit | 1feee6f95e00cf579ab16c7ca004947534545d9b (patch) | |
| tree | b3d0e63ed2f163317894c1353b631979bbafb815 | |
| parent | 0b38ac9a8a141149dfe5531c4f96b4d462939aaa (diff) | |
Properly host static files and set base url
| -rw-r--r-- | Jellyfin.Api/Controllers/DashboardController.cs | 102 | ||||
| -rw-r--r-- | Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs | 21 | ||||
| -rw-r--r-- | Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 6 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 2 | ||||
| -rw-r--r-- | Jellyfin.Server/Startup.cs | 21 | ||||
| -rw-r--r-- | MediaBrowser.Common/Configuration/IApplicationPaths.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Model/Configuration/ServerConfiguration.cs | 6 |
7 files changed, 27 insertions, 136 deletions
diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs index 33abe3ccd..3f0fc2e91 100644 --- a/Jellyfin.Api/Controllers/DashboardController.cs +++ b/Jellyfin.Api/Controllers/DashboardController.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Jellyfin.Api.Controllers { @@ -26,39 +27,21 @@ namespace Jellyfin.Api.Controllers { private readonly ILogger<DashboardController> _logger; private readonly IServerApplicationHost _appHost; - private readonly IConfiguration _appConfig; - private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly IResourceFileManager _resourceFileManager; /// <summary> /// Initializes a new instance of the <see cref="DashboardController"/> class. /// </summary> /// <param name="logger">Instance of <see cref="ILogger{DashboardController}"/> interface.</param> /// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param> - /// <param name="appConfig">Instance of <see cref="IConfiguration"/> interface.</param> - /// <param name="resourceFileManager">Instance of <see cref="IResourceFileManager"/> interface.</param> - /// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param> public DashboardController( ILogger<DashboardController> logger, - IServerApplicationHost appHost, - IConfiguration appConfig, - IResourceFileManager resourceFileManager, - IServerConfigurationManager serverConfigurationManager) + IServerApplicationHost appHost) { _logger = logger; _appHost = appHost; - _appConfig = appConfig; - _resourceFileManager = resourceFileManager; - _serverConfigurationManager = serverConfigurationManager; } /// <summary> - /// Gets the path of the directory containing the static web interface content, or null if the server is not - /// hosting the web client. - /// </summary> - private string? WebClientUiPath => GetWebClientUiPath(_appConfig, _serverConfigurationManager); - - /// <summary> /// Gets the configuration pages. /// </summary> /// <param name="enableInMainMenu">Whether to enable in the main menu.</param> @@ -169,87 +152,6 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - /// <summary> - /// Gets the robots.txt. - /// </summary> - /// <response code="200">Robots.txt returned.</response> - /// <returns>The robots.txt.</returns> - [HttpGet("robots.txt")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ApiExplorerSettings(IgnoreApi = true)] - public ActionResult GetRobotsTxt() - { - return GetWebClientResource("robots.txt"); - } - - /// <summary> - /// Gets a resource from the web client. - /// </summary> - /// <param name="resourceName">The resource name.</param> - /// <response code="200">Web client returned.</response> - /// <response code="404">Server does not host a web client.</response> - /// <returns>The resource.</returns> - [HttpGet("web/{*resourceName}")] - [ApiExplorerSettings(IgnoreApi = true)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetWebClientResource([FromRoute] string resourceName) - { - if (!_appConfig.HostWebClient() || WebClientUiPath == null) - { - return NotFound("Server does not host a web client."); - } - - var path = resourceName; - var basePath = WebClientUiPath; - - var requestPathAndQuery = Request.GetEncodedPathAndQuery(); - // Bounce them to the startup wizard if it hasn't been completed yet - if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted - && !requestPathAndQuery.Contains("wizard", StringComparison.OrdinalIgnoreCase) - && requestPathAndQuery.Contains("index", StringComparison.OrdinalIgnoreCase)) - { - return Redirect("index.html?start=wizard#!/wizardstart.html"); - } - - var stream = new FileStream(_resourceFileManager.GetResourcePath(basePath, path), FileMode.Open, FileAccess.Read); - return File(stream, MimeTypes.GetMimeType(path)); - } - - /// <summary> - /// Gets the favicon. - /// </summary> - /// <response code="200">Favicon.ico returned.</response> - /// <returns>The favicon.</returns> - [HttpGet("favicon.ico")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ApiExplorerSettings(IgnoreApi = true)] - public ActionResult GetFavIcon() - { - return GetWebClientResource("favicon.ico"); - } - - /// <summary> - /// Gets the path of the directory containing the static web interface content. - /// </summary> - /// <param name="appConfig">The app configuration.</param> - /// <param name="serverConfigManager">The server configuration manager.</param> - /// <returns>The directory path, or null if the server is not hosting the web client.</returns> - public static string? GetWebClientUiPath(IConfiguration appConfig, IServerConfigurationManager serverConfigManager) - { - if (!appConfig.HostWebClient()) - { - return null; - } - - if (!string.IsNullOrEmpty(serverConfigManager.Configuration.DashboardSourcePath)) - { - return serverConfigManager.Configuration.DashboardSourcePath; - } - - return serverConfigManager.ApplicationPaths.WebPath; - } - private IEnumerable<ConfigurationPageInfo> GetConfigPages(IPlugin plugin) { return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin, i.Item1)); diff --git a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs index a1a68bcf0..e23de86d9 100644 --- a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs @@ -34,38 +34,23 @@ namespace Jellyfin.Server.Extensions .UseSwagger(c => { // Custom path requires {documentName}, SwaggerDoc documentName is 'api-docs' - c.RouteTemplate = $"/{baseUrl}{{documentName}}/openapi.json"; + c.RouteTemplate = "{documentName}/openapi.json"; c.PreSerializeFilters.Add((swagger, httpReq) => { swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}{apiDocBaseUrl}" } }; - - // BaseUrl is empty, ignore - if (apiDocBaseUrl.Length != 0) - { - // Update all relative paths to remove baseUrl. - var updatedPaths = new OpenApiPaths(); - foreach (var (key, value) in swagger.Paths) - { - var relativePath = key; - relativePath = relativePath.Remove(0, apiDocBaseUrl.Length); - updatedPaths.Add(relativePath, value); - } - - swagger.Paths = updatedPaths; - } }); }) .UseSwaggerUI(c => { c.DocumentTitle = "Jellyfin API"; c.SwaggerEndpoint($"/{baseUrl}api-docs/openapi.json", "Jellyfin API"); - c.RoutePrefix = $"{baseUrl}api-docs/swagger"; + c.RoutePrefix = "api-docs/swagger"; }) .UseReDoc(c => { c.DocumentTitle = "Jellyfin API"; c.SpecUrl($"/{baseUrl}api-docs/openapi.json"); - c.RoutePrefix = $"{baseUrl}api-docs/redoc"; + c.RoutePrefix = "api-docs/redoc"; }); } } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 0160a05f9..283c870fd 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -135,10 +135,9 @@ namespace Jellyfin.Server.Extensions /// Extension method for adding the jellyfin API to the service collection. /// </summary> /// <param name="serviceCollection">The service collection.</param> - /// <param name="baseUrl">The base url for the API.</param> - /// <param name="pluginAssemblies">An IEnumberable containing all plugin assemblies with API controllers.</param> + /// <param name="pluginAssemblies">An IEnumerable containing all plugin assemblies with API controllers.</param> /// <returns>The MVC builder.</returns> - public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl, IEnumerable<Assembly> pluginAssemblies) + public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable<Assembly> pluginAssemblies) { IMvcBuilder mvcBuilder = serviceCollection .AddCors(options => @@ -151,7 +150,6 @@ namespace Jellyfin.Server.Extensions }) .AddMvc(opts => { - opts.UseGeneralRoutePrefix(baseUrl); opts.OutputFormatters.Insert(0, new CamelCaseJsonProfileFormatter()); opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter()); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 14cc5f4c2..223bb2558 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -169,7 +169,7 @@ namespace Jellyfin.Server // If hosting the web client, validate the client content path if (startupConfig.HostWebClient()) { - string? webContentPath = DashboardController.GetWebClientUiPath(startupConfig, appHost.ServerConfigurationManager); + string? webContentPath = appHost.ServerConfigurationManager.ApplicationPaths.WebPath; if (!Directory.Exists(webContentPath) || Directory.GetFiles(webContentPath).Length == 0) { throw new InvalidOperationException( diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index cbc1c040c..73c00260e 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -9,9 +9,12 @@ using MediaBrowser.Common; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Prometheus; @@ -44,7 +47,7 @@ namespace Jellyfin.Server { services.AddResponseCompression(); services.AddHttpContextAccessor(); - services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'), _applicationHost.GetApiPluginAssemblies()); + services.AddJellyfinApi(_applicationHost.GetApiPluginAssemblies()); services.AddJellyfinApiSwagger(); @@ -75,10 +78,12 @@ namespace Jellyfin.Server /// <param name="app">The application builder.</param> /// <param name="env">The webhost environment.</param> /// <param name="serverApplicationHost">The server application host.</param> + /// <param name="appConfig">The application config.</param> public void Configure( IApplicationBuilder app, IWebHostEnvironment env, - IServerApplicationHost serverApplicationHost) + IServerApplicationHost serverApplicationHost, + IConfiguration appConfig) { if (env.IsDevelopment()) { @@ -95,6 +100,16 @@ namespace Jellyfin.Server // TODO app.UseMiddleware<WebSocketMiddleware>(); + app.UsePathBase(_serverConfigurationManager.Configuration.BaseUrl); + if (appConfig.HostWebClient()) + { + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath), + RequestPath = "/web" + }); + } + app.UseAuthentication(); app.UseJellyfinApiSwagger(_serverConfigurationManager); app.UseRouting(); @@ -102,7 +117,7 @@ namespace Jellyfin.Server app.UseAuthorization(); if (_serverConfigurationManager.Configuration.EnableMetrics) { - // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad + // Must be registered after any middleware that could change HTTP response codes or the data will be bad app.UseHttpMetrics(); } diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs index 4cea61682..57c654667 100644 --- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -1,5 +1,3 @@ -using MediaBrowser.Model.Configuration; - namespace MediaBrowser.Common.Configuration { /// <summary> @@ -17,8 +15,7 @@ namespace MediaBrowser.Common.Configuration /// Gets the path to the web UI resources folder. /// </summary> /// <remarks> - /// This value is not relevant if the server is configured to not host any static web content. Additionally, - /// the value for <see cref="ServerConfiguration.DashboardSourcePath"/> takes precedence over this one. + /// This value is not relevant if the server is configured to not host any static web content. /// </remarks> string WebPath { get; } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 33975bc1e..97748bd0c 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -167,12 +167,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableDashboardResponseCaching { get; set; } /// <summary> - /// Gets or sets a custom path to serve the dashboard from. - /// </summary> - /// <value>The dashboard source path, or null if the default path should be used.</value> - public string DashboardSourcePath { get; set; } - - /// <summary> /// Gets or sets the image saving convention. /// </summary> /// <value>The image saving convention.</value> |
