diff --git a/NzbDrone.Api/Frontend/IndexModule.cs b/NzbDrone.Api/Frontend/IndexModule.cs deleted file mode 100644 index 46eac2824..000000000 --- a/NzbDrone.Api/Frontend/IndexModule.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.IO; -using Nancy; -using Nancy.Responses; -using NzbDrone.Common; -using NzbDrone.Common.Cache; -using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Api.Extensions; - -namespace NzbDrone.Api.Frontend -{ - public class IndexModule : NancyModule - { - private readonly IDiskProvider _diskProvider; - private readonly ICached _indexCache; - - private readonly string _indexPath; - - public IndexModule(IDiskProvider diskProvider, ICacheManger cacheManger, IAppFolderInfo appFolder) - { - _diskProvider = diskProvider; - - _indexPath = Path.Combine(appFolder.StartUpFolder, "UI", "index.html"); - - _indexCache = cacheManger.GetCache(typeof(IndexModule)); - //Serve anything that doesn't have an extension - Get[@"/(.*)"] = x => Index(); - } - - private object Index() - { - if ( - Request.Path.Contains(".") - || Request.Path.StartsWith("/static", StringComparison.CurrentCultureIgnoreCase) - || Request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase) - || Request.Path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase)) - { - return new NotFoundResponse(); - } - - - var htmlResponse = new HtmlResponse(); - - htmlResponse.Contents = stream => - { - var lastWrite = _diskProvider.GetLastFileWrite(_indexPath); - var text = _indexCache.Get(lastWrite.Ticks.ToString(), GetIndexText); - - var streamWriter = new StreamWriter(stream); - - streamWriter.Write(text); - streamWriter.Flush(); - }; - - - htmlResponse.Headers.DisableCache(); - - return htmlResponse; - } - - - private string GetIndexText() - { - var text = _diskProvider.ReadAllText(_indexPath); - - text = text.Replace(".css", ".css?v=" + BuildInfo.Version); - text = text.Replace(".js", ".js?v=" + BuildInfo.Version); - - return text; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/IsCacheableSpecification.cs b/NzbDrone.Api/Frontend/IsCacheableSpecification.cs new file mode 100644 index 000000000..30775159a --- /dev/null +++ b/NzbDrone.Api/Frontend/IsCacheableSpecification.cs @@ -0,0 +1,18 @@ +using System; +using Nancy; + +namespace NzbDrone.Api.Frontend +{ + public class IsCacheableSpecification + { + public bool IsCacheable(Request request) + { + if (request.Path.Contains(".")) return false; + if (request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase)) return false; + if (request.Path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase)) return false; + + + return true; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/IMapHttpRequestsToDisk.cs b/NzbDrone.Api/Frontend/Mappers/IMapHttpRequestsToDisk.cs similarity index 78% rename from NzbDrone.Api/Frontend/IMapHttpRequestsToDisk.cs rename to NzbDrone.Api/Frontend/Mappers/IMapHttpRequestsToDisk.cs index 9abbc2e8c..f57530643 100644 --- a/NzbDrone.Api/Frontend/IMapHttpRequestsToDisk.cs +++ b/NzbDrone.Api/Frontend/Mappers/IMapHttpRequestsToDisk.cs @@ -1,5 +1,5 @@  -namespace NzbDrone.Api.Frontend +namespace NzbDrone.Api.Frontend.Mappers { public interface IMapHttpRequestsToDisk { diff --git a/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs b/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs new file mode 100644 index 000000000..c90531ee9 --- /dev/null +++ b/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs @@ -0,0 +1,25 @@ +using System.IO; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Api.Frontend.Mappers +{ + public class IndexHtmlMapper : IMapHttpRequestsToDisk + { + private readonly string _indexPath; + + public IndexHtmlMapper(IAppFolderInfo appFolderInfo) + { + _indexPath = Path.Combine(appFolderInfo.StartUpFolder, "UI", "index.html"); + } + + public string Map(string resourceUrl) + { + return _indexPath; + } + + public bool CanHandle(string resourceUrl) + { + return !resourceUrl.Contains("."); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/LogFileMapper.cs b/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs similarity index 94% rename from NzbDrone.Api/Frontend/LogFileMapper.cs rename to NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs index 9997c2496..36cfc18bf 100644 --- a/NzbDrone.Api/Frontend/LogFileMapper.cs +++ b/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs @@ -2,7 +2,7 @@ using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -namespace NzbDrone.Api.Frontend +namespace NzbDrone.Api.Frontend.Mappers { public class LogFileMapper : IMapHttpRequestsToDisk { diff --git a/NzbDrone.Api/Frontend/MediaCoverMapper.cs b/NzbDrone.Api/Frontend/Mappers/MediaCoverMapper.cs similarity index 94% rename from NzbDrone.Api/Frontend/MediaCoverMapper.cs rename to NzbDrone.Api/Frontend/Mappers/MediaCoverMapper.cs index fd5626d4d..7400614cf 100644 --- a/NzbDrone.Api/Frontend/MediaCoverMapper.cs +++ b/NzbDrone.Api/Frontend/Mappers/MediaCoverMapper.cs @@ -2,7 +2,7 @@ using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -namespace NzbDrone.Api.Frontend +namespace NzbDrone.Api.Frontend.Mappers { public class MediaCoverMapper : IMapHttpRequestsToDisk { diff --git a/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs b/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs new file mode 100644 index 000000000..cd1f619e8 --- /dev/null +++ b/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs @@ -0,0 +1,28 @@ +using System.IO; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Api.Frontend.Mappers +{ + public class StaticResourceMapper : IMapHttpRequestsToDisk + { + private readonly IAppFolderInfo _appFolderInfo; + + public StaticResourceMapper(IAppFolderInfo appFolderInfo) + { + _appFolderInfo = appFolderInfo; + } + + public string Map(string resourceUrl) + { + var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); + path = path.Trim(Path.DirectorySeparatorChar); + + return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path); + } + + public bool CanHandle(string resourceUrl) + { + return resourceUrl.StartsWith("/Content") || resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".css"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/StaticResourceMapper.cs b/NzbDrone.Api/Frontend/StaticResourceMapper.cs deleted file mode 100644 index 17872b18e..000000000 --- a/NzbDrone.Api/Frontend/StaticResourceMapper.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using NzbDrone.Common.EnvironmentInfo; - -namespace NzbDrone.Api.Frontend -{ - public class StaticResourceMapper : IMapHttpRequestsToDisk - { - private readonly IAppFolderInfo _appFolderInfo; - private static readonly string[] Extensions = new[] { - ".css", - ".js", - ".html", - ".htm", - ".jpg", - ".jpeg", - ".ico", - ".icon", - ".gif", - ".png", - ".woff", - ".ttf", - ".eot" - }; - - public StaticResourceMapper(IAppFolderInfo appFolderInfo) - { - _appFolderInfo = appFolderInfo; - } - - public string Map(string resourceUrl) - { - var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); - path = path.Trim(Path.DirectorySeparatorChar); - - - return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path); - } - - public bool CanHandle(string resourceUrl) - { - if (string.IsNullOrWhiteSpace(resourceUrl)) - { - return false; - } - - if (resourceUrl.StartsWith("/mediacover", StringComparison.CurrentCultureIgnoreCase)) - { - return false; - } - - return Extensions.Any(resourceUrl.EndsWith); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/StaticResourceModule.cs b/NzbDrone.Api/Frontend/StaticResourceModule.cs new file mode 100644 index 000000000..0fceb918c --- /dev/null +++ b/NzbDrone.Api/Frontend/StaticResourceModule.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NLog; +using Nancy; +using Nancy.Responses; +using NzbDrone.Api.Frontend.Mappers; +using NzbDrone.Common; +using NzbDrone.Common.Cache; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Api.Frontend +{ + public class StaticResourceModule : NancyModule + { + private readonly IDiskProvider _diskProvider; + private readonly IEnumerable _requestMappers; + private readonly Logger _logger; + private readonly ICached _indexCache; + + private readonly bool _caseSensitive; + + public StaticResourceModule(IDiskProvider diskProvider, ICacheManger cacheManger, IEnumerable requestMappers, Logger logger) + { + _diskProvider = diskProvider; + _requestMappers = requestMappers; + _logger = logger; + + _indexCache = cacheManger.GetCache(typeof(StaticResourceModule)); + + Get["/{resource*}"] = x => Index(); + Get["/*"] = x => Index(); + + if (!RuntimeInfo.IsProduction) + { + _caseSensitive = true; + } + } + + private Response Index() + { + var path = Request.Url.Path; + + if ( + string.IsNullOrWhiteSpace(path) || + path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase) || + path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase)) + { + return null; + } + + + var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path)); + + + if (mapper != null) + { + var filePath = mapper.Map(path); + + if (_diskProvider.FileExists(filePath, _caseSensitive)) + { + var response = new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath)); + //_addCacheHeaders.ToResponse(context.Request, response); + + return response; + } + + _logger.Warn("File {0} not found", filePath); + } + else + { + _logger.Warn("Couldn't find handler for {0}", path); + } + + return new NotFoundResponse(); + + /* htmlResponse.Contents = stream => + { + var lastWrite = _diskProvider.GetLastFileWrite(_indexPath); + var text = _indexCache.Get(lastWrite.Ticks.ToString(), GetIndexText); + + var streamWriter = new StreamWriter(stream); + + streamWriter.Write(text); + streamWriter.Flush(); + };*/ + + //htmlResponse.Headers.DisableCache(); + } + + + /* private string GetIndexText() + { + var text = _diskProvider.ReadAllText(_indexPath); + + text = text.Replace(".css", ".css?v=" + BuildInfo.Version); + text = text.Replace(".js", ".js?v=" + BuildInfo.Version); + + return text; + }*/ + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/StaticResourceProvider.cs b/NzbDrone.Api/Frontend/StaticResourceProvider.cs index 1d2c6fb57..7a757a155 100644 --- a/NzbDrone.Api/Frontend/StaticResourceProvider.cs +++ b/NzbDrone.Api/Frontend/StaticResourceProvider.cs @@ -4,6 +4,7 @@ using NLog; using Nancy; using Nancy.Responses; +using NzbDrone.Api.Frontend.Mappers; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; diff --git a/NzbDrone.Api/NancyBootstrapper.cs b/NzbDrone.Api/NancyBootstrapper.cs index c98d0765c..63b2ba1c3 100644 --- a/NzbDrone.Api/NancyBootstrapper.cs +++ b/NzbDrone.Api/NancyBootstrapper.cs @@ -1,11 +1,9 @@ using NLog; using Nancy.Bootstrapper; -using Nancy.Conventions; using Nancy.Diagnostics; using NzbDrone.Api.Authentication; using NzbDrone.Api.ErrorManagement; using NzbDrone.Api.Extensions; -using NzbDrone.Api.Frontend; using NzbDrone.Common.Messaging; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Lifecycle; @@ -50,13 +48,6 @@ protected override DiagnosticsConfiguration DiagnosticsConfiguration get { return new DiagnosticsConfiguration { Password = @"password" }; } } - protected override void ConfigureConventions(NancyConventions nancyConventions) - { - base.ConfigureConventions(nancyConventions); - var processors = ApplicationContainer.Resolve(); - Conventions.StaticContentsConventions.Add(processors.ProcessStaticResourceRequest); - } - protected override byte[] FavIcon { get diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index b20d48df3..f634d7afb 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -94,13 +94,15 @@ - - - - + + + + + + + -