summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web')
-rw-r--r--src/SMAPI.Web/Framework/LogParser/PastebinClient.cs41
-rw-r--r--src/SMAPI.Web/Program.cs8
-rw-r--r--src/SMAPI.Web/Startup.cs7
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml93
-rw-r--r--src/SMAPI.Web/wwwroot/Content/css/log-parser.css45
-rw-r--r--src/SMAPI.Web/wwwroot/Content/js/log-parser.js70
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 />&nbsp;<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 />&nbsp;<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 />&nbsp;<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 />&nbsp;<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 />&nbsp;<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 />&nbsp;<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();