diff options
Diffstat (limited to 'src/SMAPI.Web')
-rw-r--r-- | src/SMAPI.Web/Framework/LogParser/PastebinClient.cs | 41 | ||||
-rw-r--r-- | src/SMAPI.Web/Program.cs | 8 | ||||
-rw-r--r-- | src/SMAPI.Web/Startup.cs | 7 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/LogParser/Index.cshtml | 93 | ||||
-rw-r--r-- | src/SMAPI.Web/wwwroot/Content/css/log-parser.css | 45 | ||||
-rw-r--r-- | src/SMAPI.Web/wwwroot/Content/js/log-parser.js | 70 |
6 files changed, 145 insertions, 119 deletions
diff --git a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs index 738330d3..1cfaed17 100644 --- a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs +++ b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; +using System.Text; using System.Threading.Tasks; +using System.Web; using Pathoschild.Http.Client; namespace StardewModdingAPI.Web.Framework.LogParser @@ -67,6 +69,8 @@ namespace StardewModdingAPI.Web.Framework.LogParser } } + /// <summary>Save a paste to Pastebin.</summary> + /// <param name="content">The paste content.</param> public async Task<SavePasteResponse> PostAsync(string content) { try @@ -77,18 +81,18 @@ namespace StardewModdingAPI.Web.Framework.LogParser // post to API string response = await this.Client - .PostAsync("api/api_post.php") - .WithBodyContent(new FormUrlEncodedContent(new Dictionary<string, string> - { - ["api_option"] = "paste", - ["api_user_key"] = this.UserKey, - ["api_dev_key"] = this.DevKey, - ["api_paste_private"] = "1", // unlisted - ["api_paste_name"] = $"SMAPI log {DateTime.UtcNow:s}", - ["api_paste_expire_date"] = "1W", // one week - ["api_paste_code"] = content - })) - .AsString(); + .PostAsync("api/api_post.php") + .WithBodyContent(this.GetFormUrlEncodedContent(new Dictionary<string, string> + { + ["api_option"] = "paste", + ["api_user_key"] = this.UserKey, + ["api_dev_key"] = this.DevKey, + ["api_paste_private"] = "1", // unlisted + ["api_paste_name"] = $"SMAPI log {DateTime.UtcNow:s}", + ["api_paste_expire_date"] = "N", // never expire + ["api_paste_code"] = content + })) + .AsString(); // handle Pastebin errors if (string.IsNullOrWhiteSpace(response)) @@ -113,5 +117,18 @@ namespace StardewModdingAPI.Web.Framework.LogParser { this.Client.Dispose(); } + + + /********* + ** Private methods + *********/ + /// <summary>Build an HTTP content body with form-url-encoded content.</summary> + /// <param name="data">The content to encode.</param> + /// <remarks>This bypasses an issue where <see cref="FormUrlEncodedContent"/> restricts the body length to the maximum size of a URL, which isn't applicable here.</remarks> + private HttpContent GetFormUrlEncodedContent(IDictionary<string, string> data) + { + string body = string.Join("&", from arg in data select $"{HttpUtility.UrlEncode(arg.Key)}={HttpUtility.UrlEncode(arg.Value)}"); + return new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"); + } } } diff --git a/src/SMAPI.Web/Program.cs b/src/SMAPI.Web/Program.cs index eeecb791..5856fc98 100644 --- a/src/SMAPI.Web/Program.cs +++ b/src/SMAPI.Web/Program.cs @@ -1,4 +1,4 @@ -using System.IO; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace StardewModdingAPI.Web @@ -14,10 +14,8 @@ namespace StardewModdingAPI.Web public static void Main(string[] args) { // configure web server - new WebHostBuilder() - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() + WebHost + .CreateDefaultBuilder(args) .UseStartup<Startup>() .Build() .Run(); diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 0ea9f7ee..16952124 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -33,7 +33,7 @@ namespace StardewModdingAPI.Web .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) - .Add(new BeanstalkEnvPropsConfigProvider()) //.AddEnvironmentVariables() + .Add(new BeanstalkEnvPropsConfigProvider()) .Build(); } @@ -63,6 +63,10 @@ namespace StardewModdingAPI.Web { loggerFactory.AddConsole(this.Configuration.GetSection("Logging")); loggerFactory.AddDebug(); + + if (env.IsDevelopment()) + app.UseDeveloperExceptionPage(); + app .UseCors(policy => policy .AllowAnyHeader() @@ -76,6 +80,7 @@ namespace StardewModdingAPI.Web shouldRewrite: req => req.Host.Host != "localhost" && !req.Path.StartsWithSegments("/api") + && !req.Host.Host.StartsWith("api.") )) // convert subdomain.smapi.io => smapi.io/subdomain for routing diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 49688d78..b7724c69 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -3,9 +3,9 @@ } @model StardewModdingAPI.Web.ViewModels.LogParserModel @section Head { - <link rel="stylesheet" href="~/Content/css/log-parser.css" /> + <link rel="stylesheet" href="~/Content/css/log-parser.css?r=20171202" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script> - <script src="~/Content/js/log-parser.js"></script> + <script src="~/Content/js/log-parser.js?r=20171202"></script> <style type="text/css" id="modflags"></style> <script> $(function() { @@ -23,49 +23,48 @@ @if (Model.PasteID != null) { <h2>Parsed log</h2> - <ul id="tabs"> - <li>TRACE</li> - <li>DEBUG</li> - <li class="active">INFO</li> - <li class="active">ALERT</li> - <li class="active">WARN</li> - <li class="active">ERROR</li> - <li class="notice">Click tabs to toggle message visibility</li> - </ul> } -<div id="output" class="trace debug"></div> -<script class="template" id="template-body" type="text/html"> - <div class="always"> - <table id="gameinfo"> - <caption>Game info:</caption> - <tr> - <td>SMAPI Version</td> - <td>{0}</td> - </tr> - <tr> - <td>Game Version</td> - <td>{1}</td> - </tr> - <tr> - <td>Platform</td> - <td>{2}</td> - </tr> - <tr> - <td>Mods path</td> - <td>{4}</td> - </tr> - <tr> - <td>Log started</td> - <td>{3}</td> - </tr> - </table> - <br /> - <table id="modslist"> - <caption>Installed Mods: <span id="modlink-r" class="notice btn">Remove all mod filters</span><span class="notice txt"><i>Click any mod to filter</i></span> <span id="modlink-a" class="notice btn txt">Select all</span></caption> - </table> - </div> - <table id="log"></table> -</script> +<div id="output" class="trace debug"> + @if (Model.PasteID != null) + { + <div id="log-data" style="display: none;"> + <div class="always"> + <table id="gameinfo"> + <caption>Game info:</caption> + <tr> + <td>SMAPI Version</td> + <td id="api-version"></td> + </tr> + <tr> + <td>Game Version</td> + <td id="game-version"></td> + </tr> + <tr> + <td>Platform</td> + <td id="platform"></td> + </tr> + <tr> + <td>Mods path</td> + <td id="mods-path"></td> + </tr> + <tr> + <td>Log started</td> + <td id="log-started"></td> + </tr> + </table> + <br/> + <table id="modslist"> + <caption>Installed Mods: <span id="modlink-r" class="notice btn">Remove all mod filters</span><span class="notice txt"><i>Click any mod to filter</i></span> <span id="modlink-a" class="notice btn txt">Select all</span></caption> + </table> + <div id="filters"> + Filter messages: <span>TRACE</span> | <span>DEBUG</span> | <span class="active">INFO</span> | <span class="active">ALERT</span> | <span class="active">WARN</span> | <span class="active">ERROR</span> + </div> + </div> + <table id="log"></table> + </div> + } + <div id="error" class="color-red"></div> +</div> <script class="template" id="template-css" type="text/html"> #output.modfilter:not(.mod-{0}) .mod-{0} { display:none; } #output.modfilter.mod-{0} #modslist tr { background:#ffeeee; } #output.modfilter.mod-{0} #modslist tr#modlink-{0} { background:#eeffee; } </script> @@ -95,15 +94,15 @@ <h1>Upload log file</h1> <div class="frame"> <ol> - <li><a href="https://stardewvalleywiki.com/Modding:Player_FAQs#SMAPI_log" target="_blank">Find your SMAPI log</a>.</li> + <li><a href="https://stardewvalleywiki.com/Modding:Player_FAQs#SMAPI_log" target="_blank">Find your SMAPI log file</a> (not the console text).</li> <li>Drag the file onto the textbox below (or paste the text in).</li> <li>Click <em>Parse</em>.</li> <li>Share the URL of the new page.</li> </ol> <textarea id="input" placeholder="Paste or drag the log here"></textarea> <div class="buttons"> - <input type="button" id="submit" value="Parse"/> - <input type="button" id="cancel" value="Cancel"/> + <input type="button" id="submit" value="Parse" /> + <input type="button" id="cancel" value="Cancel" /> </div> </div> </div> diff --git a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css index 975e9c2e..9f07f9e8 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css +++ b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css @@ -124,67 +124,38 @@ input[type="button"] { color: green; } -#tabs { - border-bottom: 0; - display: block; - margin: 0; +#filters { + margin: 1em 0 0 0; padding: 0; - background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(210, 235, 249, 1) 100%); } -#tabs li { - margin: 5px 1px 0 0; - height: 25px; +#filters span { + padding: 3px 1em; display: inline-block; - width: 75px; border: 1px solid #000000; - border-bottom: 0; - border-radius: 5px 5px 0 0; - text-align: center; + border-radius: 3px; font-family: monospace; - font-size: 18px; cursor: pointer; font-weight: bold; color: #000; - text-shadow: 0px 0px 2px #fff; border-color: #880000; background-color: #fcc; - box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 0, .2); } -#tabs li:hover { +#filters span:hover { background-color: #fee; } -#tabs li:first-child { - margin-left: 5px; -} - -#tabs li.active { +#filters span.active { background: #cfc; border-color: #008800; } -#tabs li.active:hover { +#filters span.active:hover { background: #efe; } -#tabs li.notice { - color: #000000; - background: transparent; - border: 0; - padding-top: 1px; - font-size: 13px; - font-weight: normal; - width: auto; - margin-left: 5px; - cursor: default; - box-shadow: none; - font-style: italic; -} - #output { - border-top: 1px solid #888; padding: 10px; overflow: auto; font-family: monospace; diff --git a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js index 8e30ae12..6cce1ce9 100644 --- a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js +++ b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js @@ -8,6 +8,7 @@ smapi.logParser = function(sectionUrl, pasteID) { var stage, flags = $("#modflags"), output = $("#output"), + error = $("#error"), filters = 0, memory = "", versionInfo, @@ -15,7 +16,6 @@ smapi.logParser = function(sectionUrl, pasteID) { modMap, modErrors, logInfo, - templateBody = $("#template-body").text(), templateModentry = $("#template-modentry").text(), templateCss = $("#template-css").text(), templateLogentry = $("#template-logentry").text(), @@ -27,16 +27,24 @@ smapi.logParser = function(sectionUrl, pasteID) { regexDate = /\[\d{2}:\d{2}:\d{2} TRACE SMAPI\] Log started at (.*?) UTC/g, regexPath = /\[\d{2}:\d{2}:\d{2} DEBUG SMAPI\] Mods go here: (.*?)(?:\n|$)/g; - $("#tabs li:not(.notice)").on("click", function(evt) { + $("#filters span").on("click", function(evt) { var t = $(evt.currentTarget); t.toggleClass("active"); - $("#output").toggleClass(t.text().toLowerCase()); + output.toggleClass(t.text().toLowerCase()); }); $("#upload-button").on("click", function() { memory = $("#input").val() || ""; $("#input").val(""); $("#popup-upload").fadeIn(); }); + + var closeUploadPopUp = function() { + $("#popup-upload").fadeOut(400, function() { + $("#input").val(memory); + memory = ""; + }); + }; + $("#popup-upload").on({ 'dragover dragenter': function(e) { e.preventDefault(); @@ -58,6 +66,10 @@ smapi.logParser = function(sectionUrl, pasteID) { }, this, file, $("#input")); reader.readAsText(file); } + }, + 'click': function(e) { + if (e.target.id === "popup-upload") + closeUploadPopUp(); } }); @@ -77,12 +89,12 @@ smapi.logParser = function(sectionUrl, pasteID) { }) .fail(function(xhr, textStatus) { $("#uploader").fadeOut(); - $("#output").html('<div id="log" class="color-red"><h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: Upload</p>Error: ' + textStatus + ': ' + xhr.responseText + "<hr /><pre>" + $("#input").val() + "</pre></div>"); + error.html('<h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: Upload</p>Error: ' + textStatus + ': ' + xhr.responseText + "<hr /><pre>" + $("#input").val() + "</pre>"); }) .then(function(data) { $("#uploader").fadeOut(); if (!data.success) - $("#output").html('<div id="log" class="color-red"><h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: Upload</p>Error: ' + data.error + "<hr />" + $("#input").val() + "</div>"); + error.html('<h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: Upload</p>Error: ' + data.error + "<hr /><pre>" + $("#input").val() + "</pre>"); else location.href = (sectionUrl.replace(/\/$/, "") + "/" + data.id); }); @@ -91,15 +103,28 @@ smapi.logParser = function(sectionUrl, pasteID) { $("#uploader").fadeOut(); } }); - $("#cancel").on("click", function() { - $("#popup-upload").fadeOut(400, function() { - $("#input").val(memory); - memory = ""; - }); + + $(document).on("keydown", function(e) { + if (e.which == 27) { + if ($("#popup-upload").css("display") !== "none" && $("#popup-upload").css("opacity") == 1) { + closeUploadPopUp(); + } + + $("#popup-raw").fadeOut(400); + } }); + $("#cancel").on("click", closeUploadPopUp); + $("#closeraw").on("click", function() { $("#popup-raw").fadeOut(400); }); + + $("#popup-raw").on("click", function(e) { + if (e.target.id === "popup-raw") { + $("#popup-raw").fadeOut(400); + } + }); + if (pasteID) { getData(pasteID); } @@ -176,7 +201,13 @@ smapi.logParser = function(sectionUrl, pasteID) { ]; stage = "parseData.parseInfo"; var date = dataDate ? new Date(dataDate[1] + "Z") : null; - versionInfo = [dataInfo[1], dataInfo[2], dataInfo[3], date ? date.getFullYear() + "-" + ("0" + date.getMonth().toString()).substr(-2) + "-" + ("0" + date.getDay().toString()).substr(-2) + " at " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + " " + date.toLocaleTimeString("en-us", { timeZoneName: "short" }).split(" ")[2] : "No timestamp found", dataPath[1]]; + versionInfo = { + apiVersion: dataInfo[1], + gameVersion: dataInfo[2], + platform: dataInfo[3], + logDate: date ? date.getFullYear() + "-" + ("0" + date.getMonth().toString()).substr(-2) + "-" + ("0" + date.getDay().toString()).substr(-2) + " at " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + " " + date.toLocaleTimeString("en-us", { timeZoneName: "short" }).split(" ")[2] : "No timestamp found", + modsPath: dataPath[1] + }; stage = "parseData.parseMods"; while ((match = regexMod.exec(dataMods))) { modErrors[match[1]] = 0; @@ -196,7 +227,13 @@ smapi.logParser = function(sectionUrl, pasteID) { function renderData() { stage = "renderData.pre"; - output.html(prepare(templateBody, versionInfo)); + + output.find("#api-version").text(versionInfo.apiVersion); + output.find("#game-version").text(versionInfo.gameVersion); + output.find("#platform").text(versionInfo.platform); + output.find("#log-started").text(versionInfo.logDate); + output.find("#mods-path").text(versionInfo.modsPath); + var modslist = $("#modslist"), log = $("#log"), modCache = [], y = 0; for (; y < modInfo.length; y++) { var errors = modErrors[modInfo[y][0]], @@ -233,6 +270,8 @@ smapi.logParser = function(sectionUrl, pasteID) { log.append(logCache.join("")); $("#modlink-r").on("click", removeFilter); $("#modlink-a").on("click", selectAll); + + $("#log-data").show(); } function prepare(str, arr) { @@ -245,11 +284,8 @@ smapi.logParser = function(sectionUrl, pasteID) { function loadData() { try { stage = "loadData.Pre"; - var start = performance.now(); parseData(); renderData(); - var end = performance.now(); - $(".always").prepend("<div>Log processed in: " + (Math.round((end - start) * 100) / 100) + ' ms (<a id="viewraw" href="#">View raw</a>)</div><br />'); $("#viewraw").on("click", function() { $("#dataraw").val($("#input").val()); $("#popup-raw").fadeIn(); @@ -257,7 +293,7 @@ smapi.logParser = function(sectionUrl, pasteID) { stage = "loadData.Post"; } catch (err) { - $("#output").html('<div id="log" class="color-red"><h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: ' + stage + "</p>" + err + '<hr /><pre id="rawlog"></pre></div>'); + error.html('<h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: ' + stage + "</p>" + err + '<hr /><pre id="rawlog"></pre>'); $("#rawlog").text($("#input").val()); } } @@ -269,7 +305,7 @@ smapi.logParser = function(sectionUrl, pasteID) { $("#input").val(data.content); loadData(); } else { - $("#output").html('<div id="log" class="color-red"><h1>Fetching the log failed!</h1><p>' + data.error + '</p><pre id="rawlog"></pre></div>'); + error.html('<h1>Fetching the log failed!</h1><p>' + data.error + '</p><pre id="rawlog"></pre>'); $("#rawlog").text($("#input").val()); } $("#uploader").fadeOut(); |