summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2018-03-26 09:35:34 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2018-03-26 09:35:34 -0400
commit46141a7af21a921284bc82d49d888da864887d6e (patch)
treec71d17897377725f32653eacc65233f0b848f813 /src/SMAPI.Web
parentafb3c49bbaab07f3148f70d54f5140cdd83f8c20 (diff)
parent4d68ef3514de7deb357a0042d1af7ccf241ab5ff (diff)
downloadSMAPI-46141a7af21a921284bc82d49d888da864887d6e.tar.gz
SMAPI-46141a7af21a921284bc82d49d888da864887d6e.tar.bz2
SMAPI-46141a7af21a921284bc82d49d888da864887d6e.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Web')
-rw-r--r--src/SMAPI.Web/Controllers/IndexController.cs53
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs16
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs50
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs4
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs5
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs7
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs7
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs19
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs2
-rw-r--r--src/SMAPI.Web/StardewModdingAPI.Web.csproj14
-rw-r--r--src/SMAPI.Web/Startup.cs3
-rw-r--r--src/SMAPI.Web/ViewModels/IndexModel.cs28
-rw-r--r--src/SMAPI.Web/ViewModels/IndexVersionModel.cs41
-rw-r--r--src/SMAPI.Web/Views/Index/Index.cshtml35
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml34
-rw-r--r--src/SMAPI.Web/appsettings.json6
17 files changed, 235 insertions, 91 deletions
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs
index 5d45118f..0464e50a 100644
--- a/src/SMAPI.Web/Controllers/IndexController.cs
+++ b/src/SMAPI.Web/Controllers/IndexController.cs
@@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
+using StardewModdingAPI.Common;
using StardewModdingAPI.Web.Framework.Clients.GitHub;
using StardewModdingAPI.Web.ViewModels;
@@ -23,7 +24,10 @@ namespace StardewModdingAPI.Web.Controllers
private readonly IGitHubClient GitHub;
/// <summary>The cache time for release info.</summary>
- private readonly TimeSpan CacheTime = TimeSpan.FromMinutes(5);
+ private readonly TimeSpan CacheTime = TimeSpan.FromSeconds(1);
+
+ /// <summary>The GitHub repository name to check for update.</summary>
+ private readonly string RepositoryName = "Pathoschild/SMAPI";
/*********
@@ -42,17 +46,24 @@ namespace StardewModdingAPI.Web.Controllers
[HttpGet]
public async Task<ViewResult> Index()
{
- // fetch latest SMAPI release
- GitRelease release = await this.Cache.GetOrCreateAsync("latest-smapi-release", async entry =>
+ // fetch SMAPI releases
+ IndexVersionModel stableVersion = await this.Cache.GetOrCreateAsync("stable-version", async entry =>
+ {
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
+ GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false);
+ return new IndexVersionModel(release.Name, release.Body, this.GetMainDownloadUrl(release), this.GetDevDownloadUrl(release));
+ });
+ IndexVersionModel betaVersion = await this.Cache.GetOrCreateAsync("beta-version", async entry =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
- return await this.GitHub.GetLatestReleaseAsync("Pathoschild/SMAPI");
+ GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true);
+ return release.IsPrerelease
+ ? this.GetBetaDownload(release)
+ : null;
});
- string downloadUrl = this.GetMainDownloadUrl(release);
- string devDownloadUrl = this.GetDevDownloadUrl(release);
// render view
- var model = new IndexModel(release.Name, release.Body, downloadUrl, devDownloadUrl);
+ var model = new IndexModel(stableVersion, betaVersion);
return this.View(model);
}
@@ -89,5 +100,33 @@ namespace StardewModdingAPI.Web.Controllers
// fallback just in case
return "https://github.com/pathoschild/SMAPI/releases";
}
+
+ /// <summary>Get the latest beta download for a SMAPI release.</summary>
+ /// <param name="release">The SMAPI release.</param>
+ private IndexVersionModel GetBetaDownload(GitRelease release)
+ {
+ // get download with the latest version
+ SemanticVersionImpl latestVersion = null;
+ string latestUrl = null;
+ foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
+ {
+ // parse version
+ Match versionMatch = Regex.Match(asset.FileName, @"SMAPI-([\d\.]+(?:-.+)?)-installer.zip");
+ if (!versionMatch.Success || !SemanticVersionImpl.TryParse(versionMatch.Groups[1].Value, out SemanticVersionImpl version))
+ continue;
+
+ // save latest version
+ if (latestVersion == null || latestVersion.CompareTo(version) < 0)
+ {
+ latestVersion = version;
+ latestUrl = asset.DownloadUrl;
+ }
+ }
+
+ // return if prerelease
+ return latestVersion?.Tag != null
+ ? new IndexVersionModel(latestVersion.ToString(), release.Body, latestUrl, null)
+ : null;
+ }
}
}
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index abae7db7..24517263 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>The cache in which to store mod metadata.</summary>
private readonly IMemoryCache Cache;
- /// <summary>The number of minutes update checks should be cached before refetching them.</summary>
- private readonly int CacheMinutes;
+ /// <summary>The number of minutes successful update checks should be cached before refetching them.</summary>
+ private readonly int SuccessCacheMinutes;
+
+ /// <summary>The number of minutes failed update checks should be cached before refetching them.</summary>
+ private readonly int ErrorCacheMinutes;
/// <summary>A regex which matches SMAPI-style semantic version.</summary>
private readonly string VersionRegex;
@@ -50,7 +53,8 @@ namespace StardewModdingAPI.Web.Controllers
ModUpdateCheckConfig config = configProvider.Value;
this.Cache = cache;
- this.CacheMinutes = config.CacheMinutes;
+ this.SuccessCacheMinutes = config.SuccessCacheMinutes;
+ this.ErrorCacheMinutes = config.ErrorCacheMinutes;
this.VersionRegex = config.SemanticVersionRegex;
this.Repositories =
new IModRepository[]
@@ -115,13 +119,13 @@ namespace StardewModdingAPI.Web.Controllers
if (info.Error == null)
{
if (info.Version == null)
- info = new ModInfoModel(info.Name, info.Version, info.Url, "Mod has no version number.");
+ info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: "Mod has no version number.");
if (!allowInvalidVersions && !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))
- info = new ModInfoModel(info.Name, info.Version, info.Url, $"Mod has invalid semantic version '{info.Version}'.");
+ info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: $"Mod has invalid semantic version '{info.Version}'.");
}
// cache & return
- entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes);
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(info.Error == null ? this.SuccessCacheMinutes : this.ErrorCacheMinutes);
return info;
});
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
index 0b205660..4abe0737 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Pathoschild.Http.Client;
@@ -11,8 +12,11 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/*********
** Properties
*********/
- /// <summary>The URL for a GitHub releases API query excluding the base URL, where {0} is the repository owner and name.</summary>
- private readonly string ReleaseUrlFormat;
+ /// <summary>The URL for a GitHub API query for the latest stable release, excluding the base URL, where {0} is the organisation and project name.</summary>
+ private readonly string StableReleaseUrlFormat;
+
+ /// <summary>The URL for a GitHub API query for the latest release (including prerelease), excluding the base URL, where {0} is the organisation and project name.</summary>
+ private readonly string AnyReleaseUrlFormat;
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
@@ -23,14 +27,16 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
*********/
/// <summary>Construct an instance.</summary>
/// <param name="baseUrl">The base URL for the GitHub API.</param>
- /// <param name="releaseUrlFormat">The URL for a GitHub releases API query excluding the <paramref name="baseUrl"/>, where {0} is the repository owner and name.</param>
+ /// <param name="stableReleaseUrlFormat">The URL for a GitHub API query for the latest stable release, excluding the <paramref name="baseUrl"/>, where {0} is the organisation and project name.</param>
+ /// <param name="anyReleaseUrlFormat">The URL for a GitHub API query for the latest release (including prerelease), excluding the <paramref name="baseUrl"/>, where {0} is the organisation and project name.</param>
/// <param name="userAgent">The user agent for the API client.</param>
/// <param name="acceptHeader">The Accept header value expected by the GitHub API.</param>
/// <param name="username">The username with which to authenticate to the GitHub API.</param>
/// <param name="password">The password with which to authenticate to the GitHub API.</param>
- public GitHubClient(string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
+ public GitHubClient(string baseUrl, string stableReleaseUrlFormat, string anyReleaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
{
- this.ReleaseUrlFormat = releaseUrlFormat;
+ this.StableReleaseUrlFormat = stableReleaseUrlFormat;
+ this.AnyReleaseUrlFormat = anyReleaseUrlFormat;
this.Client = new FluentClient(baseUrl)
.SetUserAgent(userAgent)
@@ -41,18 +47,23 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// <summary>Get the latest release for a GitHub repository.</summary>
/// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
- /// <returns>Returns the latest release if found, else <c>null</c>.</returns>
- public async Task<GitRelease> GetLatestReleaseAsync(string repo)
+ /// <param name="includePrerelease">Whether to return a prerelease version if it's latest.</param>
+ /// <returns>Returns the release if found, else <c>null</c>.</returns>
+ public async Task<GitRelease> GetLatestReleaseAsync(string repo, bool includePrerelease = false)
{
- // validate key format
- if (!repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
- throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
-
- // fetch info
+ this.AssetKeyFormat(repo);
try
{
+ if (includePrerelease)
+ {
+ GitRelease[] results = await this.Client
+ .GetAsync(string.Format(this.AnyReleaseUrlFormat, repo))
+ .AsArray<GitRelease>();
+ return results.FirstOrDefault();
+ }
+
return await this.Client
- .GetAsync(string.Format(this.ReleaseUrlFormat, repo))
+ .GetAsync(string.Format(this.StableReleaseUrlFormat, repo))
.As<GitRelease>();
}
catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
@@ -66,5 +77,18 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
{
this.Client?.Dispose();
}
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Assert that a repository key is formatted correctly.</summary>
+ /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
+ /// <exception cref="ArgumentException">The repository key is invalid.</exception>
+ private void AssetKeyFormat(string repo)
+ {
+ if (repo == null || !repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
+ throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
+ }
}
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
index b944088d..827374fb 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
@@ -19,6 +19,10 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// <summary>The Markdown description for the release.</summary>
public string Body { get; set; }
+ /// <summary>Whether this is a prerelease version.</summary>
+ [JsonProperty("prerelease")]
+ public bool IsPrerelease { get; set; }
+
/// <summary>The attached files.</summary>
public GitAsset[] Assets { get; set; }
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
index 6e8eadff..9519c26f 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
@@ -11,7 +11,8 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
*********/
/// <summary>Get the latest release for a GitHub repository.</summary>
/// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
- /// <returns>Returns the latest release if found, else <c>null</c>.</returns>
- Task<GitRelease> GetLatestReleaseAsync(string repo);
+ /// <param name="includePrerelease">Whether to return a prerelease version if it's latest.</param>
+ /// <returns>Returns the release if found, else <c>null</c>.</returns>
+ Task<GitRelease> GetLatestReleaseAsync(string repo, bool includePrerelease = false);
}
}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
index 61219414..de6c024a 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
@@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/// <summary>The base URL for the GitHub API.</summary>
public string GitHubBaseUrl { get; set; }
- /// <summary>The URL for a GitHub API latest-release query excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
- public string GitHubReleaseUrlFormat { get; set; }
+ /// <summary>The URL for a GitHub API query for the latest stable release, excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
+ public string GitHubStableReleaseUrlFormat { get; set; }
+
+ /// <summary>The URL for a GitHub API query for the latest release (including prerelease), excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
+ public string GitHubAnyReleaseUrlFormat { get; set; }
/// <summary>The Accept header value expected by the GitHub API.</summary>
public string GitHubAcceptHeader { get; set; }
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
index 58c3a100..fc3b7dc2 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
@@ -6,8 +6,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/*********
** Accessors
*********/
- /// <summary>The number of minutes update checks should be cached before refetching them.</summary>
- public int CacheMinutes { get; set; }
+ /// <summary>The number of minutes successful update checks should be cached before refetching them.</summary>
+ public int SuccessCacheMinutes { get; set; }
+
+ /// <summary>The number of minutes failed update checks should be cached before refetching them.</summary>
+ public int ErrorCacheMinutes { get; set; }
/// <summary>A regex which matches SMAPI-style semantic version.</summary>
/// <remarks>Derived from SMAPI's SemanticVersion implementation.</remarks>
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 266055a6..3e5a4272 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
return new ModInfoModel("Found no mod with this ID.");
// create model
- return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
+ return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index 7bad6127..59eb8cd1 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -38,10 +38,21 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
// fetch info
try
{
- GitRelease release = await this.Client.GetLatestReleaseAsync(id);
- return release != null
- ? new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases")
- : new ModInfoModel("Found no mod with this ID.");
+ // get latest release
+ GitRelease latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: true);
+ GitRelease preview = null;
+ if (latest == null)
+ return new ModInfoModel("Found no mod with this ID.");
+
+ // get latest stable release (if not latest)
+ if (latest.IsPrerelease)
+ {
+ preview = latest;
+ latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: false);
+ }
+
+ // return data
+ return new ModInfoModel(name: id, version: this.NormaliseVersion(latest?.Tag), previewVersion: this.NormaliseVersion(preview?.Tag), url: $"https://github.com/{id}/releases");
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
index e1dc0fcc..6411ad4c 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
return new ModInfoModel("Found no mod with this ID.");
if (mod.Error != null)
return new ModInfoModel(mod.Error);
- return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
+ return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/StardewModdingAPI.Web.csproj b/src/SMAPI.Web/StardewModdingAPI.Web.csproj
index 19198503..e2eee8a8 100644
--- a/src/SMAPI.Web/StardewModdingAPI.Web.csproj
+++ b/src/SMAPI.Web/StardewModdingAPI.Web.csproj
@@ -10,13 +10,13 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="HtmlAgilityPack" Version="1.6.0" />
- <PackageReference Include="Markdig" Version="0.14.8" />
- <PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
- <PackageReference Include="Microsoft.AspNetCore.Rewrite" Version="2.0.0" />
- <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
- <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
+ <PackageReference Include="HtmlAgilityPack" Version="1.7.2" />
+ <PackageReference Include="Markdig" Version="0.14.9" />
+ <PackageReference Include="Microsoft.AspNetCore" Version="2.0.2" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.3" />
+ <PackageReference Include="Microsoft.AspNetCore.Rewrite" Version="2.0.2" />
+ <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.2" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.1" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index d7d4d074..47102e5c 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -74,7 +74,8 @@ namespace StardewModdingAPI.Web
services.AddSingleton<IGitHubClient>(new GitHubClient(
baseUrl: api.GitHubBaseUrl,
- releaseUrlFormat: api.GitHubReleaseUrlFormat,
+ stableReleaseUrlFormat: api.GitHubStableReleaseUrlFormat,
+ anyReleaseUrlFormat: api.GitHubAnyReleaseUrlFormat,
userAgent: userAgent,
acceptHeader: api.GitHubAcceptHeader,
username: api.GitHubUsername,
diff --git a/src/SMAPI.Web/ViewModels/IndexModel.cs b/src/SMAPI.Web/ViewModels/IndexModel.cs
index 6d3da91e..4268c878 100644
--- a/src/SMAPI.Web/ViewModels/IndexModel.cs
+++ b/src/SMAPI.Web/ViewModels/IndexModel.cs
@@ -6,17 +6,11 @@ namespace StardewModdingAPI.Web.ViewModels
/*********
** Accessors
*********/
- /// <summary>The latest SMAPI version.</summary>
- public string LatestVersion { get; set; }
+ /// <summary>The latest stable SMAPI version.</summary>
+ public IndexVersionModel StableVersion { get; set; }
- /// <summary>The Markdown description for the release.</summary>
- public string Description { get; set; }
-
- /// <summary>The main download URL.</summary>
- public string DownloadUrl { get; set; }
-
- /// <summary>The for-developers download URL.</summary>
- public string DevDownloadUrl { get; set; }
+ /// <summary>The latest prerelease SMAPI version (if newer than <see cref="StableVersion"/>).</summary>
+ public IndexVersionModel BetaVersion { get; set; }
/*********
@@ -26,16 +20,12 @@ namespace StardewModdingAPI.Web.ViewModels
public IndexModel() { }
/// <summary>Construct an instance.</summary>
- /// <param name="latestVersion">The latest SMAPI version.</param>
- /// <param name="description">The Markdown description for the release.</param>
- /// <param name="downloadUrl">The main download URL.</param>
- /// <param name="devDownloadUrl">The for-developers download URL.</param>
- internal IndexModel(string latestVersion, string description, string downloadUrl, string devDownloadUrl)
+ /// <param name="stableVersion">The latest stable SMAPI version.</param>
+ /// <param name="betaVersion">The latest prerelease SMAPI version (if newer than <paramref name="stableVersion"/>).</param>
+ internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion)
{
- this.LatestVersion = latestVersion;
- this.Description = description;
- this.DownloadUrl = downloadUrl;
- this.DevDownloadUrl = devDownloadUrl;
+ this.StableVersion = stableVersion;
+ this.BetaVersion = betaVersion;
}
}
}
diff --git a/src/SMAPI.Web/ViewModels/IndexVersionModel.cs b/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
new file mode 100644
index 00000000..4f63b979
--- /dev/null
+++ b/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
@@ -0,0 +1,41 @@
+namespace StardewModdingAPI.Web.ViewModels
+{
+ /// <summary>The fields for a SMAPI version.</summary>
+ public class IndexVersionModel
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The release version.</summary>
+ public string Version { get; set; }
+
+ /// <summary>The Markdown description for the release.</summary>
+ public string Description { get; set; }
+
+ /// <summary>The main download URL.</summary>
+ public string DownloadUrl { get; set; }
+
+ /// <summary>The for-developers download URL (not applicable for prerelease versions).</summary>
+ public string DevDownloadUrl { get; set; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public IndexVersionModel() { }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="version">The release number.</param>
+ /// <param name="description">The Markdown description for the release.</param>
+ /// <param name="downloadUrl">The main download URL.</param>
+ /// <param name="devDownloadUrl">The for-developers download URL (not applicable for prerelease versions).</param>
+ internal IndexVersionModel(string version, string description, string downloadUrl, string devDownloadUrl)
+ {
+ this.Version = version;
+ this.Description = description;
+ this.DownloadUrl = downloadUrl;
+ this.DevDownloadUrl = devDownloadUrl;
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml
index ad58898e..4efb9f8a 100644
--- a/src/SMAPI.Web/Views/Index/Index.cshtml
+++ b/src/SMAPI.Web/Views/Index/Index.cshtml
@@ -13,7 +13,11 @@
</p>
<div id="call-to-action">
- <a href="@Model.DownloadUrl" class="main-cta">Download SMAPI @Model.LatestVersion</a><br />
+ <a href="@Model.StableVersion.DownloadUrl" class="main-cta">Download SMAPI @Model.StableVersion.Version</a><br />
+ @if (Model.BetaVersion != null)
+ {
+ <a href="@Model.BetaVersion.DownloadUrl" class="secondary-cta">Download SMAPI @Model.BetaVersion.Version<br /><small>for Stardew Valley 1.3 beta</small></a><br />
+ }
<a href="https://stardewvalleywiki.com/Modding:Installing_SMAPI" class="secondary-cta">Install guide</a><br />
<a href="https://stardewvalleywiki.com/Modding:Player_FAQs" class="secondary-cta">FAQs</a><br />
<img src="favicon.ico" />
@@ -25,12 +29,29 @@
<li>Get help <a href="https://stardewvalleywiki.com/Modding:Community#Discord">on Discord</a> or <a href="https://community.playstarbound.com/threads/smapi-stardew-modding-api.108375/">in the forums</a></li>
</ul>
-<h2>What's new in SMAPI @Model.LatestVersion?</h2>
-<div class="github-description">
- @Html.Raw(Markdig.Markdown.ToHtml(Model.Description))
-</div>
+@if (Model.BetaVersion == null)
+{
+ <h2>What's new in SMAPI @Model.StableVersion.Version?</h2>
+ <div class="github-description">
+ @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
+ </div>
+ <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p>
+}
+else
+{
+ <h2>What's new in...</h2>
+ <h3>SMAPI @Model.StableVersion.Version?</h3>
+ <div class="github-description">
+ @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
+ </div>
+ <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p>
-<p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p>
+ <h3>SMAPI @Model.BetaVersion.Version?</h3>
+ <div class="github-description">
+ @Html.Raw(Markdig.Markdown.ToHtml(Model.BetaVersion.Description))
+ </div>
+ <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p>
+}
<h2>Donate to support SMAPI ♥</h2>
<p>
@@ -62,7 +83,7 @@
<h2>For mod creators</h2>
<ul>
- <li><a href="@Model.DevDownloadUrl">SMAPI @Model.LatestVersion for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li>
+ <li><a href="@Model.StableVersion.DevDownloadUrl">SMAPI @Model.StableVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li>
<li><a href="https://stardewvalleywiki.com/Modding:Index">Modding documentation</a></li>
<li>Need help? Come <a href="https://stardewvalleywiki.com/Modding:Community#Discord">chat on Discord</a>.</li>
</ul>
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml
index d2d8004e..7213e286 100644
--- a/src/SMAPI.Web/Views/LogParser/Index.cshtml
+++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml
@@ -54,19 +54,19 @@
<caption>Game info:</caption>
<tr>
<td>SMAPI version:</td>
- <td>@Model.ParsedLog.ApiVersion</td>
+ <td v-pre>@Model.ParsedLog.ApiVersion</td>
</tr>
<tr>
<td>Game version:</td>
- <td>@Model.ParsedLog.GameVersion</td>
+ <td v-pre>@Model.ParsedLog.GameVersion</td>
</tr>
<tr>
<td>Platform:</td>
- <td>@Model.ParsedLog.OperatingSystem</td>
+ <td v-pre>@Model.ParsedLog.OperatingSystem</td>
</tr>
<tr>
<td>Mods path:</td>
- <td>@Model.ParsedLog.ModPath</td>
+ <td v-pre>@Model.ParsedLog.ModPath</td>
</tr>
<tr>
<td>Log started:</td>
@@ -85,7 +85,7 @@
{
<tr v-on:click="toggleMod('@GetSlug(mod.Name)')" class="mod-entry" v-bind:class="{ hidden: !showMods['@GetSlug(mod.Name)'] }">
<td><input type="checkbox" v-bind:checked="showMods['@GetSlug(mod.Name)']" v-show="anyModsHidden" /></td>
- <td>
+ <td v-pre>
@mod.Name
@if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out LogModInfo[] contentPackList))
{
@@ -97,19 +97,19 @@
</div>
}
</td>
- <td>@mod.Version</td>
- <td>@mod.Author</td>
+ <td v-pre>@mod.Version</td>
+ <td v-pre>@mod.Author</td>
@if (mod.Errors == 0)
{
- <td class="color-green">no errors</td>
+ <td v-pre class="color-green">no errors</td>
}
else if (mod.Errors == 1)
{
- <td class="color-red">@mod.Errors error</td>
+ <td v-pre class="color-red">@mod.Errors error</td>
}
else
{
- <td class="color-red">@mod.Errors errors</td>
+ <td v-pre class="color-red">@mod.Errors errors</td>
}
</tr>
}
@@ -130,16 +130,16 @@
string levelStr = message.Level.ToString().ToLower();
<tr class="@levelStr mod" v-show="filtersAllow('@GetSlug(message.Mod)', '@levelStr')">
- <td>@message.Time</td>
- <td>@message.Level.ToString().ToUpper()</td>
- <td data-title="@message.Mod">@message.Mod</td>
- <td>@message.Text</td>
+ <td v-pre>@message.Time</td>
+ <td v-pre>@message.Level.ToString().ToUpper()</td>
+ <td v-pre data-title="@message.Mod">@message.Mod</td>
+ <td v-pre>@message.Text</td>
</tr>
if (message.Repeated > 0)
{
<tr class="@levelStr mod mod-repeat" v-show="filtersAllow('@GetSlug(message.Mod)', '@levelStr')">
<td colspan="3"></td>
- <td><i>repeats [@message.Repeated] times.</i></td>
+ <td v-pre><i>repeats [@message.Repeated] times.</i></td>
</tr>
}
}
@@ -151,11 +151,11 @@ else if (Model.ParsedLog?.IsValid == false)
<h2>Parsed log</h2>
<div id="error" class="color-red">
<p><strong>We couldn't parse that file, but you can still share the link.</strong></p>
- <p>Error details: @Model.ParsedLog.Error</p>
+ <p v-pre>Error details: @Model.ParsedLog.Error</p>
</div>
<h3>Raw log</h3>
- <pre>@Model.ParsedLog.RawText</pre>
+ <pre v-pre>@Model.ParsedLog.RawText</pre>
}
<div id="upload-area">
diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json
index 3cf72ddb..03ca31ed 100644
--- a/src/SMAPI.Web/appsettings.json
+++ b/src/SMAPI.Web/appsettings.json
@@ -24,7 +24,8 @@
"ChucklefishModPageUrlFormat": "resources/{0}",
"GitHubBaseUrl": "https://api.github.com",
- "GitHubReleaseUrlFormat": "repos/{0}/releases/latest",
+ "GitHubStableReleaseUrlFormat": "repos/{0}/releases/latest",
+ "GitHubAnyReleaseUrlFormat": "repos/{0}/releases?per_page=1",
"GitHubAcceptHeader": "application/vnd.github.v3+json",
"GitHubUsername": null, // see top note
"GitHubPassword": null, // see top note
@@ -39,7 +40,8 @@
},
"ModUpdateCheck": {
- "CacheMinutes": 60,
+ "SuccessCacheMinutes": 60,
+ "ErrorCacheMinutes": 5,
"SemanticVersionRegex": "^(?>(?<major>0|[1-9]\\d*))\\.(?>(?<minor>0|[1-9]\\d*))(?>(?:\\.(?<patch>0|[1-9]\\d*))?)(?:-(?<prerelease>(?>[a-z0-9]+[\\-\\.]?)+))?$",
"ChucklefishKey": "Chucklefish",