From 4189e2f3faa9197e83aebd32fc0af93d46ee1a61 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 13 Sep 2020 11:59:17 -0400 Subject: add support for renewing uploaded JSON/log files --- .../Controllers/JsonValidatorController.cs | 8 ++-- src/SMAPI.Web/Controllers/LogParserController.cs | 5 +- .../Framework/Storage/IStorageProvider.cs | 3 +- src/SMAPI.Web/Framework/Storage/StorageProvider.cs | 53 +++++++++++++--------- src/SMAPI.Web/Views/JsonValidator/Index.cshtml | 2 +- src/SMAPI.Web/Views/LogParser/Index.cshtml | 2 +- 6 files changed, 44 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs index 6ba97749..c77a3036 100644 --- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs +++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs @@ -58,7 +58,7 @@ namespace StardewModdingAPI.Web.Controllers /// Render the schema validator UI. /// The schema name with which to validate the JSON, or 'edit' to return to the edit screen. /// The stored file ID. - /// The operation to perform for the selected log ID. This can be 'edit', or any other value to view. + /// The operation to perform for the selected log ID. This can be 'edit', 'renew', or any other value to view. [HttpGet] [Route("json")] [Route("json/{schemaName}")] @@ -68,8 +68,10 @@ namespace StardewModdingAPI.Web.Controllers { // parse arguments schemaName = this.NormalizeSchemaName(schemaName); + operation = operation?.Trim().ToLower(); bool hasId = !string.IsNullOrWhiteSpace(id); - bool isEditView = !hasId || operation?.Trim().ToLower() == "edit"; + bool isEditView = !hasId || operation == "edit"; + bool renew = operation == "renew"; // build result model var result = this.GetModel(id, schemaName, isEditView); @@ -77,7 +79,7 @@ namespace StardewModdingAPI.Web.Controllers return this.View("Index", result); // fetch raw JSON - StoredFileInfo file = await this.Storage.GetAsync(id); + StoredFileInfo file = await this.Storage.GetAsync(id, renew); if (string.IsNullOrWhiteSpace(file.Content)) return this.View("Index", result.SetUploadError("The JSON file seems to be empty.")); result.SetContent(file.Content, expiry: file.Expiry, uploadWarning: file.Warning); diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 97c419d9..39de4b5d 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -40,17 +40,18 @@ namespace StardewModdingAPI.Web.Controllers /// Render the log parser UI. /// The stored file ID. /// Whether to display the raw unparsed log. + /// Whether to reset the log expiry. [HttpGet] [Route("log")] [Route("log/{id}")] - public async Task Index(string id = null, bool raw = false) + public async Task Index(string id = null, bool raw = false, bool renew = false) { // fresh page if (string.IsNullOrWhiteSpace(id)) return this.View("Index", this.GetModel(id)); // log page - StoredFileInfo file = await this.Storage.GetAsync(id); + StoredFileInfo file = await this.Storage.GetAsync(id, renew); ParsedLog log = file.Success ? new LogParser().Parse(file.Content) : new ParsedLog { IsValid = false, Error = file.Error }; diff --git a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs index 96a34fbb..dfc1fb47 100644 --- a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs @@ -13,6 +13,7 @@ namespace StardewModdingAPI.Web.Framework.Storage /// Fetch raw text from storage. /// The storage ID returned by . - Task GetAsync(string id); + /// Whether to reset the file expiry. + Task GetAsync(string id, bool renew); } } diff --git a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs index 35538443..c6f8bac1 100644 --- a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs +++ b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; @@ -48,10 +49,7 @@ namespace StardewModdingAPI.Web.Framework.Storage this.GzipHelper = gzipHelper; } - /// Save a text file to storage. - /// The content to upload. - /// Whether to gzip the text. - /// Returns metadata about the save attempt. + /// public async Task SaveAsync(string content, bool compress = true) { string id = Guid.NewGuid().ToString("N"); @@ -84,9 +82,8 @@ namespace StardewModdingAPI.Web.Framework.Storage } } - /// Fetch raw text from storage. - /// The storage ID returned by . - public async Task GetAsync(string id) + /// + public async Task GetAsync(string id, bool renew) { // fetch from blob storage if (Guid.TryParseExact(id, "N", out Guid _)) @@ -96,14 +93,21 @@ namespace StardewModdingAPI.Web.Framework.Storage { try { + // get client BlobClient blob = this.GetAzureBlobClient(id); + + // extend expiry + if (renew) + await blob.SetMetadataAsync(new Dictionary { ["expiryRenewed"] = DateTime.UtcNow.ToString("O") }); // change the blob's last-modified date (the specific property set doesn't matter) + + // fetch file Response response = await blob.DownloadAsync(); using BlobDownloadInfo result = response.Value; - using StreamReader reader = new StreamReader(result.Content); DateTimeOffset expiry = result.Details.LastModified + TimeSpan.FromDays(this.ExpiryDays); string content = this.GzipHelper.DecompressString(reader.ReadToEnd()); + // build model return new StoredFileInfo { Success = true, @@ -125,25 +129,32 @@ namespace StardewModdingAPI.Web.Framework.Storage // local filesystem for testing else { + // get file FileInfo file = new FileInfo(this.GetDevFilePath(id)); - if (file.Exists) + if (file.Exists && file.LastWriteTimeUtc.AddDays(this.ExpiryDays) < DateTime.UtcNow) // expired + file.Delete(); + if (!file.Exists) { - if (file.LastWriteTimeUtc.AddDays(this.ExpiryDays) < DateTime.UtcNow) - file.Delete(); - else + return new StoredFileInfo { - 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." - }; - } + Error = "There's no file with that ID." + }; } + + // renew + if (renew) + { + File.SetLastWriteTimeUtc(file.FullName, DateTime.UtcNow); + file.Refresh(); + } + + // build model return new StoredFileInfo { - Error = "There's no file with that ID." + 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." }; } } diff --git a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml index 7b89a23d..1db79857 100644 --- a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml +++ b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml @@ -76,7 +76,7 @@ else if (!Model.IsEditView && Model.PasteID != null) diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 71e12d47..d4ff4f10 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -78,7 +78,7 @@ else if (Model.ParsedLog?.IsValid == true) } -- cgit