From cc1ee0dbce64e16020a8c6c2fb903a2433dde08b Mon Sep 17 00:00:00 2001 From: Berkay Date: Mon, 25 Nov 2019 13:30:48 +0300 Subject: Add Turkish translation --- src/SMAPI/i18n/tr.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/SMAPI/i18n/tr.json diff --git a/src/SMAPI/i18n/tr.json b/src/SMAPI/i18n/tr.json new file mode 100644 index 00000000..34229f2b --- /dev/null +++ b/src/SMAPI/i18n/tr.json @@ -0,0 +1,3 @@ +{ + "warn.invalid-content-removed": "Yanlış paketlenmiş bir içerik, oyunun çökmemesi için yüklenmedi (SMAPI konsol penceresinde detaylı bilgi mevcut)." +} -- cgit From 0223382ca8e267d730629ee69cdf1cff673c8573 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 27 Nov 2019 19:50:42 -0500 Subject: remove unused package --- src/SMAPI.Web/SMAPI.Web.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 8a7ca741..30209146 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -21,7 +21,6 @@ - -- cgit From b1400bcb684c43790dd38628b6c131e9e7c4d400 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 27 Nov 2019 21:49:36 -0500 Subject: fallback to Amazon S3 if saving a log to Pastebin fails --- docs/release-notes.md | 5 + src/SMAPI.Web/Controllers/LogParserController.cs | 177 ++++++++++++++++++--- .../Framework/Clients/Pastebin/PasteInfo.cs | 10 +- .../Framework/Clients/Pastebin/PastebinClient.cs | 2 +- .../Framework/ConfigModels/ApiClientsConfig.cs | 16 ++ src/SMAPI.Web/SMAPI.Web.csproj | 1 + src/SMAPI.Web/Startup.cs | 1 + src/SMAPI.Web/ViewModels/LogParserModel.cs | 9 +- src/SMAPI.Web/Views/LogParser/Index.cshtml | 21 ++- src/SMAPI.Web/appsettings.Development.json | 3 + src/SMAPI.Web/appsettings.json | 35 ++-- src/SMAPI.Web/wwwroot/Content/css/log-parser.css | 6 + 12 files changed, 245 insertions(+), 41 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index a891c495..4234223c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,11 @@ ← [README](README.md) # Release notes +## Upcoming release + +* For the web UI: + * If a log can't be uploaded to Pastebin (e.g. due to rate limits), it'll be uploaded to Amazon S3 instead. Logs uploaded to S3 will expire after one month. + ## 3.0 Released 26 November 2019 for Stardew Valley 1.4. diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index f7f19cd8..32c45038 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -1,6 +1,13 @@ using System; +using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; +using Amazon; +using Amazon.Runtime; +using Amazon.S3; +using Amazon.S3.Model; +using Amazon.S3.Transfer; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using StardewModdingAPI.Toolkit.Utilities; @@ -21,7 +28,10 @@ namespace StardewModdingAPI.Web.Controllers ** Fields *********/ /// The site config settings. - private readonly SiteConfig Config; + private readonly SiteConfig SiteConfig; + + /// The API client settings. + private readonly ApiClientsConfig ClientsConfig; /// The underlying Pastebin client. private readonly IPastebinClient Pastebin; @@ -38,11 +48,13 @@ namespace StardewModdingAPI.Web.Controllers ***/ /// Construct an instance. /// The context config settings. + /// The API client settings. /// The Pastebin API client. /// The underlying text compression helper. - public LogParserController(IOptions siteConfig, IPastebinClient pastebin, IGzipHelper gzipHelper) + public LogParserController(IOptions siteConfig, IOptions clientsConfig, IPastebinClient pastebin, IGzipHelper gzipHelper) { - this.Config = siteConfig.Value; + this.SiteConfig = siteConfig.Value; + this.ClientsConfig = clientsConfig.Value; this.Pastebin = pastebin; this.GzipHelper = gzipHelper; } @@ -66,8 +78,9 @@ namespace StardewModdingAPI.Web.Controllers PasteInfo paste = await this.GetAsync(id); ParsedLog log = paste.Success ? new LogParser().Parse(paste.Content) - : new ParsedLog { IsValid = false, Error = "Pastebin error: " + paste.Error }; - return this.View("Index", this.GetModel(id).SetResult(log, raw)); + : new ParsedLog { IsValid = false, Error = paste.Error }; + + return this.View("Index", this.GetModel(id, uploadWarning: paste.Warning, expiry: paste.Expiry).SetResult(log, raw)); } /*** @@ -85,15 +98,13 @@ namespace StardewModdingAPI.Web.Controllers // upload log input = this.GzipHelper.CompressString(input); - SavePasteResult result = await this.Pastebin.PostAsync($"SMAPI log {DateTime.UtcNow:s}", input); - - // handle errors - if (!result.Success) - return this.View("Index", this.GetModel(result.ID, uploadError: $"Pastebin error: {result.Error ?? "unknown error"}")); + var uploadResult = await this.TrySaveLog(input); + if (!uploadResult.Succeeded) + return this.View("Index", this.GetModel(null, uploadError: uploadResult.UploadError)); // redirect to view - UriBuilder uri = new UriBuilder(new Uri(this.Config.LogParserUrl)); - uri.Path = uri.Path.TrimEnd('/') + '/' + result.ID; + UriBuilder uri = new UriBuilder(new Uri(this.SiteConfig.LogParserUrl)); + uri.Path = $"{uri.Path.TrimEnd('/')}/{uploadResult.ID}"; return this.Redirect(uri.Uri.ToString()); } @@ -105,19 +116,116 @@ namespace StardewModdingAPI.Web.Controllers /// The Pastebin paste ID. private async Task GetAsync(string id) { - PasteInfo response = await this.Pastebin.GetAsync(id); - response.Content = this.GzipHelper.DecompressString(response.Content); - return response; + // get from Amazon S3 + if (Guid.TryParseExact(id, "N", out Guid _)) + { + var credentials = new BasicAWSCredentials(accessKey: this.ClientsConfig.AmazonAccessKey, secretKey: this.ClientsConfig.AmazonSecretKey); + + using (IAmazonS3 s3 = new AmazonS3Client(credentials, RegionEndpoint.GetBySystemName(this.ClientsConfig.AmazonRegion))) + { + try + { + using (GetObjectResponse response = await s3.GetObjectAsync(this.ClientsConfig.AmazonLogBucket, $"logs/{id}")) + using (Stream responseStream = response.ResponseStream) + using (StreamReader reader = new StreamReader(responseStream)) + { + DateTime expiry = response.Expiration.ExpiryDateUtc; + string pastebinError = response.Metadata["x-amz-meta-pastebin-error"]; + string content = this.GzipHelper.DecompressString(reader.ReadToEnd()); + + return new PasteInfo + { + Success = true, + Content = content, + Expiry = expiry, + Warning = pastebinError + }; + } + } + catch (AmazonServiceException ex) + { + return ex.ErrorCode == "NoSuchKey" + ? new PasteInfo { Error = "There's no log with that ID." } + : new PasteInfo { Error = $"Could not fetch that log from AWS S3 ({ex.ErrorCode}: {ex.Message})." }; + } + } + } + + // get from PasteBin + else + { + PasteInfo response = await this.Pastebin.GetAsync(id); + response.Content = this.GzipHelper.DecompressString(response.Content); + return response; + } + } + + /// Save a log to Pastebin or Amazon S3, if available. + /// The content to upload. + /// Returns metadata about the save attempt. + private async Task TrySaveLog(string content) + { + // save to PasteBin + string uploadError; + { + SavePasteResult result = await this.Pastebin.PostAsync($"SMAPI log {DateTime.UtcNow:s}", content); + if (result.Success) + return new UploadResult(true, result.ID, null); + + uploadError = $"Pastebin error: {result.Error ?? "unknown error"}"; + } + + // fallback to S3 + try + { + var credentials = new BasicAWSCredentials(accessKey: this.ClientsConfig.AmazonAccessKey, secretKey: this.ClientsConfig.AmazonSecretKey); + + using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(content))) + using (IAmazonS3 s3 = new AmazonS3Client(credentials, RegionEndpoint.GetBySystemName(this.ClientsConfig.AmazonRegion))) + using (TransferUtility uploader = new TransferUtility(s3)) + { + string id = Guid.NewGuid().ToString("N"); + + var uploadRequest = new TransferUtilityUploadRequest + { + BucketName = this.ClientsConfig.AmazonLogBucket, + Key = $"logs/{id}", + InputStream = stream, + Metadata = + { + // note: AWS will lowercase keys and prefix 'x-amz-meta-' + ["smapi-uploaded"] = DateTime.UtcNow.ToString("O"), + ["pastebin-error"] = uploadError + } + }; + + await uploader.UploadAsync(uploadRequest); + + return new UploadResult(true, id, uploadError); + } + } + catch (Exception ex) + { + return new UploadResult(false, null, $"{uploadError}\n{ex.Message}"); + } } /// Build a log parser model. /// The paste ID. - /// An error which occurred while uploading the log to Pastebin. - private LogParserModel GetModel(string pasteID, string uploadError = null) + /// When the uploaded file will no longer be available. + /// A non-blocking warning while uploading the log. + /// An error which occurred while uploading the log. + private LogParserModel GetModel(string pasteID, DateTime? expiry = null, string uploadWarning = null, string uploadError = null) { - string sectionUrl = this.Config.LogParserUrl; + string sectionUrl = this.SiteConfig.LogParserUrl; Platform? platform = this.DetectClientPlatform(); - return new LogParserModel(sectionUrl, pasteID, platform) { UploadError = uploadError }; + + return new LogParserModel(sectionUrl, pasteID, platform) + { + UploadWarning = uploadWarning, + UploadError = uploadError, + Expiry = expiry + }; } /// Detect the viewer's OS. @@ -143,5 +251,36 @@ namespace StardewModdingAPI.Web.Controllers return null; } } + + /// The result of an attempt to upload a file. + private class UploadResult + { + /********* + ** Accessors + *********/ + /// Whether the file upload succeeded. + public bool Succeeded { get; } + + /// The file ID, if applicable. + public string ID { get; } + + /// The upload error, if any. + public string UploadError { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Whether the file upload succeeded. + /// The file ID, if applicable. + /// The upload error, if any. + public UploadResult(bool succeeded, string id, string uploadError) + { + this.Succeeded = succeeded; + this.ID = id; + this.UploadError = uploadError; + } + } } } diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs index 955156eb..bb2de356 100644 --- a/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs +++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs @@ -1,3 +1,5 @@ +using System; + namespace StardewModdingAPI.Web.Framework.Clients.Pastebin { /// The response for a get-paste request. @@ -9,7 +11,13 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin /// The fetched paste content (if is true). public string Content { get; set; } - /// The error message (if saving failed). + /// When the file will no longer be available. + public DateTime? Expiry { get; set; } + + /// The error message if saving succeeded, but a non-blocking issue was encountered. + public string Warning { get; set; } + + /// The error message if saving failed. public string Error { get; set; } } } diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs index 2e8a8c68..d695aab6 100644 --- a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs +++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs @@ -62,7 +62,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin } catch (Exception ex) { - return new PasteInfo { Error = ex.ToString() }; + return new PasteInfo { Error = $"Pastebin error: {ex}" }; } } diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs index 121690c5..7119ef03 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs @@ -13,6 +13,22 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels public string UserAgent { get; set; } + /**** + ** Amazon Web Services + ****/ + /// The access key for AWS authentication. + public string AmazonAccessKey { get; set; } + + /// The secret key for AWS authentication. + public string AmazonSecretKey { get; set; } + + /// The AWS region endpoint (like 'us-east-1'). + public string AmazonRegion { get; set; } + + /// The AWS bucket in which to store temporary uploaded logs. + public string AmazonLogBucket { get; set; } + + /**** ** Chucklefish ****/ diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 30209146..863c586e 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -12,6 +12,7 @@ + diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 8110b696..fc6161b5 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -58,6 +58,7 @@ namespace StardewModdingAPI.Web { // init basic services services + .Configure(this.Configuration.GetSection("ApiClients")) .Configure(this.Configuration.GetSection("BackgroundServices")) .Configure(this.Configuration.GetSection("ModCompatibilityList")) .Configure(this.Configuration.GetSection("ModUpdateCheck")) diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs index f4c5214b..b06b5b2d 100644 --- a/src/SMAPI.Web/ViewModels/LogParserModel.cs +++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -34,12 +35,18 @@ namespace StardewModdingAPI.Web.ViewModels /// Whether to show the raw unparsed log. public bool ShowRaw { get; set; } - /// An error which occurred while uploading the log to Pastebin. + /// A non-blocking warning while uploading the log. + public string UploadWarning { get; set; } + + /// An error which occurred while uploading the log. public string UploadError { get; set; } /// An error which occurred while parsing the log file. public string ParseError => this.ParsedLog?.Error; + /// When the uploaded file will no longer be available. + public DateTime? Expiry { get; set; } + /********* ** Public methods diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index f98ffdf9..df2ac115 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -1,3 +1,4 @@ +@using Humanizer @using Newtonsoft.Json @using StardewModdingAPI.Toolkit.Utilities @using StardewModdingAPI.Web.Framework.LogParsing.Models @@ -18,7 +19,7 @@ { } - + @@ -62,6 +63,18 @@ else if (Model.ParsedLog?.IsValid == true) } +@* save warnings *@ +@if (Model.UploadWarning != null || Model.Expiry != null) +{ + +} + @* upload new log *@ @if (Model.ParsedLog == null) { @@ -71,7 +84,7 @@ else if (Model.ParsedLog?.IsValid == true) @foreach (Platform platform in new[] { Platform.Android, Platform.Linux, Platform.Mac, Platform.Windows }) {
  • - +
  • } @@ -151,7 +164,7 @@ else if (Model.ParsedLog?.IsValid == true)
    @foreach (LogModInfo contentPack in contentPackList.Where(pack => pack.HasUpdate)) { - + @contentPack.Name
    + + @contentPack.Name
    }
    } @@ -173,7 +186,7 @@ else if (Model.ParsedLog?.IsValid == true)
    @foreach (LogModInfo contentPack in contentPackList.Where(pack => pack.HasUpdate)) { - @contentPack.Version → @contentPack.UpdateVersion
    + @contentPack.Version → @contentPack.UpdateVersion
    }
    } diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index baf7efb7..8e863591 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -18,6 +18,9 @@ }, "ApiClients": { + "AmazonAccessKey": null, + "AmazonSecretKey": null, + "GitHubUsername": null, "GitHubPassword": null, diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 674bb672..3b6f8fbd 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -16,17 +16,22 @@ }, "Site": { - "RootUrl": null, // see top note - "ModListUrl": null, // see top note - "LogParserUrl": null, // see top note - "JsonValidatorUrl": null, // see top note - "BetaEnabled": null, // see top note - "BetaBlurb": null // see top note + "RootUrl": null, + "ModListUrl": null, + "LogParserUrl": null, + "JsonValidatorUrl": null, + "BetaEnabled": null, + "BetaBlurb": null }, "ApiClients": { "UserAgent": "SMAPI/{0} (+https://smapi.io)", + "AmazonAccessKey": null, + "AmazonSecretKey": null, + "AmazonRegion": "us-east-1", + "AmazonLogBucket": "smapi-log-parser", + "ChucklefishBaseUrl": "https://community.playstarbound.com", "ChucklefishModPageUrlFormat": "resources/{0}", @@ -34,27 +39,27 @@ "GitHubBaseUrl": "https://api.github.com", "GitHubAcceptHeader": "application/vnd.github.v3+json", - "GitHubUsername": null, // see top note - "GitHubPassword": null, // see top note + "GitHubUsername": null, + "GitHubPassword": null, "ModDropApiUrl": "https://www.moddrop.com/api/mods/data", "ModDropModPageUrl": "https://www.moddrop.com/sdv/mod/{0}", - "NexusApiKey": null, // see top note + "NexusApiKey": null, "NexusBaseUrl": "https://www.nexusmods.com/stardewvalley/", "NexusModUrlFormat": "mods/{0}", "NexusModScrapeUrlFormat": "mods/{0}?tab=files", "PastebinBaseUrl": "https://pastebin.com/", - "PastebinUserKey": null, // see top note - "PastebinDevKey": null // see top note + "PastebinUserKey": null, + "PastebinDevKey": null }, "MongoDB": { - "Host": null, // see top note - "Username": null, // see top note - "Password": null, // see top note - "Database": null // see top note + "Host": null, + "Username": null, + "Password": null, + "Database": null }, "ModCompatibilityList": { diff --git a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css index d5013207..4d4ab326 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css +++ b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css @@ -47,6 +47,12 @@ table caption { background: #FCC; } +.save-metadata { + margin-top: 1em; + font-size: 0.8em; + opacity: 0.3; +} + /********* ** Log metadata & filters *********/ -- cgit From 286f0f8c1e81e7052312f19d73fc0dfe36313859 Mon Sep 17 00:00:00 2001 From: archification Date: Fri, 29 Nov 2019 23:49:59 -0600 Subject: add alacritty support to unix-launcher --- src/SMAPI.Installer/unix-launcher.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/SMAPI.Installer/unix-launcher.sh b/src/SMAPI.Installer/unix-launcher.sh index f81828f0..bebba9fe 100644 --- a/src/SMAPI.Installer/unix-launcher.sh +++ b/src/SMAPI.Installer/unix-launcher.sh @@ -62,7 +62,7 @@ else fi # select terminal (prefer $TERMINAL for overrides and testing, then xterm for best compatibility, then known supported terminals) - for terminal in "$TERMINAL" xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite x-terminal-emulator; do + for terminal in "$TERMINAL" xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite alacritty x-terminal-emulator; do if $COMMAND "$terminal" 2>/dev/null; then # Find the true shell behind x-terminal-emulator if [ "$(basename "$(readlink -f $(which "$terminal"))")" != "x-terminal-emulator" ]; then @@ -100,6 +100,14 @@ else # Kitty overrides the TERM varible unless you set it explicitly kitty -o term=xterm $LAUNCHER ;; + alacritty) + # Alacritty doesn't like the double quotes or the variable + if [ "$ARCH" == "x86_64" ]; then + alacritty -e sh -c 'TERM=xterm ./StardewModdingAPI.bin.x86_64 $*' + else + alacritty -e sh -c 'TERM=xterm ./StardewModdingAPI.bin.x86 $*' + fi + ;; xterm|xfce4-terminal|gnome-terminal|terminal|termite) $LAUNCHTERM -e "sh -c 'TERM=xterm $LAUNCHER'" ;; -- cgit From 10a18290773f3152f018ef9ecc3f7ae4544d7367 Mon Sep 17 00:00:00 2001 From: Daniel Heath Date: Sat, 30 Nov 2019 20:25:16 +1100 Subject: Prevent linux install failing with cryptic error This avoids the error described at https://stackoverflow.com/questions/49242075/mono-bug-magic-number-is-wrong-542 System.TypeInitializationException: The type initializer for 'System.Console' threw an exception. ---> System.TypeInitializationException: The type initializer for 'System.ConsoleDriver' threw an exception. ---> System.Exception: Magic number is wrong: 542 --- src/SMAPI.Installer/unix-install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SMAPI.Installer/unix-install.sh b/src/SMAPI.Installer/unix-install.sh index e3a5d8cc..6d0c86ce 100644 --- a/src/SMAPI.Installer/unix-install.sh +++ b/src/SMAPI.Installer/unix-install.sh @@ -12,6 +12,9 @@ elif type type >/dev/null 2>&1; then COMMAND="type" fi +# if $TERM is not set to xterm, mono will bail out when attempting to write to the console. +export TERM=xterm + # validate Mono & run installer if $COMMAND mono >/dev/null 2>&1; then mono internal/unix-install.exe -- cgit From 9fadc3a6ad5066cabdac9e853f32b3eb69259bab Mon Sep 17 00:00:00 2001 From: FeatherShine Date: Sun, 1 Dec 2019 00:42:27 +0800 Subject: add Chinese translation --- src/SMAPI/i18n/zh.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/SMAPI/i18n/zh.json diff --git a/src/SMAPI/i18n/zh.json b/src/SMAPI/i18n/zh.json new file mode 100644 index 00000000..8dec396a --- /dev/null +++ b/src/SMAPI/i18n/zh.json @@ -0,0 +1,3 @@ +{ + "warn.invalid-content-removed": "非法内容已移除以防游戏闪退(查看SMAPI控制台获得更多信息)" +} \ No newline at end of file -- cgit From d7948fbe1cfb111fab009cd0977778dfff358d58 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 30 Nov 2019 16:44:34 -0500 Subject: update translation docs (#664, #671) --- docs/README.md | 2 +- docs/release-notes.md | 2 ++ src/SMAPI/i18n/zh.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index ddde6b09..d675b742 100644 --- a/docs/README.md +++ b/docs/README.md @@ -63,7 +63,7 @@ contributing translations. locale | status ---------- | :---------------- default | ✓ [fully translated](../src/SMAPI/i18n/default.json) -Chinese | ❑ not translated +Chinese | ✓ [fully translated](../src/SMAPI/i18n/zh.json) French | ❑ not translated German | ✓ [fully translated](../src/SMAPI/i18n/de.json) Hungarian | ❑ not translated diff --git a/docs/release-notes.md b/docs/release-notes.md index 4234223c..fde74d9d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,7 @@ * For the web UI: * If a log can't be uploaded to Pastebin (e.g. due to rate limits), it'll be uploaded to Amazon S3 instead. Logs uploaded to S3 will expire after one month. + * Updated translations. Thanks to feathershine (added Chinese)! ## 3.0 Released 26 November 2019 for Stardew Valley 1.4. @@ -85,6 +86,7 @@ For modders: * Update checks are now faster in some cases. * Updated mod compatibility list. * Updated SMAPI/game version map. + * Updated translations. Thanks to eren-kemer (added German)! * Fixes: * Fixed some assets not updated when you switch language to English. * Fixed lag in some cases due to incorrect asset caching when playing in non-English. diff --git a/src/SMAPI/i18n/zh.json b/src/SMAPI/i18n/zh.json index 8dec396a..bbd6a574 100644 --- a/src/SMAPI/i18n/zh.json +++ b/src/SMAPI/i18n/zh.json @@ -1,3 +1,3 @@ { "warn.invalid-content-removed": "非法内容已移除以防游戏闪退(查看SMAPI控制台获得更多信息)" -} \ No newline at end of file +} -- cgit From 854ee8c1542ba709d70df3d53082945f1fd196f1 Mon Sep 17 00:00:00 2001 From: Georgii Smirnov Date: Sun, 1 Dec 2019 17:43:50 +0300 Subject: Create ru.json Add Russian Translation --- src/SMAPI/i18n/ru.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/SMAPI/i18n/ru.json diff --git a/src/SMAPI/i18n/ru.json b/src/SMAPI/i18n/ru.json new file mode 100644 index 00000000..a6a242fa --- /dev/null +++ b/src/SMAPI/i18n/ru.json @@ -0,0 +1,3 @@ +{ + "warn.invalid-content-removed": "Недопустимое содержимое было удалено, чтобы предотвратить сбой (см. информацию в консоли SMAPI)" +} -- cgit From 8766a79408ce79bd632c1fe1c9b17ab0a7a6a976 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 2 Dec 2019 20:13:58 -0500 Subject: update readme and release notes --- docs/README.md | 4 ++-- docs/release-notes.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index d675b742..386259a9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -71,6 +71,6 @@ Italian | ❑ not translated Japanese | ❑ not translated Korean | ❑ not translated Portuguese | ❑ not translated -Russian | ❑ not translated +Russian | ✓ [fully translated](../src/SMAPI/i18n/ru.json) Spanish | ❑ not translated -Turkish | ❑ not translated +Turkish | ✓ [fully translated](../src/SMAPI/i18n/tr.json) diff --git a/docs/release-notes.md b/docs/release-notes.md index fde74d9d..c3840148 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,7 +5,8 @@ * For the web UI: * If a log can't be uploaded to Pastebin (e.g. due to rate limits), it'll be uploaded to Amazon S3 instead. Logs uploaded to S3 will expire after one month. - * Updated translations. Thanks to feathershine (added Chinese)! + * Improved compatibility with some Linux terminals (thanks to archification and DanielHeath!). + * Updated translations. Thanks to berkayylmao (added Turkish), feathershine (added Chinese), and Osiris901 (added Russian)! ## 3.0 Released 26 November 2019 for Stardew Valley 1.4. -- cgit From 5f532c259d5d3050bd6a053659067617db136d57 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 1 Dec 2019 21:55:20 -0500 Subject: migrate from AWS to Azure This commit migrates from subdomains to paths (due to the cost of a wildcard HTTPS certificate on Azure), adds a web project to redirect the old subdomains from AWS to Azure, and removes AWS-specific hacks. --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .gitignore | 5 +- build/prepare-install-package.targets | 2 +- docs/release-notes.md | 22 ++--- docs/technical/web.md | 12 ++- .../Controllers/ModsApiController.cs | 33 ++++++++ .../Framework/LambdaRewriteRule.cs | 37 +++++++++ src/SMAPI.Web.LegacyRedirects/Program.cs | 23 ++++++ .../Properties/launchSettings.json | 29 +++++++ .../SMAPI.Web.LegacyRedirects.csproj | 21 +++++ src/SMAPI.Web.LegacyRedirects/Startup.cs | 94 ++++++++++++++++++++++ .../Controllers/JsonValidatorController.cs | 24 ++---- src/SMAPI.Web/Controllers/LogParserController.cs | 14 +--- src/SMAPI.Web/Controllers/ModsApiController.cs | 8 +- .../Framework/BeanstalkEnvPropsConfigProvider.cs | 54 ------------- .../Framework/ConfigModels/ModUpdateCheckConfig.cs | 3 - src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs | 12 --- .../ConditionalRewriteSubdomainRule.cs | 48 ----------- src/SMAPI.Web/Startup.cs | 17 +--- .../ViewModels/JsonValidator/JsonValidatorModel.cs | 7 +- src/SMAPI.Web/ViewModels/LogParserModel.cs | 7 +- src/SMAPI.Web/Views/Index/Index.cshtml | 10 +-- src/SMAPI.Web/Views/Index/Privacy.cshtml | 4 +- src/SMAPI.Web/Views/JsonValidator/Index.cshtml | 10 +-- src/SMAPI.Web/Views/LogParser/Index.cshtml | 16 ++-- src/SMAPI.Web/Views/Shared/_Layout.cshtml | 8 +- src/SMAPI.Web/appsettings.Development.json | 4 - src/SMAPI.Web/appsettings.json | 7 +- src/SMAPI.sln | 6 ++ src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- src/SMAPI/Framework/Models/SConfig.cs | 2 +- src/SMAPI/Framework/SCore.cs | 4 +- src/SMAPI/SMAPI.config.json | 2 +- 33 files changed, 311 insertions(+), 238 deletions(-) create mode 100644 src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs create mode 100644 src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs create mode 100644 src/SMAPI.Web.LegacyRedirects/Program.cs create mode 100644 src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json create mode 100644 src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj create mode 100644 src/SMAPI.Web.LegacyRedirects/Startup.cs delete mode 100644 src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs delete mode 100644 src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 74954cf4..c51d164b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -24,7 +24,7 @@ Exact steps which reproduce the bug, if possible. For example: 4. Error occurs. **Log file** -Upload your SMAPI log to https://log.smapi.io and post a link here. +Upload your SMAPI log to https://smapi.io/log and post a link here. **Screenshots** If applicable, add screenshots to help explain your problem. diff --git a/.gitignore b/.gitignore index 7080a8fc..65695211 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,7 @@ _ReSharper*/ appsettings.Development.json # AWS generated files -src/SMAPI.Web/aws-beanstalk-tools-defaults.json +src/SMAPI.Web.LegacyRedirects/aws-beanstalk-tools-defaults.json + +# Azure generated files +src/SMAPI.Web/Properties/PublishProfiles/smapi-web-release - Web Deploy.pubxml diff --git a/build/prepare-install-package.targets b/build/prepare-install-package.targets index e5286bf5..4297756d 100644 --- a/build/prepare-install-package.targets +++ b/build/prepare-install-package.targets @@ -63,7 +63,7 @@ - + diff --git a/docs/release-notes.md b/docs/release-notes.md index c3840148..dcce70c9 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -110,7 +110,7 @@ For modders: * Clicking a mod link now automatically adds it to the visible mods if the list is filtered. * JSON validator: - * Added JSON validator at [json.smapi.io](https://json.smapi.io), which lets you validate a JSON file against predefined mod formats. + * Added JSON validator at [smapi.io/json](https://smapi.io/json), which lets you validate a JSON file against predefined mod formats. * Added support for the `manifest.json` format. * Added support for the Content Patcher format (thanks to TehPers!). * Added support for referencing a schema in a JSON Schema-compatible text editor. @@ -373,7 +373,7 @@ Released 19 November 2018 for Stardew Valley 1.3.32. * Updated compatibility list. * For the web UI: - * Added a [mod compatibility page](https://mods.smapi.io) and [privacy page](https://smapi.io/privacy). + * Added a [mod compatibility page](https://smapi.io/mods) and [privacy page](https://smapi.io/privacy). * The log parser now has a separate filter for game messages. * The log parser now shows content pack authors (thanks to danvolchek!). * Tweaked log parser UI (thanks to danvolchek!). @@ -557,7 +557,7 @@ Released 11 April 2018 for Stardew Valley 1.2.30–1.2.33. * Fixed error when two content packs use different capitalization for the same required mod ID. * Fixed rare crash if the game duplicates an item. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Tweaked UI. ## 2.5.4 @@ -569,7 +569,7 @@ Released 26 March 2018 for Stardew Valley 1.2.30–1.2.33. * Fixed error when mods remove an asset editor/loader. * Fixed minimum game version incorrectly increased in SMAPI 2.5.3. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Fixed error when log text contains certain tokens. * For modders: @@ -591,7 +591,7 @@ Released 13 March 2018 for Stardew Valley ~~1.2.30~~–1.2.33. * Fixed Linux ["magic number is wrong" errors](https://github.com/mono/mono/issues/6752) by changing default terminal order. * Updated compatibility list and added update checks for more mods. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Fixed incorrect filtering in some cases. * Fixed error if mods have duplicate names. * Fixed parse bugs if a mod has no author name. @@ -605,7 +605,7 @@ Released 25 February 2018 for Stardew Valley 1.2.30–1.2.33. * For modders: * Fixed issue where replacing an asset through `asset.AsImage()` or `asset.AsDictionary()` didn't take effect. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Fixed blank page after uploading a log in some cases. ## 2.5.1 @@ -636,7 +636,7 @@ Released 24 February 2018 for Stardew Valley 1.2.30–1.2.33. * Fixed unhelpful error when a translation file has duplicate keys due to case-insensitivity. * Fixed some JSON field names being case-sensitive. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Added support for SMAPI 2.5 content packs. * Reduced download size when viewing a parsed log with repeated errors. * Improved parse error handling. @@ -657,7 +657,7 @@ Released 24 January 2018 for Stardew Valley 1.2.30–1.2.33. * Fixed intermittent errors (e.g. 'collection has been modified') with some mods when loading a save. * Fixed compatibility with Linux Terminator terminal. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Fixed error parsing logs with zero installed mods. * For modders: @@ -692,7 +692,7 @@ Released 26 December 2017 for Stardew Valley 1.2.30–1.2.33. * Fixed issue where a mod could change the cursor position reported to other mods. * Updated compatibility list. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Fixed broken favicon. ## 2.2 @@ -706,7 +706,7 @@ Released 02 December 2017 for Stardew Valley 1.2.30–1.2.33. * Improved error when a mod has an invalid `EntryDLL` filename format. * Updated compatibility list. -* For the [log parser](https://log.smapi.io): +* For the [log parser](https://smapi.io/log): * Logs no longer expire after a week. * Fixed error when uploading very large logs. * Slightly improved the UI. @@ -721,7 +721,7 @@ Released 02 December 2017 for Stardew Valley 1.2.30–1.2.33. Released 01 November 2017 for Stardew Valley 1.2.30–1.2.33. * For players: - * Added a [log parser](https://log.smapi.io) site. + * Added a [log parser](https://smapi.io/log) site. * Added better Steam instructions to the SMAPI installer. * Renamed the bundled _TrainerMod_ to _ConsoleCommands_ to make its purpose clearer. * Removed the game's test messages from the console log. diff --git a/docs/technical/web.md b/docs/technical/web.md index 78d93625..97e0704a 100644 --- a/docs/technical/web.md +++ b/docs/technical/web.md @@ -14,13 +14,13 @@ and update check API. ## Log parser The log parser provides a web UI for uploading, parsing, and sharing SMAPI logs. The logs are -persisted in a compressed form to Pastebin. The log parser lives at https://log.smapi.io. +persisted in a compressed form to Pastebin. The log parser lives at https://smapi.io/log. ## JSON validator ### Overview The JSON validator provides a web UI for uploading and sharing JSON files, and validating them as plain JSON or against a predefined format like `manifest.json` or Content Patcher's `content.json`. -The JSON validator lives at https://json.smapi.io. +The JSON validator lives at https://smapi.io/json. ### Schema file format Schema files are defined in `wwwroot/schemas` using the [JSON Schema](https://json-schema.org/) @@ -111,7 +111,7 @@ format | schema URL ## Web API ### Overview -SMAPI provides a web API at `api.smapi.io` for use by SMAPI and external tools. The URL includes a +SMAPI provides a web API at `smapi.io/api` for use by SMAPI and external tools. The URL includes a `{version}` token, which is the SMAPI version for backwards compatibility. This API is publicly accessible but not officially released; it may change at any time. @@ -184,7 +184,7 @@ may be useful to external tools. Example request: ```js -POST https://api.smapi.io/v3.0/mods +POST https://smapi.io/api/v3.0/mods { "mods": [ { @@ -350,8 +350,7 @@ To launch the environment: mongod --dbpath C:\dev\smapi-cache ``` 2. Launch `SMAPI.Web` from Visual Studio to run a local version of the site. - (Local URLs will use HTTP instead of HTTPS, and subdomains will become routes, like - `log.smapi.io` → `localhost:59482/log`.) + (Local URLs will use HTTP instead of HTTPS.) ### Production environment A production environment includes the web servers and cache database hosted online for public @@ -367,7 +366,6 @@ Initial setup: ------------------------------- | ----------------- `LogParser:PastebinDevKey` | The [Pastebin developer key](https://pastebin.com/api#1) used to authenticate with the Pastebin API. `LogParser:PastebinUserKey` | The [Pastebin user key](https://pastebin.com/api#8) used to authenticate with the Pastebin API. Can be left blank to post anonymously. - `LogParser:SectionUrl` | The root URL of the log page, like `https://log.smapi.io/`. `ModUpdateCheck:GitHubPassword` | The password with which to authenticate to GitHub when fetching release info. `ModUpdateCheck:GitHubUsername` | The username with which to authenticate to GitHub when fetching release info. `MongoDB:Host` | The hostname for the MongoDB instance. diff --git a/src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs b/src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs new file mode 100644 index 00000000..44ed0b6b --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Pathoschild.Http.Client; +using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; + +namespace SMAPI.Web.LegacyRedirects.Controllers +{ + /// Provides an API to perform mod update checks. + [ApiController] + [Produces("application/json")] + [Route("api/v{version}/mods")] + public class ModsApiController : Controller + { + /********* + ** Public methods + *********/ + /// Fetch version metadata for the given mods. + /// The mod search criteria. + [HttpPost] + public async Task> PostAsync([FromBody] ModSearchModel model) + { + using IClient client = new FluentClient("https://smapi.io/api"); + + Startup.ConfigureJsonNet(client.Formatters.JsonFormatter.SerializerSettings); + + return await client + .PostAsync(this.Request.Path) + .WithBody(model) + .AsArray(); + } + } +} diff --git a/src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs b/src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs new file mode 100644 index 00000000..e5138e5c --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace SMAPI.Web.LegacyRedirects.Framework +{ + /// Rewrite requests to prepend the subdomain portion (if any) to the path. + /// Derived from . + internal class LambdaRewriteRule : IRule + { + /********* + ** Accessors + *********/ + /// Rewrite an HTTP request if needed. + private readonly Action Rewrite; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Rewrite an HTTP request if needed. + public LambdaRewriteRule(Action rewrite) + { + this.Rewrite = rewrite ?? throw new ArgumentNullException(nameof(rewrite)); + } + + /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). + /// The rewrite context. + public void ApplyRule(RewriteContext context) + { + HttpRequest request = context.HttpContext.Request; + HttpResponse response = context.HttpContext.Response; + this.Rewrite(context, request, response); + } + } +} diff --git a/src/SMAPI.Web.LegacyRedirects/Program.cs b/src/SMAPI.Web.LegacyRedirects/Program.cs new file mode 100644 index 00000000..6adee877 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Program.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace SMAPI.Web.LegacyRedirects +{ + /// The main app entry point. + public class Program + { + /********* + ** Public methods + *********/ + /// The main app entry point. + /// The command-line arguments. + public static void Main(string[] args) + { + Host + .CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(builder => builder.UseStartup()) + .Build() + .Run(); + } + } +} diff --git a/src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json b/src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json new file mode 100644 index 00000000..e9a1b210 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52756", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "SMAPI.Web.LegacyRedirects": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "/", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj b/src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj new file mode 100644 index 00000000..a3d5c2b6 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.0 + + + + + + + + + + + + + + + + + diff --git a/src/SMAPI.Web.LegacyRedirects/Startup.cs b/src/SMAPI.Web.LegacyRedirects/Startup.cs new file mode 100644 index 00000000..4af51575 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Startup.cs @@ -0,0 +1,94 @@ +using System.Net; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Rewrite; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using SMAPI.Web.LegacyRedirects.Framework; +using StardewModdingAPI.Toolkit.Serialization; + +namespace SMAPI.Web.LegacyRedirects +{ + /// The web app startup configuration. + public class Startup + { + /********* + ** Public methods + *********/ + /// The method called by the runtime to add services to the container. + /// The service injection container. + public void ConfigureServices(IServiceCollection services) + { + services + .AddControllers() + .AddNewtonsoftJson(options => Startup.ConfigureJsonNet(options.SerializerSettings)); + } + + /// The method called by the runtime to configure the HTTP request pipeline. + /// The application builder. + /// The hosting environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + app.UseDeveloperExceptionPage(); + + app + .UseRewriter(this.GetRedirectRules()) + .UseRouting() + .UseAuthorization() + .UseEndpoints(endpoints => endpoints.MapControllers()); + } + + /// Configure a Json.NET serializer. + /// The serializer settings to edit. + internal static void ConfigureJsonNet(JsonSerializerSettings settings) + { + foreach (JsonConverter converter in new JsonHelper().JsonSettings.Converters) + settings.Converters.Add(converter); + + settings.Formatting = Formatting.Indented; + settings.NullValueHandling = NullValueHandling.Ignore; + } + + + /********* + ** Private methods + *********/ + /// Get the redirect rules to apply. + private RewriteOptions GetRedirectRules() + { + var redirects = new RewriteOptions(); + + redirects.Add( + new LambdaRewriteRule((context, request, response) => + { + string host = request.Host.Host; + + // map API requests to proxy + // This is needed because the low-level HTTP client SMAPI uses for Linux/Mac compatibility doesn't support redirects. + if (host == "api.smapi.io") + { + request.Path = $"/api{request.Path}"; + return; + } + + // redirect other requests to Azure + string newRoot = host switch + { + "api.smapi.io" => "smapi.io/api", + "json.smapi.io" => "smapi.io/json", + "log.smapi.io" => "smapi.io/log", + "mods.smapi.io" => "smapi.io/mods", + _ => "smapi.io" + }; + response.StatusCode = (int)HttpStatusCode.PermanentRedirect; + response.Headers["Location"] = $"{(request.IsHttps ? "https" : "http")}://{newRoot}{request.PathBase}{request.Path}{request.QueryString}"; + context.Result = RuleResult.EndResponse; + }) + ); + + return redirects; + } + } +} diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs index b2eb9a87..c32fb084 100644 --- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs +++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs @@ -5,14 +5,12 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; using StardewModdingAPI.Web.Framework; using StardewModdingAPI.Web.Framework.Clients.Pastebin; using StardewModdingAPI.Web.Framework.Compression; -using StardewModdingAPI.Web.Framework.ConfigModels; using StardewModdingAPI.Web.ViewModels.JsonValidator; namespace StardewModdingAPI.Web.Controllers @@ -23,18 +21,12 @@ namespace StardewModdingAPI.Web.Controllers /********* ** Fields *********/ - /// The site config settings. - private readonly SiteConfig Config; - /// The underlying Pastebin client. private readonly IPastebinClient Pastebin; /// The underlying text compression helper. private readonly IGzipHelper GzipHelper; - /// The section URL for the schema validator. - private string SectionUrl => this.Config.JsonValidatorUrl; - /// The supported JSON schemas (names indexed by ID). private readonly IDictionary SchemaFormats = new Dictionary { @@ -57,12 +49,10 @@ namespace StardewModdingAPI.Web.Controllers ** Constructor ***/ /// Construct an instance. - /// The context config settings. /// The Pastebin API client. /// The underlying text compression helper. - public JsonValidatorController(IOptions siteConfig, IPastebinClient pastebin, IGzipHelper gzipHelper) + public JsonValidatorController(IPastebinClient pastebin, IGzipHelper gzipHelper) { - this.Config = siteConfig.Value; this.Pastebin = pastebin; this.GzipHelper = gzipHelper; } @@ -81,7 +71,7 @@ namespace StardewModdingAPI.Web.Controllers { schemaName = this.NormalizeSchemaName(schemaName); - var result = new JsonValidatorModel(this.SectionUrl, id, schemaName, this.SchemaFormats); + var result = new JsonValidatorModel(id, schemaName, this.SchemaFormats); if (string.IsNullOrWhiteSpace(id)) return this.View("Index", result); @@ -142,7 +132,7 @@ namespace StardewModdingAPI.Web.Controllers public async Task PostAsync(JsonValidatorRequestModel request) { if (request == null) - return this.View("Index", new JsonValidatorModel(this.SectionUrl, null, null, this.SchemaFormats).SetUploadError("The request seems to be invalid.")); + return this.View("Index", new JsonValidatorModel(null, null, this.SchemaFormats).SetUploadError("The request seems to be invalid.")); // normalize schema name string schemaName = this.NormalizeSchemaName(request.SchemaName); @@ -150,7 +140,7 @@ namespace StardewModdingAPI.Web.Controllers // get raw log text string input = request.Content; if (string.IsNullOrWhiteSpace(input)) - return this.View("Index", new JsonValidatorModel(this.SectionUrl, null, schemaName, this.SchemaFormats).SetUploadError("The JSON file seems to be empty.")); + return this.View("Index", new JsonValidatorModel(null, schemaName, this.SchemaFormats).SetUploadError("The JSON file seems to be empty.")); // upload log input = this.GzipHelper.CompressString(input); @@ -158,12 +148,10 @@ namespace StardewModdingAPI.Web.Controllers // handle errors if (!result.Success) - return this.View("Index", new JsonValidatorModel(this.SectionUrl, result.ID, schemaName, this.SchemaFormats).SetUploadError($"Pastebin error: {result.Error ?? "unknown error"}")); + return this.View("Index", new JsonValidatorModel(result.ID, schemaName, this.SchemaFormats).SetUploadError($"Pastebin error: {result.Error ?? "unknown error"}")); // redirect to view - UriBuilder uri = new UriBuilder(new Uri(this.SectionUrl)); - uri.Path = $"{uri.Path.TrimEnd('/')}/{schemaName}/{result.ID}"; - return this.Redirect(uri.Uri.ToString()); + return this.Redirect(this.Url.Action("Index", "LogParser", new { schemaName = schemaName, id = result.ID })); } diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 32c45038..2ced5a05 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -27,9 +27,6 @@ namespace StardewModdingAPI.Web.Controllers /********* ** Fields *********/ - /// The site config settings. - private readonly SiteConfig SiteConfig; - /// The API client settings. private readonly ApiClientsConfig ClientsConfig; @@ -47,13 +44,11 @@ namespace StardewModdingAPI.Web.Controllers ** Constructor ***/ /// Construct an instance. - /// The context config settings. /// The API client settings. /// The Pastebin API client. /// The underlying text compression helper. - public LogParserController(IOptions siteConfig, IOptions clientsConfig, IPastebinClient pastebin, IGzipHelper gzipHelper) + public LogParserController(IOptions clientsConfig, IPastebinClient pastebin, IGzipHelper gzipHelper) { - this.SiteConfig = siteConfig.Value; this.ClientsConfig = clientsConfig.Value; this.Pastebin = pastebin; this.GzipHelper = gzipHelper; @@ -103,9 +98,7 @@ namespace StardewModdingAPI.Web.Controllers return this.View("Index", this.GetModel(null, uploadError: uploadResult.UploadError)); // redirect to view - UriBuilder uri = new UriBuilder(new Uri(this.SiteConfig.LogParserUrl)); - uri.Path = $"{uri.Path.TrimEnd('/')}/{uploadResult.ID}"; - return this.Redirect(uri.Uri.ToString()); + return this.Redirect(this.Url.Action("Index", "LogParser", new { id = uploadResult.ID })); } @@ -217,10 +210,9 @@ namespace StardewModdingAPI.Web.Controllers /// An error which occurred while uploading the log. private LogParserModel GetModel(string pasteID, DateTime? expiry = null, string uploadWarning = null, string uploadError = null) { - string sectionUrl = this.SiteConfig.LogParserUrl; Platform? platform = this.DetectClientPlatform(); - return new LogParserModel(sectionUrl, pasteID, platform) + return new LogParserModel(pasteID, platform) { UploadWarning = uploadWarning, UploadError = uploadError, diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index fe220eb5..f10e0067 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/s