summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Controllers/IndexController.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web/Controllers/IndexController.cs')
-rw-r--r--src/SMAPI.Web/Controllers/IndexController.cs180
1 files changed, 118 insertions, 62 deletions
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs
index 0464e50a..8c4a0332 100644
--- a/src/SMAPI.Web/Controllers/IndexController.cs
+++ b/src/SMAPI.Web/Controllers/IndexController.cs
@@ -1,10 +1,15 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using HtmlAgilityPack;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
-using StardewModdingAPI.Common;
+using Microsoft.Extensions.Options;
+using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Web.Framework.Clients.GitHub;
+using StardewModdingAPI.Web.Framework.ConfigModels;
using StardewModdingAPI.Web.ViewModels;
namespace StardewModdingAPI.Web.Controllers
@@ -17,6 +22,9 @@ namespace StardewModdingAPI.Web.Controllers
/*********
** Properties
*********/
+ /// <summary>The site config settings.</summary>
+ private readonly SiteConfig SiteConfig;
+
/// <summary>The cache in which to store release data.</summary>
private readonly IMemoryCache Cache;
@@ -24,7 +32,7 @@ namespace StardewModdingAPI.Web.Controllers
private readonly IGitHubClient GitHub;
/// <summary>The cache time for release info.</summary>
- private readonly TimeSpan CacheTime = TimeSpan.FromSeconds(1);
+ private readonly TimeSpan CacheTime = TimeSpan.FromMinutes(10);
/// <summary>The GitHub repository name to check for update.</summary>
private readonly string RepositoryName = "Pathoschild/SMAPI";
@@ -36,34 +44,35 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>Construct an instance.</summary>
/// <param name="cache">The cache in which to store release data.</param>
/// <param name="github">The GitHub API client.</param>
- public IndexController(IMemoryCache cache, IGitHubClient github)
+ /// <param name="siteConfig">The context config settings.</param>
+ public IndexController(IMemoryCache cache, IGitHubClient github, IOptions<SiteConfig> siteConfig)
{
this.Cache = cache;
this.GitHub = github;
+ this.SiteConfig = siteConfig.Value;
}
/// <summary>Display the index page.</summary>
[HttpGet]
public async Task<ViewResult> Index()
{
- // 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);
- GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true);
- return release.IsPrerelease
- ? this.GetBetaDownload(release)
- : null;
- });
+ // choose versions
+ ReleaseVersion[] versions = await this.GetReleaseVersionsAsync();
+ ReleaseVersion stableVersion = versions.LastOrDefault(version => !version.IsBeta && !version.IsForDevs);
+ ReleaseVersion stableVersionForDevs = versions.LastOrDefault(version => !version.IsBeta && version.IsForDevs);
+ ReleaseVersion betaVersion = versions.LastOrDefault(version => version.IsBeta && !version.IsForDevs);
+ ReleaseVersion betaVersionForDevs = versions.LastOrDefault(version => version.IsBeta && version.IsForDevs);
// render view
- var model = new IndexModel(stableVersion, betaVersion);
+ IndexVersionModel stableVersionModel = stableVersion != null
+ ? new IndexVersionModel(stableVersion.Version.ToString(), stableVersion.Release.Body, stableVersion.Asset.DownloadUrl, stableVersionForDevs?.Asset.DownloadUrl)
+ : new IndexVersionModel("unknown", "", "https://github.com/Pathoschild/SMAPI/releases", null); // just in case something goes wrong)
+ IndexVersionModel betaVersionModel = betaVersion != null && this.SiteConfig.EnableSmapiBeta
+ ? new IndexVersionModel(betaVersion.Version.ToString(), betaVersion.Release.Body, betaVersion.Asset.DownloadUrl, betaVersionForDevs?.Asset.DownloadUrl)
+ : null;
+
+ // render view
+ var model = new IndexModel(stableVersionModel, betaVersionModel);
return this.View(model);
}
@@ -71,62 +80,109 @@ namespace StardewModdingAPI.Web.Controllers
/*********
** Private methods
*********/
- /// <summary>Get the main download URL for a SMAPI release.</summary>
- /// <param name="release">The SMAPI release.</param>
- private string GetMainDownloadUrl(GitRelease release)
+ /// <summary>Get a sorted, parsed list of SMAPI downloads for the latest releases.</summary>
+ private async Task<ReleaseVersion[]> GetReleaseVersionsAsync()
{
- // get main download URL
- foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
+ return await this.Cache.GetOrCreateAsync("available-versions", async entry =>
{
- if (Regex.IsMatch(asset.FileName, @"SMAPI-[\d\.]+-installer.zip"))
- return asset.DownloadUrl;
- }
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
- // fallback just in case
- return "https://github.com/pathoschild/SMAPI/releases";
- }
+ // get latest release (whether preview or stable)
+ GitRelease stableRelease = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true);
- /// <summary>Get the for-developers download URL for a SMAPI release.</summary>
- /// <param name="release">The SMAPI release.</param>
- private string GetDevDownloadUrl(GitRelease release)
- {
- // get dev download URL
- foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
- {
- if (Regex.IsMatch(asset.FileName, @"SMAPI-[\d\.]+-installer-for-developers.zip"))
- return asset.DownloadUrl;
- }
+ // split stable/prerelease if applicable
+ GitRelease betaRelease = null;
+ if (stableRelease.IsPrerelease)
+ {
+ GitRelease result = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false);
+ if (result != null)
+ {
+ betaRelease = stableRelease;
+ stableRelease = result;
+ }
+ }
+
+ // strip 'noinclude' blocks from release descriptions
+ foreach (GitRelease release in new[] { stableRelease, betaRelease })
+ {
+ if (release == null)
+ continue;
+
+ HtmlDocument doc = new HtmlDocument();
+ doc.LoadHtml(release.Body);
+ foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? new HtmlNode[0])
+ node.Remove();
+ release.Body = doc.DocumentNode.InnerHtml.Trim();
+ }
- // fallback just in case
- return "https://github.com/pathoschild/SMAPI/releases";
+ // get versions
+ ReleaseVersion[] stableVersions = this.ParseReleaseVersions(stableRelease).ToArray();
+ ReleaseVersion[] betaVersions = this.ParseReleaseVersions(betaRelease).ToArray();
+ return stableVersions
+ .Concat(betaVersions)
+ .OrderBy(p => p.Version)
+ .ToArray();
+ });
}
- /// <summary>Get the latest beta download for a SMAPI release.</summary>
- /// <param name="release">The SMAPI release.</param>
- private IndexVersionModel GetBetaDownload(GitRelease release)
+ /// <summary>Get a parsed list of SMAPI downloads for a release.</summary>
+ /// <param name="release">The GitHub release.</param>
+ private IEnumerable<ReleaseVersion> ParseReleaseVersions(GitRelease release)
{
- // get download with the latest version
- SemanticVersionImpl latestVersion = null;
- string latestUrl = null;
- foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
+ if (release?.Assets == null)
+ yield break;
+
+ foreach (GitAsset asset in release.Assets)
{
- // parse version
- Match versionMatch = Regex.Match(asset.FileName, @"SMAPI-([\d\.]+(?:-.+)?)-installer.zip");
- if (!versionMatch.Success || !SemanticVersionImpl.TryParse(versionMatch.Groups[1].Value, out SemanticVersionImpl version))
+ Match match = Regex.Match(asset.FileName, @"SMAPI-(?<version>[\d\.]+(?:-.+)?)-installer(?<forDevs>-for-developers)?.zip");
+ if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion version))
continue;
+ bool isBeta = version.IsPrerelease();
+ bool isForDevs = match.Groups["forDevs"].Success;
- // save latest version
- if (latestVersion == null || latestVersion.CompareTo(version) < 0)
- {
- latestVersion = version;
- latestUrl = asset.DownloadUrl;
- }
+ yield return new ReleaseVersion(release, asset, version, isBeta, isForDevs);
}
+ }
- // return if prerelease
- return latestVersion?.Tag != null
- ? new IndexVersionModel(latestVersion.ToString(), release.Body, latestUrl, null)
- : null;
+ /// <summary>A parsed release download.</summary>
+ private class ReleaseVersion
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The underlying GitHub release.</summary>
+ public GitRelease Release { get; }
+
+ /// <summary>The underlying download asset.</summary>
+ public GitAsset Asset { get; }
+
+ /// <summary>The SMAPI version.</summary>
+ public ISemanticVersion Version { get; }
+
+ /// <summary>Whether this is a beta download.</summary>
+ public bool IsBeta { get; }
+
+ /// <summary>Whether this is a 'for developers' download.</summary>
+ public bool IsForDevs { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="release">The underlying GitHub release.</param>
+ /// <param name="asset">The underlying download asset.</param>
+ /// <param name="version">The SMAPI version.</param>
+ /// <param name="isBeta">Whether this is a beta download.</param>
+ /// <param name="isForDevs">Whether this is a 'for developers' download.</param>
+ public ReleaseVersion(GitRelease release, GitAsset asset, ISemanticVersion version, bool isBeta, bool isForDevs)
+ {
+ this.Release = release;
+ this.Asset = asset;
+ this.Version = version;
+ this.IsBeta = isBeta;
+ this.IsForDevs = isForDevs;
+ }
}
}
}