diff options
Diffstat (limited to 'src/SMAPI.Web/Framework')
5 files changed, 92 insertions, 50 deletions
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 { /// <summary>The response for a get-paste request.</summary> 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 -{ - /// <summary>The response for a save-log request.</summary> - internal class SavePasteResult - { - /// <summary>Whether the log was successfully saved.</summary> - public bool Success { get; set; } - - /// <summary>The saved paste ID (if <see cref="Success"/> is <c>true</c>).</summary> - public string ID { get; set; } - - /// <summary>The error message (if saving failed).</summary> - 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 *********/ + /// <summary>Get whether a MongoDB instance is configured.</summary> + public bool IsConfigured() + { + return !string.IsNullOrWhiteSpace(this.Host); + } + /// <summary>Get the MongoDB connection string.</summary> public string GetConnectionString() { diff --git a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs index 12a5e421..96a34fbb 100644 --- a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs @@ -6,11 +6,10 @@ namespace StardewModdingAPI.Web.Framework.Storage internal interface IStorageProvider { /// <summary>Save a text file to storage.</summary> - /// <param name="title">The display title, if applicable.</param> /// <param name="content">The content to upload.</param> /// <param name="compress">Whether to gzip the text.</param> /// <returns>Returns metadata about the save attempt.</returns> - Task<UploadResult> SaveAsync(string title, string content, bool compress = true); + Task<UploadResult> SaveAsync(string content, bool compress = true); /// <summary>Fetch raw text from storage.</summary> /// <param name="id">The storage ID returned by <see cref="SaveAsync"/>.</param> diff --git a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs index 12a35f18..35538443 100644 --- a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs @@ -27,6 +27,12 @@ namespace StardewModdingAPI.Web.Framework.Storage /// <summary>The underlying text compression helper.</summary> private readonly IGzipHelper GzipHelper; + /// <summary>Whether Azure blob storage is configured.</summary> + private bool HasAzure => !string.IsNullOrWhiteSpace(this.ClientsConfig.AzureBlobConnectionString); + + /// <summary>The number of days since the blob's last-modified date when it will be deleted.</summary> + private int ExpiryDays => this.ClientsConfig.AzureBlobTempExpiryDays; + /********* ** Public methods @@ -43,25 +49,38 @@ namespace StardewModdingAPI.Web.Framework.Storage } /// <summary>Save a text file to storage.</summary> - /// <param name="title">The display title, if applicable.</param> /// <param name="content">The content to upload.</param> /// <param name="compress">Whether to gzip the text.</param> /// <returns>Returns metadata about the save attempt.</returns> - public async Task<UploadResult> SaveAsync(string title, string content, bool compress = true) + public async Task<UploadResult> SaveAsync(string content, bool compress = true) { - try - { - using Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); - string id = Guid.NewGuid().ToString("N"); + string id = Guid.NewGuid().ToString("N"); - BlobClient blob = this.GetAzureBlobClient(id); - await blob.UploadAsync(stream); + // save to Azure + if (this.HasAzure) + { + try + { + using Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); + BlobClient blob = this.GetAzureBlobClient(id); + await blob.UploadAsync(stream); - return new UploadResult(true, id, null); + return new UploadResult(true, id, null); + } + catch (Exception ex) + { + return new UploadResult(false, null, ex.Message); + } } - catch (Exception ex) + + // save to local filesystem for testing + else { - return new UploadResult(false, null, ex.Message); + string path = this.GetDevFilePath(id); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + File.WriteAllText(path, content); + return new UploadResult(true, id, null); } } @@ -69,39 +88,67 @@ namespace StardewModdingAPI.Web.Framework.Storage /// <param name="id">The storage ID returned by <see cref="SaveAsync"/>.</param> public async Task<StoredFileInfo> GetAsync(string id) { - // fetch from Azure/Amazon + // fetch from blob storage if (Guid.TryParseExact(id, "N", out Guid _)) { - // try Azure - try + // Azure Blob storage + if (this.HasAzure) { - BlobClient blob = this.GetAzureBlobClient(id); - Response<BlobDownloadInfo> response = await blob.DownloadAsync(); - using BlobDownloadInfo result = response.Value; + try + { + BlobClient blob = this.GetAzureBlobClient(id); + Response<BlobDownloadInfo> response = await blob.DownloadAsync(); + using BlobDownloadInfo result = response.Value; - using StreamReader reader = new StreamReader(result.Content); - DateTimeOffset expiry = result.Details.LastModified + TimeSpan.FromDays(this.ClientsConfig.AzureBlobTempExpiryDays); - string content = this.GzipHelper.DecompressString(reader.ReadToEnd()); + using StreamReader reader = new StreamReader(result.Content); + DateTimeOffset expiry = result.Details.LastModified + TimeSpan.FromDays(this.ExpiryDays); + string content = this.GzipHelper.DecompressString(reader.ReadToEnd()); - return new StoredFileInfo + return new StoredFileInfo + { + Success = true, + Content = content, + Expiry = expiry.UtcDateTime + }; + } + catch (RequestFailedException ex) { - Success = true, - Content = content, - Expiry = expiry.UtcDateTime - }; + return new StoredFileInfo + { + Error = ex.ErrorCode == "BlobNotFound" + ? "There's no file with that ID." + : $"Could not fetch that file from storage ({ex.ErrorCode}: {ex.Message})." + }; + } } - catch (RequestFailedException ex) + + // local filesystem for testing + else { + FileInfo file = new FileInfo(this.GetDevFilePath(id)); + if (file.Exists) + { + if (file.LastWriteTimeUtc.AddDays(this.ExpiryDays) < DateTime.UtcNow) + file.Delete(); + else + { + return new StoredFileInfo + { + Success = true, + Content = File.ReadAllText(file.FullName), + Expiry = DateTime.UtcNow.AddDays(this.ExpiryDays), + Warning = "This file was saved temporarily to the local computer. This should only happen in a local development environment." + }; + } + } return new StoredFileInfo { - Error = ex.ErrorCode == "BlobNotFound" - ? "There's no file with that ID." - : $"Could not fetch that file from storage ({ex.ErrorCode}: {ex.Message})." + Error = "There's no file with that ID." }; } } - // get from PasteBin + // get from Pastebin else { PasteInfo response = await this.Pastebin.GetAsync(id); @@ -116,12 +163,19 @@ namespace StardewModdingAPI.Web.Framework.Storage } /// <summary>Get a client for reading and writing to Azure Blob storage.</summary> - /// <param name="id">The file ID to fetch.</param> + /// <param name="id">The file ID.</param> private BlobClient GetAzureBlobClient(string id) { var azure = new BlobServiceClient(this.ClientsConfig.AzureBlobConnectionString); var container = azure.GetBlobContainerClient(this.ClientsConfig.AzureBlobTempContainer); return container.GetBlobClient($"uploads/{id}"); } + + /// <summary>Get the absolute file path for an upload when running in a local test environment with no Azure account configured.</summary> + /// <param name="id">The file ID.</param> + private string GetDevFilePath(string id) + { + return Path.Combine(Path.GetTempPath(), "smapi-web-temp", $"{id}.txt"); + } } } |