diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs
new file mode 100644
index 00000000..5d45118f
--- /dev/null
+++ b/src/SMAPI.Web/Controllers/IndexController.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Memory;
+using StardewModdingAPI.Web.Framework.Clients.GitHub;
+using StardewModdingAPI.Web.ViewModels;
+
+namespace StardewModdingAPI.Web.Controllers
+{
+ /// Provides an info/download page about SMAPI.
+ [Route("")]
+ [Route("install")]
+ internal class IndexController : Controller
+ {
+ /*********
+ ** Properties
+ *********/
+ /// The cache in which to store release data.
+ private readonly IMemoryCache Cache;
+
+ /// The GitHub API client.
+ private readonly IGitHubClient GitHub;
+
+ /// The cache time for release info.
+ private readonly TimeSpan CacheTime = TimeSpan.FromMinutes(5);
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// Construct an instance.
+ /// The cache in which to store release data.
+ /// The GitHub API client.
+ public IndexController(IMemoryCache cache, IGitHubClient github)
+ {
+ this.Cache = cache;
+ this.GitHub = github;
+ }
+
+ /// Display the index page.
+ [HttpGet]
+ public async Task Index()
+ {
+ // fetch latest SMAPI release
+ GitRelease release = await this.Cache.GetOrCreateAsync("latest-smapi-release", async entry =>
+ {
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
+ return await this.GitHub.GetLatestReleaseAsync("Pathoschild/SMAPI");
+ });
+ string downloadUrl = this.GetMainDownloadUrl(release);
+ string devDownloadUrl = this.GetDevDownloadUrl(release);
+
+ // render view
+ var model = new IndexModel(release.Name, release.Body, downloadUrl, devDownloadUrl);
+ return this.View(model);
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// Get the main download URL for a SMAPI release.
+ /// The SMAPI release.
+ private string GetMainDownloadUrl(GitRelease release)
+ {
+ // get main download URL
+ foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
+ {
+ if (Regex.IsMatch(asset.FileName, @"SMAPI-[\d\.]+-installer.zip"))
+ return asset.DownloadUrl;
+ }
+
+ // fallback just in case
+ return "https://github.com/pathoschild/SMAPI/releases";
+ }
+
+ /// Get the for-developers download URL for a SMAPI release.
+ /// The SMAPI release.
+ 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;
+ }
+
+ // fallback just in case
+ return "https://github.com/pathoschild/SMAPI/releases";
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs
index b9227a2f..04a11a82 100644
--- a/src/SMAPI.Web/Controllers/LogParserController.cs
+++ b/src/SMAPI.Web/Controllers/LogParserController.cs
@@ -19,7 +19,7 @@ namespace StardewModdingAPI.Web.Controllers
** Properties
*********/
/// The log parser config settings.
- private readonly LogParserConfig Config;
+ private readonly ContextConfig Config;
/// The underlying Pastebin client.
private readonly IPastebinClient Pastebin;
@@ -36,11 +36,11 @@ namespace StardewModdingAPI.Web.Controllers
** Constructor
***/
/// Construct an instance.
- /// The log parser config settings.
+ /// The context config settings.
/// The Pastebin API client.
- public LogParserController(IOptions configProvider, IPastebinClient pastebin)
+ public LogParserController(IOptions contextProvider, IPastebinClient pastebin)
{
- this.Config = configProvider.Value;
+ this.Config = contextProvider.Value;
this.Pastebin = pastebin;
}
@@ -50,12 +50,11 @@ namespace StardewModdingAPI.Web.Controllers
/// Render the log parser UI.
/// The paste ID.
[HttpGet]
- [Route("")]
[Route("log")]
[Route("log/{id}")]
public ViewResult Index(string id = null)
{
- return this.View("Index", new LogParserModel(this.Config.SectionUrl, id));
+ return this.View("Index", new LogParserModel(this.Config.LogParserUrl, id));
}
/***
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs
new file mode 100644
index 00000000..73ce4025
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs
@@ -0,0 +1,20 @@
+using Newtonsoft.Json;
+
+namespace StardewModdingAPI.Web.Framework.Clients.GitHub
+{
+ /// A GitHub download attached to a release.
+ internal class GitAsset
+ {
+ /// The file name.
+ [JsonProperty("name")]
+ public string FileName { get; set; }
+
+ /// The file content type.
+ [JsonProperty("content_type")]
+ public string ContentType { get; set; }
+
+ /// The download URL.
+ [JsonProperty("browser_download_url")]
+ public string DownloadUrl { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
index 0a47f3b4..b944088d 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
@@ -15,5 +15,11 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// The semantic version string.
[JsonProperty("tag_name")]
public string Tag { get; set; }
+
+ /// The Markdown description for the release.
+ public string Body { get; set; }
+
+ /// The attached files.
+ public GitAsset[] Assets { get; set; }
}
}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ContextConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ContextConfig.cs
new file mode 100644
index 00000000..117462f4
--- /dev/null
+++ b/src/SMAPI.Web/Framework/ConfigModels/ContextConfig.cs
@@ -0,0 +1,15 @@
+namespace StardewModdingAPI.Web.Framework.ConfigModels
+{
+ /// The config settings for the app context.
+ public class ContextConfig // must be public to pass into views
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// The root URL for the app.
+ public string RootUrl { get; set; }
+
+ /// The root URL for the log parser.
+ public string LogParserUrl { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs
deleted file mode 100644
index 198274b2..00000000
--- a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace StardewModdingAPI.Web.Framework.ConfigModels
-{
- /// The config settings for the log parser.
- internal class LogParserConfig
- {
- /*********
- ** Accessors
- *********/
- /// The root URL for the log parser controller.
- public string SectionUrl { get; set; }
- }
-}
diff --git a/src/SMAPI.Web/Properties/launchSettings.json b/src/SMAPI.Web/Properties/launchSettings.json
index e485e4e3..88179044 100644
--- a/src/SMAPI.Web/Properties/launchSettings.json
+++ b/src/SMAPI.Web/Properties/launchSettings.json
@@ -11,7 +11,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
- "launchUrl": "log",
+ "launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/src/SMAPI.Web/StardewModdingAPI.Web.csproj b/src/SMAPI.Web/StardewModdingAPI.Web.csproj
index b5b0ff07..19198503 100644
--- a/src/SMAPI.Web/StardewModdingAPI.Web.csproj
+++ b/src/SMAPI.Web/StardewModdingAPI.Web.csproj
@@ -11,6 +11,7 @@
+
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index 307c4ae9..e5e759e7 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -48,7 +48,7 @@ namespace StardewModdingAPI.Web
// init configuration
services
.Configure(this.Configuration.GetSection("ModUpdateCheck"))
- .Configure(this.Configuration.GetSection("LogParser"))
+ .Configure(this.Configuration.GetSection("Context"))
.Configure(options => options.ConstraintMap.Add("semanticVersion", typeof(VersionConstraint)))
.AddMemoryCache()
.AddMvc()
@@ -134,7 +134,6 @@ namespace StardewModdingAPI.Web
// shortcut redirects
.Add(new RedirectToUrlRule("^/docs$", "https://stardewvalleywiki.com/Modding:Index"))
- .Add(new RedirectToUrlRule("^/install$", "https://stardewvalleywiki.com/Modding:Installing_SMAPI"))
)
.UseStaticFiles() // wwwroot folder
.UseMvc();
diff --git a/src/SMAPI.Web/ViewModels/IndexModel.cs b/src/SMAPI.Web/ViewModels/IndexModel.cs
new file mode 100644
index 00000000..6d3da91e
--- /dev/null
+++ b/src/SMAPI.Web/ViewModels/IndexModel.cs
@@ -0,0 +1,41 @@
+namespace StardewModdingAPI.Web.ViewModels
+{
+ /// The view model for the index page.
+ public class IndexModel
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// The latest SMAPI version.
+ public string LatestVersion { get; set; }
+
+ /// The Markdown description for the release.
+ public string Description { get; set; }
+
+ /// The main download URL.
+ public string DownloadUrl { get; set; }
+
+ /// The for-developers download URL.
+ public string DevDownloadUrl { get; set; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// Construct an instance.
+ public IndexModel() { }
+
+ /// Construct an instance.
+ /// The latest SMAPI version.
+ /// The Markdown description for the release.
+ /// The main download URL.
+ /// The for-developers download URL.
+ internal IndexModel(string latestVersion, string description, string downloadUrl, string devDownloadUrl)
+ {
+ this.LatestVersion = latestVersion;
+ 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
new file mode 100644
index 00000000..1bae8b16
--- /dev/null
+++ b/src/SMAPI.Web/Views/Index/Index.cshtml
@@ -0,0 +1,62 @@
+@{
+ ViewData["Title"] = "SMAPI";
+}
+@model StardewModdingAPI.Web.ViewModels.IndexModel
+@section Head {
+
+}
+
+
+ The mod loader for Stardew Valley. It works fine with GOG and Steam achievements, it's
+ compatible with Linux/Mac/Windows, you can uninstall it anytime, and there's a friendly
+ community if you need help. It's a cool pufferchick.
+
+ Donate $1+/month
+ You'll have access to all private posts about behind-the-scenes info, upcoming features, and early previews of SMAPI updates. You can optionally provide early feedback on SMAPI features to influence development.
+
+ Special thanks to
+ acerbicon,
+ ChefRude,
+ jwdred,
+ Karmylla,
+ OfficialPiAddict,
+ Robby LaFarge,
+ and a few anonymous users for supporting SMAPI; you're awesome! 🏅
+