瀏覽代碼

Properly host static files and set base url

crobibero 4 年之前
父節點
當前提交
1feee6f95e

+ 2 - 100
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,38 +27,20 @@ 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>
@@ -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));

+ 3 - 18
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";
                 });
         }
     }

+ 2 - 4
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());
 

+ 1 - 1
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(

+ 18 - 3
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();
             }
 

+ 1 - 4
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; }
 

+ 0 - 6
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -166,12 +166,6 @@ namespace MediaBrowser.Model.Configuration
         /// <value><c>true</c> if [enable dashboard response caching]; otherwise, <c>false</c>.</value>
         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>