From 3f43ebcc0e31db523fa82a163374cebf2f577cde Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 21:10:36 -0400 Subject: [PATCH] fix issues with subdomain routing in log UI (#358) --- .../Controllers/LogParserController.cs | 12 ++++++--- .../Framework/ConfigModels/LogParserConfig.cs | 3 +++ .../Framework/RewriteSubdomainRule.cs | 22 ++++++++++++++-- src/SMAPI.Web/Startup.cs | 9 ++++++- src/SMAPI.Web/ViewModels/LogParserModel.cs | 26 +++++++++++++++++++ src/SMAPI.Web/Views/LogParser/Index.cshtml | 6 +++++ src/SMAPI.Web/appsettings.Development.json | 5 +++- src/SMAPI.Web/appsettings.json | 1 + .../wwwroot/Content/js/log-parser.js | 9 ++++--- 9 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 src/SMAPI.Web/ViewModels/LogParserModel.cs diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 893d9a52..ee1d51cd 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Options; using StardewModdingAPI.Web.Framework; using StardewModdingAPI.Web.Framework.ConfigModels; using StardewModdingAPI.Web.Framework.LogParser; +using StardewModdingAPI.Web.ViewModels; namespace StardewModdingAPI.Web.Controllers { @@ -13,6 +14,9 @@ namespace StardewModdingAPI.Web.Controllers /********* ** Properties *********/ + /// The log parser config settings. + private readonly LogParserConfig Config; + /// The underlying Pastebin client. private readonly PastebinClient PastebinClient; @@ -28,10 +32,10 @@ namespace StardewModdingAPI.Web.Controllers public LogParserController(IOptions configProvider) { // init Pastebin client - LogParserConfig config = configProvider.Value; + this.Config = configProvider.Value; string version = this.GetType().Assembly.GetName().Version.ToString(3); - string userAgent = string.Format(config.PastebinUserAgent, version); - this.PastebinClient = new PastebinClient(config.PastebinBaseUrl, userAgent, config.PastebinDevKey); + string userAgent = string.Format(this.Config.PastebinUserAgent, version); + this.PastebinClient = new PastebinClient(this.Config.PastebinBaseUrl, userAgent, this.Config.PastebinDevKey); } /*** @@ -42,7 +46,7 @@ namespace StardewModdingAPI.Web.Controllers [Route("log")] public ViewResult Index() { - return this.View("Index"); + return this.View("Index", new LogParserModel(this.Config.SectionUrl)); } /*** diff --git a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs index 5cb0cf95..18d8ff05 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs @@ -6,6 +6,9 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /********* ** Accessors *********/ + /// The root URL for the log parser controller. + public string SectionUrl { get; set; } + /// The base URL for the Pastebin API. public string PastebinBaseUrl { get; set; } diff --git a/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs index 5a56844f..cc183fe3 100644 --- a/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs +++ b/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs @@ -1,4 +1,7 @@ using System; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Rewrite; namespace StardewModdingAPI.Web.Framework @@ -7,14 +10,29 @@ namespace StardewModdingAPI.Web.Framework /// Derived from . internal class RewriteSubdomainRule : IRule { + /********* + ** Accessors + *********/ + /// The paths (excluding the hostname portion) to not rewrite. + public Regex[] ExceptPaths { get; set; } + + + /********* + ** Public methods + *********/ /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). /// The rewrite context. public void ApplyRule(RewriteContext context) { context.Result = RuleResult.ContinueRules; + HttpRequest request = context.HttpContext.Request; + + // check ignores + if (this.ExceptPaths?.Any(pattern => pattern.IsMatch(request.Path)) == true) + return; // get host parts - string host = context.HttpContext.Request.Host.Host; + string host = request.Host.Host; string[] parts = host.Split('.'); // validate @@ -24,7 +42,7 @@ namespace StardewModdingAPI.Web.Framework return; // prepend to path - context.HttpContext.Request.Path = $"/{parts[0]}{context.HttpContext.Request.Path}"; + request.Path = $"/{parts[0]}{request.Path}"; } } } diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index c0ea90da..e19593c7 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -1,3 +1,4 @@ +using System.Text.RegularExpressions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Rewrite; @@ -64,7 +65,13 @@ namespace StardewModdingAPI.Web loggerFactory.AddConsole(this.Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app - .UseRewriter(new RewriteOptions().Add(new RewriteSubdomainRule())) // convert subdomain.smapi.io => smapi.io/subdomain for routing + .UseRewriter( + new RewriteOptions() + .Add(new RewriteSubdomainRule + { + ExceptPaths = new[] { new Regex("^/Content", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) } + }) + ) // convert subdomain.smapi.io => smapi.io/subdomain for routing .UseStaticFiles() // wwwroot folder .UseMvc(); } diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs new file mode 100644 index 00000000..ba31db87 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs @@ -0,0 +1,26 @@ +namespace StardewModdingAPI.Web.ViewModels +{ + /// The view model for the log parser page. + public class LogParserModel + { + /********* + ** Accessors + *********/ + /// The root URL for the log parser controller. + public string SectionUrl { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public LogParserModel() { } + + /// Construct an instance. + /// The root URL for the log parser controller. + public LogParserModel(string sectionUrl) + { + this.SectionUrl = sectionUrl; + } + } +} diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 87a3962b..bd47f2ff 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -1,12 +1,18 @@ @{ ViewData["Title"] = "SMAPI log parser"; } +@model StardewModdingAPI.Web.ViewModels.LogParserModel @section Head { + } @********* diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index fa8ce71a..e49eabb7 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -1,4 +1,4 @@ -{ +{ "Logging": { "IncludeScopes": false, "LogLevel": { @@ -6,5 +6,8 @@ "System": "Information", "Microsoft": "Information" } + }, + "LogParser": { + "SectionUrl": "http://localhost:59482/log/" } } diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index ca1299ce..1b5c35cd 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -28,6 +28,7 @@ "NexusModUrlFormat": "mods/{0}" }, "LogParser": { + "SectionUrl": "https://log.smapi.io/", "PastebinBaseUrl": "https://pastebin.com/", "PastebinUserAgent": "SMAPI/{0} (+https://github.com/Pathoschild/SMAPI)", "PastebinDevKey": "b8219d942109d1e60ebb14fbb45f06f9" diff --git a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js index b1f8f5c6..3949fabe 100644 --- a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js +++ b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js @@ -1,6 +1,7 @@ /* globals $, LZString */ -$(function() { +var smapi = smapi || {}; +smapi.logParser = function(sectionUrl) { /********* ** Initialisation *********/ @@ -75,7 +76,7 @@ $(function() { $ .ajax({ type: "POST", - url: "/log/save", + url: sectionUrl + "/save", data: JSON.stringify(paste), contentType: "application/json" // sent to API }) @@ -273,7 +274,7 @@ $(function() { function getData() { $("#uploader").attr("data-text", "Loading..."); $("#uploader").fadeIn(); - $.get("/log/fetch/" + location.search.substring(1), function(data) { + $.get(sectionUrl + "/fetch/" + location.search.substring(1), function(data) { if (data.success) { $("#input").val(LZString.decompressFromUTF16(data.content) || data.content); loadData(); @@ -284,4 +285,4 @@ $(function() { $("#uploader").fadeOut(); }); } -}); +};