From 2b1f607d41b3d4d071c0db0671dbc99b6982909f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 3 Dec 2019 21:21:28 -0500 Subject: encapsulate file storage, also handle Pastebin rate limits in JSON validator --- src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs index 7119ef03..1e020840 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs @@ -26,7 +26,7 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels public string AmazonRegion { get; set; } /// The AWS bucket in which to store temporary uploaded logs. - public string AmazonLogBucket { get; set; } + public string AmazonTempBucket { get; set; } /**** -- cgit From 8ddb60cee636cc17291100c316df4786eb3bb448 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 3 Dec 2019 23:06:42 -0500 Subject: move supporter list into environment config --- src/SMAPI.Web/Controllers/IndexController.cs | 2 +- src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs | 3 +++ src/SMAPI.Web/ViewModels/IndexModel.cs | 7 ++++++- src/SMAPI.Web/Views/Index/Index.cshtml | 24 ++++++++-------------- src/SMAPI.Web/appsettings.Development.json | 5 ----- src/SMAPI.Web/appsettings.json | 5 +++-- 6 files changed, 22 insertions(+), 24 deletions(-) (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs index 4e3602d5..a887f14a 100644 --- a/src/SMAPI.Web/Controllers/IndexController.cs +++ b/src/SMAPI.Web/Controllers/IndexController.cs @@ -72,7 +72,7 @@ namespace StardewModdingAPI.Web.Controllers : null; // render view - var model = new IndexModel(stableVersionModel, betaVersionModel, this.SiteConfig.BetaBlurb); + var model = new IndexModel(stableVersionModel, betaVersionModel, this.SiteConfig.BetaBlurb, this.SiteConfig.SupporterList); return this.View(model); } diff --git a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs index d379c423..43969f51 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs @@ -11,5 +11,8 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// A short sentence shown under the beta download button, if any. public string BetaBlurb { get; set; } + + /// A list of supports to credit on the main page, in Markdown format. + public string SupporterList { get; set; } } } diff --git a/src/SMAPI.Web/ViewModels/IndexModel.cs b/src/SMAPI.Web/ViewModels/IndexModel.cs index 82c4e06f..450fdc0e 100644 --- a/src/SMAPI.Web/ViewModels/IndexModel.cs +++ b/src/SMAPI.Web/ViewModels/IndexModel.cs @@ -15,6 +15,9 @@ namespace StardewModdingAPI.Web.ViewModels /// A short sentence shown under the beta download button, if any. public string BetaBlurb { get; set; } + /// A list of supports to credit on the main page, in Markdown format. + public string SupporterList { get; set; } + /********* ** Public methods @@ -26,11 +29,13 @@ namespace StardewModdingAPI.Web.ViewModels /// The latest stable SMAPI version. /// The latest prerelease SMAPI version (if newer than ). /// A short sentence shown under the beta download button, if any. - internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion, string betaBlurb) + /// A list of supports to credit on the main page, in Markdown format. + internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion, string betaBlurb, string supporterList) { this.StableVersion = stableVersion; this.BetaVersion = betaVersion; this.BetaBlurb = betaBlurb; + this.SupporterList = supporterList; } } } diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml index ec9cfafe..5d91dc84 100644 --- a/src/SMAPI.Web/Views/Index/Index.cshtml +++ b/src/SMAPI.Web/Views/Index/Index.cshtml @@ -1,3 +1,4 @@ +@using Markdig @using Microsoft.Extensions.Options @using StardewModdingAPI.Web.Framework @using StardewModdingAPI.Web.Framework.ConfigModels @@ -94,29 +95,22 @@ else
  • -

    - Special thanks to - bwdy, - hawkfalcon, - iKeychain, - jwdred, - Karmylla, - minervamaga, - Pucklynn, - Renorien, - Robby LaFarge, - and a few anonymous users for their ongoing support on Patreon; you're awesome! -

    +@if (!string.IsNullOrWhiteSpace(Model.SupporterList)) +{ + @Html.Raw(Markdig.Markdown.ToHtml( + $"Special thanks to {Model.SupporterList}, and a few anonymous users for their ongoing support on Patreon; you're awesome!" + )) +}

    For mod creators

      diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index 6b32f4ab..74ded25d 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -8,11 +8,6 @@ */ { - "Site": { - "BetaEnabled": false, - "BetaBlurb": null - }, - "ApiClients": { "AmazonAccessKey": null, "AmazonSecretKey": null, diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index b3567469..2e20b299 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -16,8 +16,9 @@ }, "Site": { - "BetaEnabled": null, - "BetaBlurb": null + "BetaEnabled": false, + "BetaBlurb": null, + "SupporterList": null }, "ApiClients": { -- cgit From 02f645900eb31648376abe21df30dd956221ad90 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 21 Dec 2019 11:56:19 -0500 Subject: add option to disable uploading new files to Pastebin --- src/SMAPI.Web/Controllers/JsonValidatorController.cs | 2 +- src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs | 3 +++ src/SMAPI.Web/Framework/Storage/StorageProvider.cs | 3 ++- src/SMAPI.Web/appsettings.json | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs index e4eff0f4..c4bfff3b 100644 --- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs +++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs @@ -141,7 +141,7 @@ namespace StardewModdingAPI.Web.Controllers return this.View("Index", this.GetModel(null, schemaName).SetUploadError("The JSON file seems to be empty.")); // upload file - var result = await this.Storage.SaveAsync(title: $"JSON validator {DateTime.UtcNow:s}", content: input, compress: true); + UploadResult result = await this.Storage.SaveAsync(title: $"JSON validator {DateTime.UtcNow:s}", content: input, compress: true); if (!result.Succeeded) return this.View("Index", this.GetModel(result.ID, schemaName).SetUploadError(result.UploadError)); diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs index 1e020840..e88f351f 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs @@ -97,5 +97,8 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// The developer key used to authenticate with the Pastebin API. public string PastebinDevKey { get; set; } + /// Whether to enable uploading new files to Pastebin. This doesn't affect fetching already-uploaded files. + public bool PastebinEnableUploads { get; set; } + } } diff --git a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs index bbb6e06b..e5e71325 100644 --- a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs @@ -52,7 +52,8 @@ namespace StardewModdingAPI.Web.Framework.Storage public async Task SaveAsync(string title, string content, bool compress = true) { // save to PasteBin - string uploadError; + string uploadError = null; + if (this.ClientsConfig.PastebinEnableUploads) { SavePasteResult result = await this.Pastebin.PostAsync(title, content); if (result.Success) diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 2e20b299..6ba05a22 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -49,7 +49,8 @@ "PastebinBaseUrl": "https://pastebin.com/", "PastebinUserKey": null, - "PastebinDevKey": null + "PastebinDevKey": null, + "PastebinEnableUploads": true }, "MongoDB": { -- cgit From 242dc718cdedf2c7a264670008b9f760eba160d9 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 21 Dec 2019 15:41:55 -0500 Subject: switch to Azure Blob storage for saving files --- .../Framework/Clients/Pastebin/IPastebinClient.cs | 5 - .../Framework/Clients/Pastebin/PastebinClient.cs | 58 +---------- .../Framework/ConfigModels/ApiClientsConfig.cs | 26 +++-- .../Framework/Storage/IStorageProvider.cs | 2 +- src/SMAPI.Web/Framework/Storage/StorageProvider.cs | 108 +++++++++++---------- src/SMAPI.Web/SMAPI.Web.csproj | 1 + src/SMAPI.Web/Startup.cs | 4 +- src/SMAPI.Web/appsettings.Development.json | 7 +- src/SMAPI.Web/appsettings.json | 9 +- 9 files changed, 87 insertions(+), 133 deletions(-) (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs index a635abe3..431fed7b 100644 --- a/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs +++ b/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs @@ -9,10 +9,5 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin /// Fetch a saved paste. /// The paste ID. Task GetAsync(string id); - - /// Save a paste to Pastebin. - /// The paste name. - /// The paste content. - Task PostAsync(string name, string content); } } diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs index d695aab6..1be00be7 100644 --- a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs +++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; using System.Net; -using System.Net.Http; using System.Threading.Tasks; using Pathoschild.Http.Client; @@ -16,12 +14,6 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin /// The underlying HTTP client. private readonly IClient Client; - /// The user key used to authenticate with the Pastebin API. - private readonly string UserKey; - - /// The developer key used to authenticate with the Pastebin API. - private readonly string DevKey; - /********* ** Public methods @@ -29,13 +21,9 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin /// Construct an instance. /// The base URL for the Pastebin API. /// The user agent for the API client. - /// The user key used to authenticate with the Pastebin API. - /// The developer key used to authenticate with the Pastebin API. - public PastebinClient(string baseUrl, string userAgent, string userKey, string devKey) + public PastebinClient(string baseUrl, string userAgent) { this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent); - this.UserKey = userKey; - this.DevKey = devKey; } /// Fetch a saved paste. @@ -66,50 +54,6 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin } } - /// Save a paste to Pastebin. - /// The paste name. - /// The paste content. - public async Task PostAsync(string name, string content) - { - try - { - // validate - if (string.IsNullOrWhiteSpace(content)) - return new SavePasteResult { Error = "The log content can't be empty." }; - - // post to API - string response = await this.Client - .PostAsync("api/api_post.php") - .WithBody(p => p.FormUrlEncoded(new - { - api_option = "paste", - api_user_key = this.UserKey, - api_dev_key = this.DevKey, - api_paste_private = 1, // unlisted - api_paste_name = name, - api_paste_expire_date = "N", // never expire - api_paste_code = content - })) - .AsString(); - - // handle Pastebin errors - if (string.IsNullOrWhiteSpace(response)) - return new SavePasteResult { Error = "Received an empty response from Pastebin." }; - if (response.StartsWith("Bad API request")) - return new SavePasteResult { Error = response }; - if (!response.Contains("/")) - return new SavePasteResult { Error = $"Received an unknown response: {response}" }; - - // return paste ID - string pastebinID = response.Split("/").Last(); - return new SavePasteResult { Success = true, ID = pastebinID }; - } - catch (Exception ex) - { - return new SavePasteResult { Success = false, Error = ex.ToString() }; - } - } - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() { diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs index e88f351f..4a73750b 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs @@ -29,6 +29,19 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels public string AmazonTempBucket { get; set; } + /**** + ** Azure + ****/ + /// The connection string for the Azure Blob storage account. + public string AzureBlobConnectionString { get; set; } + + /// The Azure Blob container in which to store temporary uploaded logs. + public string AzureBlobTempContainer { get; set; } + + /// The number of days since the blob's last-modified date when it will be deleted. + public int AzureBlobTempExpiryDays { get; set; } + + /**** ** Chucklefish ****/ @@ -61,6 +74,7 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// The password with which to authenticate to the GitHub API (if any). public string GitHubPassword { get; set; } + /**** ** ModDrop ****/ @@ -70,6 +84,7 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// The URL for a ModDrop mod page for the user, where {0} is the mod ID. public string ModDropModPageUrl { get; set; } + /**** ** Nexus Mods ****/ @@ -85,20 +100,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// The Nexus API authentication key. public string NexusApiKey { get; set; } + /**** ** Pastebin ****/ /// The base URL for the Pastebin API. public string PastebinBaseUrl { get; set; } - - /// The user key used to authenticate with the Pastebin API. - public string PastebinUserKey { get; set; } - - /// The developer key used to authenticate with the Pastebin API. - public string PastebinDevKey { get; set; } - - /// Whether to enable uploading new files to Pastebin. This doesn't affect fetching already-uploaded files. - public bool PastebinEnableUploads { get; set; } - } } diff --git a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs index e222a235..12a5e421 100644 --- a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs @@ -5,7 +5,7 @@ namespace StardewModdingAPI.Web.Framework.Storage /// Provides access to raw data storage. internal interface IStorageProvider { - /// Save a text file to Pastebin or Amazon S3, if available. + /// Save a text file to storage. /// The display title, if applicable. /// The content to upload. /// Whether to gzip the text. diff --git a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs index e5e71325..b2d8ae7e 100644 --- a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs @@ -6,7 +6,9 @@ using Amazon; using Amazon.Runtime; using Amazon.S3; using Amazon.S3.Model; -using Amazon.S3.Transfer; +using Azure; +using Azure.Storage.Blobs; +using Azure.Storage.Blobs.Models; using Microsoft.Extensions.Options; using StardewModdingAPI.Web.Framework.Clients.Pastebin; using StardewModdingAPI.Web.Framework.Compression; @@ -44,54 +46,26 @@ namespace StardewModdingAPI.Web.Framework.Storage this.GzipHelper = gzipHelper; } - /// Save a text file to Pastebin or Amazon S3, if available. + /// Save a text file to storage. /// The display title, if applicable. /// The content to upload. /// Whether to gzip the text. /// Returns metadata about the save attempt. public async Task SaveAsync(string title, string content, bool compress = true) { - // save to PasteBin - string uploadError = null; - if (this.ClientsConfig.PastebinEnableUploads) - { - SavePasteResult result = await this.Pastebin.PostAsync(title, 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.AmazonTempBucket, - Key = $"uploads/{id}", - InputStream = stream, - Metadata = - { - // note: AWS will lowercase keys and prefix 'x-amz-meta-' - ["smapi-uploaded"] = DateTime.UtcNow.ToString("O"), - ["pastebin-error"] = uploadError - } - }; + BlobClient blob = this.GetAzureBlobClient(id); + await blob.UploadAsync(stream); - await uploader.UploadAsync(uploadRequest); - - return new UploadResult(true, id, uploadError); + return new UploadResult(true, id, null); } catch (Exception ex) { - return new UploadResult(false, null, $"{uploadError}\n{ex.Message}"); + return new UploadResult(false, null, ex.Message); } } @@ -99,35 +73,62 @@ namespace StardewModdingAPI.Web.Framework.Storage /// The storage ID returned by . public async Task GetAsync(string id) { - // get from Amazon S3 + // fetch from Azure/Amazon 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 Azure try { - using GetObjectResponse response = await s3.GetObjectAsync(this.ClientsConfig.AmazonTempBucket, $"uploads/{id}"); - using Stream responseStream = response.ResponseStream; - using StreamReader reader = new StreamReader(responseStream); + BlobClient blob = this.GetAzureBlobClient(id); + Response response = await blob.DownloadAsync(); + using BlobDownloadInfo result = response.Value; - DateTime expiry = response.Expiration.ExpiryDateUtc; - string pastebinError = response.Metadata["x-amz-meta-pastebin-error"]; + using StreamReader reader = new StreamReader(result.Content); + DateTimeOffset expiry = result.Details.LastModified + TimeSpan.FromDays(this.ClientsConfig.AzureBlobTempExpiryDays); string content = this.GzipHelper.DecompressString(reader.ReadToEnd()); return new StoredFileInfo { Success = true, Content = content, - Expiry = expiry, - Warning = pastebinError + Expiry = expiry.UtcDateTime }; } - catch (AmazonServiceException ex) + catch (RequestFailedException ex) { - return ex.ErrorCode == "NoSuchKey" - ? new StoredFileInfo { Error = "There's no file with that ID." } - : new StoredFileInfo { Error = $"Could not fetch that file from AWS S3 ({ex.ErrorCode}: {ex.Message})." }; + if (ex.ErrorCode != "BlobNotFound") + return new StoredFileInfo { Error = $"Could not fetch that file from storage ({ex.ErrorCode}: {ex.Message})." }; + } + + // try legacy Amazon S3 + { + 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.AmazonTempBucket, $"uploads/{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 StoredFileInfo + { + Success = true, + Content = content, + Expiry = expiry, + Warning = pastebinError + }; + } + catch (AmazonServiceException ex) + { + return ex.ErrorCode == "NoSuchKey" + ? new StoredFileInfo { Error = "There's no file with that ID." } + : new StoredFileInfo { Error = $"Could not fetch that file from AWS S3 ({ex.ErrorCode}: {ex.Message})." }; + } } } @@ -144,5 +145,14 @@ namespace StardewModdingAPI.Web.Framework.Storage }; } } + + /// Get a client for reading and writing to Azure Blob storage. + /// The file ID to fetch. + private BlobClient GetAzureBlobClient(string id) + { + var azure = new BlobServiceClient(this.ClientsConfig.AzureBlobConnectionString); + var container = azure.GetBlobContainerClient(this.ClientsConfig.AzureBlobTempContainer); + return container.GetBlobClient($"uploads/{id}"); + } } } diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 2c56fa75..773a7316 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -13,6 +13,7 @@ + diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 31b5e61d..07ee0c9e 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -153,9 +153,7 @@ namespace StardewModdingAPI.Web services.AddSingleton(new PastebinClient( baseUrl: api.PastebinBaseUrl, - userAgent: userAgent, - userKey: api.PastebinUserKey, - devKey: api.PastebinDevKey + userAgent: userAgent )); } diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index 74ded25d..05f0da1d 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -12,13 +12,12 @@ "AmazonAccessKey": null, "AmazonSecretKey": null, + "AzureBlobConnectionString": null, + "GitHubUsername": null, "GitHubPassword": null, - "NexusApiKey": null, - - "PastebinUserKey": null, - "PastebinDevKey": null + "NexusApiKey": null }, "MongoDB": { diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 6ba05a22..c6dc1b69 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -29,6 +29,10 @@ "AmazonRegion": "us-east-1", "AmazonTempBucket": "smapi-web-temp", + "AzureBlobConnectionString": null, + "AzureBlobTempContainer": "smapi-web-temp", + "AzureBlobTempExpiryDays": 30, + "ChucklefishBaseUrl": "https://community.playstarbound.com", "ChucklefishModPageUrlFormat": "resources/{0}", @@ -47,10 +51,7 @@ "NexusModUrlFormat": "mods/{0}", "NexusModScrapeUrlFormat": "mods/{0}?tab=files", - "PastebinBaseUrl": "https://pastebin.com/", - "PastebinUserKey": null, - "PastebinDevKey": null, - "PastebinEnableUploads": true + "PastebinBaseUrl": "https://pastebin.com/" }, "MongoDB": { -- cgit From ba46491ebc305a66a0a3a1f3cefa8bb677e57ff7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 21 Dec 2019 23:14:15 -0500 Subject: drop Amazon S3 support --- docs/release-notes.md | 2 +- .../Framework/ConfigModels/ApiClientsConfig.cs | 16 --------- src/SMAPI.Web/Framework/Storage/StorageProvider.cs | 41 +++------------------- src/SMAPI.Web/SMAPI.Web.csproj | 1 - src/SMAPI.Web/appsettings.Development.json | 3 -- src/SMAPI.Web/appsettings.json | 5 --- 6 files changed, 6 insertions(+), 62 deletions(-) (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/docs/release-notes.md b/docs/release-notes.md index fa64a037..591f4cc2 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,7 +13,7 @@ * For the web UI: * Added option to edit & reupload in the JSON validator. - * If a JSON validator upload can't be saved to Pastebin (e.g. due to rate limits), it's now uploaded to Amazon S3 instead. Files uploaded to S3 expire after one month. + * The log parser and JSON validator no longer save files to Pastebin due to ongoing performance issues. All files are now saved to Azure Blob storage instead and expire after one month. * Updated the JSON validator for Content Patcher 1.10 and 1.11. * Fixed JSON validator no longer letting you change format when viewing results. diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs index 4a73750b..878130bf 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs @@ -13,22 +13,6 @@ 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 AmazonTempBucket { get; set; } - - /**** ** Azure ****/ diff --git a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs index b2d8ae7e..12a35f18 100644 --- a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs @@ -2,10 +2,6 @@ using System; using System.IO; using System.Text; using System.Threading.Tasks; -using Amazon; -using Amazon.Runtime; -using Amazon.S3; -using Amazon.S3.Model; using Azure; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; @@ -96,39 +92,12 @@ namespace StardewModdingAPI.Web.Framework.Storage } catch (RequestFailedException ex) { - if (ex.ErrorCode != "BlobNotFound") - return new StoredFileInfo { Error = $"Could not fetch that file from storage ({ex.ErrorCode}: {ex.Message})." }; - } - - // try legacy Amazon S3 - { - 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.AmazonTempBucket, $"uploads/{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 StoredFileInfo - { - Success = true, - Content = content, - Expiry = expiry, - Warning = pastebinError - }; - } - catch (AmazonServiceException ex) + return new StoredFileInfo { - return ex.ErrorCode == "NoSuchKey" - ? new StoredFileInfo { Error = "There's no file with that ID." } - : new StoredFileInfo { Error = $"Could not fetch that file from AWS S3 ({ex.ErrorCode}: {ex.Message})." }; - } + Error = ex.ErrorCode == "BlobNotFound" + ? "There's no file with that ID." + : $"Could not fetch that file from storage ({ex.ErrorCode}: {ex.Message})." + }; } } diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 773a7316..22f5e975 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -12,7 +12,6 @@ - diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index 05f0da1d..3c2001ef 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -9,9 +9,6 @@ */ { "ApiClients": { - "AmazonAccessKey": null, - "AmazonSecretKey": null, - "AzureBlobConnectionString": null, "GitHubUsername": null, diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index c6dc1b69..0f61ebb9 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -24,11 +24,6 @@ "ApiClients": { "UserAgent": "SMAPI/{0} (+https://smapi.io)", - "AmazonAccessKey": null, - "AmazonSecretKey": null, - "AmazonRegion": "us-east-1", - "AmazonTempBucket": "smapi-web-temp", - "AzureBlobConnectionString": null, "AzureBlobTempContainer": "smapi-web-temp", "AzureBlobTempExpiryDays": 30, -- cgit From 082f285bc7ce156ad0750bb48d46ed65a2e4aedb Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 22 Dec 2019 00:44:13 -0500 Subject: streamline local environments, update technical docs & privacy page --- docs/release-notes.md | 2 +- docs/technical/web.md | 69 ++++++++++++---------- .../Framework/Clients/Pastebin/PasteInfo.cs | 2 - .../Framework/Clients/Pastebin/SavePasteResult.cs | 15 ----- .../Framework/ConfigModels/MongoDbConfig.cs | 6 ++ src/SMAPI.Web/SMAPI.Web.csproj | 2 + src/SMAPI.Web/Startup.cs | 25 +++++++- src/SMAPI.Web/Views/Index/Privacy.cshtml | 8 +-- src/SMAPI.Web/appsettings.Development.json | 6 +- src/SMAPI.Web/wwwroot/Content/js/json-validator.js | 6 +- 10 files changed, 81 insertions(+), 60 deletions(-) delete mode 100644 src/SMAPI.Web/Framework/Clients/Pastebin/SavePasteResult.cs (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/docs/release-notes.md b/docs/release-notes.md index bbe08c13..bae1cb98 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -27,7 +27,7 @@ * Fixed asset propagation for `Characters\Farmer\farmer_girl_base_bald`. * For SMAPI developers: - * You can now run local environments without configuring Amazon, Azure, and Pastebin accounts. + * You can now run local environments without configuring Amazon, Azure, MongoDB, and Pastebin accounts. ## 3.0.1 Released 02 December 2019 for Stardew Valley 1.4.0.1. diff --git a/docs/technical/web.md b/docs/technical/web.md index 97e0704a..697ec280 100644 --- a/docs/technical/web.md +++ b/docs/technical/web.md @@ -10,17 +10,21 @@ and update check API. * [Short URLs](#short-urls) * [For SMAPI developers](#for-smapi-developers) * [Local development](#local-development) - * [Deploying to Amazon Beanstalk](#deploying-to-amazon-beanstalk) + * [Production environment](#production-environment) ## 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://smapi.io/log. +The log parser at https://smapi.io/log provides a web UI for uploading, parsing, and sharing SMAPI +logs. + +The logs are saved in a compressed form to Amazon Blob storage for 30 days. ## 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://smapi.io/json. +The JSON validator at https://smapi.io/json 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 logs are saved in a compressed form to Amazon Blob storage for 30 days. ### Schema file format Schema files are defined in `wwwroot/schemas` using the [JSON Schema](https://json-schema.org/) @@ -336,43 +340,46 @@ short url | → | target page A local environment lets you run a complete copy of the web project (including cache database) on your machine, with no external dependencies aside from the actual mod sites. -Initial setup: - -1. [Install MongoDB](https://docs.mongodb.com/manual/administration/install-community/) and add its - `bin` folder to the system PATH. -2. Create a local folder for the MongoDB data (e.g. `C:\dev\smapi-cache`). -3. Enter your credentials in the `appsettings.Development.json` file. You can leave the MongoDB - credentials as-is to use the default local instance; see the next section for the other settings. - -To launch the environment: -1. Launch MongoDB from a terminal (change the data path if applicable): - ```sh - 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.) +1. Enter the Nexus credentials in `appsettings.Development.json` . You can leave the other + credentials empty to default to fetching data anonymously, and storing data in-memory and + on disk. +2. Launch `SMAPI.Web` from Visual Studio to run a local version of the site. ### Production environment A production environment includes the web servers and cache database hosted online for public -access. This section assumes you're creating a new production environment from scratch (not using -the official live environment). +access. + +This section assumes you're creating a new environment on Azure, but the app isn't tied to any +Azure services. If you want to host it on a different site, you'll need to adjust the instructions +accordingly. Initial setup: -1. Launch an empty MongoDB server (e.g. using [MongoDB Atlas](https://www.mongodb.com/cloud/atlas)). -2. Create an AWS Beanstalk .NET environment with these environment properties: +1. Launch an empty MongoDB server (e.g. using [MongoDB Atlas](https://www.mongodb.com/cloud/atlas)) + for mod data. +2. Create an Azure Blob storage account for uploaded files. +3. Create an Azure App Services environment running the latest .NET Core on Linux or Windows. +4. Add these application settings in the new App Services environment: property name | description ------------------------------- | ----------------- - `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. - `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. + `ApiClients.AzureBlobConnectionString` | The connection string for the Azure Blob storage account created in step 2. + `ApiClients.GitHubUsername`
      `ApiClients.GitHubPassword` | The login credentials for the GitHub account with which to fetch release info. If these are omitted, GitHub will impose much stricter rate limits. + `ApiClients:NexusApiKey` | The [Nexus API authentication key](https://github.com/Pathoschild/FluentNexus#init-a-client). `MongoDB:Host` | The hostname for the MongoDB instance. `MongoDB:Username` | The login username for the MongoDB instance. `MongoDB:Password` | The login password for the MongoDB instance. - `MongoDB:Database` | The database name (e.g. `smapi` in production or `smapi-edge` in testing environments). + `MongoDB:Database` | The MongoDB database name (e.g. `smapi` in production or `smapi-edge` in testing environments). + + Optional settings: + + property name | description + ------------------------------- | ----------------- + `BackgroundServices:Enabled` | Set to `true` to enable background processes like fetching data from the wiki, or false to disable them. + `Site:BetaEnabled` | Set to `true` to show a separate download button if there's a beta version of SMAPI in its GitHub releases. + `Site:BetaBlurb` | If `Site:BetaEnabled` is true and there's a beta version of SMAPI in its GitHub releases, this is shown on the beta download button as explanatory subtext. + `Site:SupporterList` | A list of Patreon supports to credit on the download page. To deploy updates: -1. Deploy the web project using [AWS Toolkit for Visual Studio](https://aws.amazon.com/visualstudio/). +1. [Deploy the web project from Visual Studio](https://docs.microsoft.com/en-us/visualstudio/deployment/quickstart-deploy-to-azure). 2. If the MongoDB schema changed, delete the MongoDB database. (It'll be recreated automatically.) diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs index 1ef3ef12..813ea115 100644 --- a/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs +++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs @@ -1,5 +1,3 @@ -using System; - namespace StardewModdingAPI.Web.Framework.Clients.Pastebin { /// The response for a get-paste request. diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/SavePasteResult.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/SavePasteResult.cs deleted file mode 100644 index 89dab697..00000000 --- a/src/SMAPI.Web/Framework/Clients/Pastebin/SavePasteResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace StardewModdingAPI.Web.Framework.Clients.Pastebin -{ - /// The response for a save-log request. - internal class SavePasteResult - { - /// Whether the log was successfully saved. - public bool Success { get; set; } - - /// The saved paste ID (if is true). - public string ID { get; set; } - - /// The error message (if saving failed). - public string Error { get; set; } - } -} diff --git a/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs index 3c508300..e2e18477 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs @@ -24,6 +24,12 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /********* ** Public method *********/ + /// Get whether a MongoDB instance is configured. + public bool IsConfigured() + { + return !string.IsNullOrWhiteSpace(this.Host); + } + /// Get the MongoDB connection string. public string GetConnectionString() { diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 22f5e975..504254cd 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -14,6 +14,7 @@ + @@ -23,6 +24,7 @@ + diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 07ee0c9e..338cd2d5 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -1,5 +1,7 @@ +using System; using System.Collections.Generic; using Hangfire; +using Hangfire.MemoryStorage; using Hangfire.Mongo; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -8,6 +10,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Mongo2Go; using MongoDB.Bson.Serialization; using MongoDB.Driver; using Newtonsoft.Json; @@ -89,10 +92,20 @@ namespace StardewModdingAPI.Web } // init MongoDB + services.AddSingleton(serv => !mongoConfig.IsConfigured() + ? MongoDbRunner.Start() + : throw new InvalidOperationException("The MongoDB connection is configured, so the local development version should not be used.") + ); services.AddSingleton(serv => { + // get connection string + string connectionString = mongoConfig.IsConfigured() + ? mongoConfig.GetConnectionString() + : serv.GetRequiredService().ConnectionString; + + // get client BsonSerializer.RegisterSerializer(new UtcDateTimeOffsetSerializer()); - return new MongoClient(mongoConfig.GetConnectionString()).GetDatabase(mongoConfig.Database); + return new MongoClient(connectionString).GetDatabase(mongoConfig.Database); }); services.AddSingleton(serv => new ModCacheRepository(serv.GetRequiredService())); services.AddSingleton(serv => new WikiCacheRepository(serv.GetRequiredService())); @@ -104,12 +117,18 @@ namespace StardewModdingAPI.Web config .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseSimpleAssemblyNameTypeSerializer() - .UseRecommendedSerializerSettings() - .UseMongoStorage(mongoConfig.GetConnectionString(), $"{mongoConfig.Database}-hangfire", new MongoStorageOptions + .UseRecommendedSerializerSettings(); + + if (mongoConfig.IsConfigured()) + { + config.UseMongoStorage(mongoConfig.GetConnectionString(), $"{mongoConfig.Database}-hangfire", new MongoStorageOptions { MigrationOptions = new MongoMigrationOptions(MongoMigrationStrategy.Drop), CheckConnection = false // error on startup takes down entire process }); + } + else + config.UseMemoryStorage(); }); // init API clients diff --git a/src/SMAPI.Web/Views/Index/Privacy.cshtml b/src/SMAPI.Web/Views/Index/Privacy.cshtml index 7327de3d..fd78f908 100644 --- a/src/SMAPI.Web/Views/Index/Privacy.cshtml +++ b/src/SMAPI.Web/Views/Index/Privacy.cshtml @@ -22,10 +22,10 @@

      Data collected and transmitted

      Web logging

      -

      This website and SMAPI's web API are hosted by Amazon Web Services. Their servers may automatically collect diagnostics like your IP address, but this information is not visible to SMAPI's web application or developers. For more information, see the Amazon Privacy Notice.

      +

      This website and SMAPI's web API are hosted on Microsoft Azure. Their servers may automatically collect diagnostics like your IP address, but this information is not visible to SMAPI's web apps or its developers. For more information, see the Microsoft Azure legal resources.

      Update checks

      -

      SMAPI notifies you when there's a new version of SMAPI or your mods available. To do so, it sends your game/SMAPI/mod versions and platform type to its web API. No personal information is stored by the web application, but see web logging.

      +

      SMAPI notifies you when there's a new version of SMAPI or your mods available. To do so, it sends basic metadata like your game/SMAPI/mod versions and platform type to its web API. No personal information is stored by the web app.

      You can disable update checks, and no information will be transmitted to the web API. To do so:

        @@ -34,8 +34,8 @@
      1. change "CheckForUpdates": true to "CheckForUpdates": false.
      -

      Log parser

      -

      The log parser page lets you store a log file for analysis and sharing. The log data is stored indefinitely in an obfuscated form as unlisted pastes in Pastebin. No personal information is stored by the log parser beyond what you choose to upload, but see web logging and the Pastebin Privacy Statement.

      +

      Log parser and JSON validator

      +

      The log parser and JSON validator let you upload files to analyze and share with other users. The log data is stored for 30 days in an obfuscated form in a private Microsoft Azure Blob storage account. No personal information is stored by the log parser beyond what you choose to upload as part of those files.

      Multiplayer sync

      As part of its multiplayer API, SMAPI transmits basic context to players you connect to (mainly your OS, SMAPI version, game version, and installed mods). This is used to enable multiplayer features like inter-mod messages, compatibility checks, etc. Although this information is normally hidden from players, it may be visible due to mods or configuration changes.

      diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index 3c2001ef..a6e48c69 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -18,9 +18,13 @@ }, "MongoDB": { - "Host": "localhost", + "Host": null, "Username": null, "Password": null, "Database": "smapi-edge" + }, + + "BackgroundServices": { + "Enabled": true } } diff --git a/src/SMAPI.Web/wwwroot/Content/js/json-validator.js b/src/SMAPI.Web/wwwroot/Content/js/json-validator.js index 401efbee..72b8192b 100644 --- a/src/SMAPI.Web/wwwroot/Content/js/json-validator.js +++ b/src/SMAPI.Web/wwwroot/Content/js/json-validator.js @@ -71,9 +71,9 @@ smapi.LineNumberRange = function (maxLines) { /** * UI logic for the JSON validator page. * @param {string} urlFormat The URL format for a file, with $schemaName and $id placeholders. - * @param {string} pasteID The Pastebin paste ID for the content being viewed, if any. + * @param {string} fileId The file ID for the content being viewed, if any. */ -smapi.jsonValidator = function (urlFormat, pasteID) { +smapi.jsonValidator = function (urlFormat, fileId) { /** * The original content element. */ @@ -138,7 +138,7 @@ smapi.jsonValidator = function (urlFormat, pasteID) { // change format $("#output #format").on("change", function() { var schemaName = $(this).val(); - location.href = urlFormat.replace("$schemaName", schemaName).replace("$id", pasteID); + location.href = urlFormat.replace("$schemaName", schemaName).replace("$id", fileId); }); // upload form -- cgit From d6ef6f627ae049c29c2241d39261dee7de3da663 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 22 Dec 2019 12:08:01 -0500 Subject: configure MongoDB connection string directly --- docs/technical/web.md | 4 +--- .../Framework/ConfigModels/MongoDbConfig.cs | 25 +++------------------- src/SMAPI.Web/Startup.cs | 4 ++-- src/SMAPI.Web/appsettings.Development.json | 4 +--- src/SMAPI.Web/appsettings.json | 6 ++---- 5 files changed, 9 insertions(+), 34 deletions(-) (limited to 'src/SMAPI.Web/Framework/ConfigModels') diff --git a/docs/technical/web.md b/docs/technical/web.md index 697ec280..67e86c8b 100644 --- a/docs/technical/web.md +++ b/docs/technical/web.md @@ -366,9 +366,7 @@ Initial setup: `ApiClients.AzureBlobConnectionString` | The connection string for the Azure Blob storage account created in step 2. `ApiClients.GitHubUsername`
      `ApiClients.GitHubPassword` | The login credentials for the GitHub account with which to fetch release info. If these are omitted, GitHub will impose much stricter rate limits. `ApiClients:NexusApiKey` | The [Nexus API authentication key](https://github.com/Pathoschild/FluentNexus#init-a-client). - `MongoDB:Host` | The hostname for the MongoDB instance. - `MongoDB:Username` | The login username for the MongoDB instance. - `MongoDB:Password` | The login password for the MongoDB instance. + `MongoDB:ConnectionString` | The connection string for the MongoDB instance. `MongoDB:Database` | The MongoDB database name (e.g. `smapi` in production or `smapi-edge` in testing environments). Optional settings: diff --git a/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs index e2e18477..c7b6cb00 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/MongoDbConfig.cs @@ -1,5 +1,3 @@ -using System; - namespace StardewModdingAPI.Web.Framework.ConfigModels { /// The config settings for mod compatibility list. @@ -8,14 +6,8 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /********* ** Accessors *********/ - /// The MongoDB hostname. - public string Host { get; set; } - - /// The MongoDB username (if any). - public string Username { get; set; } - - /// The MongoDB password (if any). - public string Password { get; set; } + /// The MongoDB connection string. + public string ConnectionString { get; set; } /// The database name. public string Database { get; set; } @@ -27,18 +19,7 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// Get whether a MongoDB instance is configured. public bool IsConfigured() { - return !string.IsNullOrWhiteSpace(this.Host); - } - - /// Get the MongoDB connection string. - public string GetConnectionString() - { - bool isLocal = this.Host == "localhost"; - bool hasLogin = !string.IsNullOrWhiteSpace(this.Username) && !string.IsNullOrWhiteSpace(this.Password); - - return $"mongodb{(isLocal ? "" : "+srv")}://" - + (hasLogin ? $"{Uri.EscapeDataString(this.Username)}:{Uri.EscapeDataString(this.Password)}@" : "") - + $"{this.Host}/{this.Database}?retryWrites=true&w=majority"; + return !string.IsNullOrWhiteSpace(this.ConnectionString); } } } diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 338cd2d5..29086472 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -100,7 +100,7 @@ namespace StardewModdingAPI.Web { // get connection string string connectionString = mongoConfig.IsConfigured() - ? mongoConfig.GetConnectionString() + ? mongoConfig.ConnectionString : serv.GetRequiredService().ConnectionString; // get client @@ -121,7 +121,7 @@ namespace StardewModdingAPI.Web if (mongoConfig.IsConfigured()) { - config.UseMongoStorage(mongoConfig.GetConnectionString(), $"{mongoConfig.Database}-hangfire", new MongoStorageOptions + config.UseMongoStorage(mongoConfig.ConnectionString, $"{mongoConfig.Database}-hangfire", new MongoStorageOptions { MigrationOptions = new MongoMigrationOptions(MongoMigrationStrategy.Drop), CheckConnection = false // error on startup takes down entire process diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index a6e48c69..54460c46 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -18,9 +18,7 @@ }, "MongoDB": { - "Host": null, - "Username": null, - "Password": null, + "ConnectionString": null, "Database": "smapi-edge" }, diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 0f61ebb9..caeb381f 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -50,10 +50,8 @@ }, "MongoDB": { - "Host": null, - "Username": null, - "Password": null, - "Database": null + "ConnectionString": null, + "Database": "smapi" }, "ModCompatibilityList": { -- cgit