mirror of
https://github.com/Pathoschild/SMAPI.git
synced 2026-10-12 02:08:51 +00:00
add DNS traffic to stats page
This commit is contained in:
+1
-1
@@ -32,7 +32,7 @@ _ReSharper*/
|
||||
appsettings.Development.json
|
||||
|
||||
# auto-generated files
|
||||
src/SMAPI.Web/wwwroot/Content/data/*.jsonl
|
||||
src/SMAPI.Web/wwwroot/Content/data
|
||||
src/SMAPI.Web/Properties/PublishProfiles
|
||||
src/SMAPI.Web/Properties/ServiceDependencies
|
||||
|
||||
|
||||
@@ -280,6 +280,7 @@ internal class BackgroundService : IHostedService, IDisposable
|
||||
File.Copy(BackgroundService.ModDatasetRepo.GetFilePath("stats/mods by type.jsonl"), copyToPath, overwrite: true);
|
||||
File.Copy(BackgroundService.ModDatasetRepo.GetFilePath("stats/Content Patcher packs by format version.jsonl"), Path.Combine(BackgroundService.WebRootPath, "Content", "data", "content-packs-by-format.jsonl"), overwrite: true);
|
||||
File.Copy(BackgroundService.ModDatasetRepo.GetFilePath("reference-data/SMAPI costs.jsonl"), Path.Combine(BackgroundService.WebRootPath, "Content", "data", "smapi-costs.jsonl"), overwrite: true);
|
||||
File.Copy(BackgroundService.ModDatasetRepo.GetFilePath("reference-data/SMAPI DNS queries.json"), Path.Combine(BackgroundService.WebRootPath, "Content", "data", "smapi-dns-queries.json"), overwrite: true);
|
||||
|
||||
context.WriteLine("Done!");
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
smapi.statsPage({
|
||||
modsByTypeUri: @this.ForJson(Url.ContentWithCacheBust("~/Content/data/mods-by-type.jsonl")),
|
||||
contentPacksByFormatUri: @this.ForJson(Url.ContentWithCacheBust("~/Content/data/content-packs-by-format.jsonl")),
|
||||
smapiCostsUri: @this.ForJson(Url.ContentWithCacheBust("~/Content/data/smapi-costs.jsonl"))
|
||||
smapiCostsUri: @this.ForJson(Url.ContentWithCacheBust("~/Content/data/smapi-costs.jsonl")),
|
||||
smapiDnsQueriesUri: @this.ForJson(Url.ContentWithCacheBust("~/Content/data/smapi-dns-queries.json"))
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -35,6 +36,7 @@
|
||||
<li><a href="#mods-by-type">Mods by type</a></li>
|
||||
<li><a href="#new-mods">New mods</a></li>
|
||||
<li><a href="#content-patcher-packs">Content Patcher packs</a></li>
|
||||
<li><a href="#web-traffic">Web traffic</a></li>
|
||||
<li><a href="#smapi-costs">SMAPI costs</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -101,6 +103,13 @@
|
||||
<div class="chart pie-chart" id="contentPacksByFormat"></div>
|
||||
<div class="chart line-chart" id="contentPacksByFormatOverTime"></div>
|
||||
|
||||
@* web traffic *@
|
||||
<h2 id="web-traffic">Web traffic</h2>
|
||||
<div class="last-updated">Based on data from {{dnsQueries.lastUpdated}}.</div>
|
||||
|
||||
<div class="chart-intro">SMAPI's web services received about {{(dnsQueries.latestCount / 1000000).toFixed(1)}} million DNS queries in {{dnsQueries.lastUpdated}}. This serves as a rough indicator of player and mod author activity, since SMAPI doesn't collect any telemetry.</div>
|
||||
<div class="chart" id="dnsQueriesOverTime"></div>
|
||||
|
||||
@* SMAPI costs *@
|
||||
<h2 id="smapi-costs">SMAPI costs</h2>
|
||||
<div class="last-updated">Based on data from {{costs.lastUpdated}}. This information is delayed due to billing cycles.</div>
|
||||
|
||||
@@ -67,6 +67,20 @@ smapi.statsPage = function (options) {
|
||||
}
|
||||
},
|
||||
|
||||
// configure the 'web traffic' stats
|
||||
dnsQueries: {
|
||||
// notable events which may affect traffic, indexed by their date in smapi-dns-queries.json
|
||||
notableEvents: {
|
||||
"2018-05": "Stardew Valley 1.3 beta", // 30 April 2018
|
||||
"2018-08": "Stardew Valley 1.3", // 01 August 2018
|
||||
"2019-12": "Stardew Valley 1.4", // 26 November 2019
|
||||
"2020-12": "Stardew Valley 1.5", // 21 December 2020
|
||||
"2021-08": "Stardew Valley 1.5.5", // 17 August 2021
|
||||
"2024-03": "Stardew Valley 1.6", // 19 March 2024
|
||||
"2024-11": "Stardew Valley 1.6.9" // 04 November 2024
|
||||
}
|
||||
},
|
||||
|
||||
// configure the 'SMAPI costs' stats
|
||||
costs: {
|
||||
// colors for each service (omitted entries cycle through the colorPalette)
|
||||
@@ -110,6 +124,10 @@ smapi.statsPage = function (options) {
|
||||
xLabels: [],
|
||||
versions: []
|
||||
},
|
||||
dnsQueries: {
|
||||
xLabels: [],
|
||||
values: []
|
||||
},
|
||||
costs: {
|
||||
rows: [],
|
||||
serviceKeys: [],
|
||||
@@ -147,6 +165,10 @@ smapi.statsPage = function (options) {
|
||||
total: 0,
|
||||
topVersions: []
|
||||
},
|
||||
dnsQueries: {
|
||||
lastUpdated: null,
|
||||
latestCount: 0
|
||||
},
|
||||
costs: {
|
||||
lastUpdated: null,
|
||||
previousCost: 0,
|
||||
@@ -187,12 +209,13 @@ smapi.statsPage = function (options) {
|
||||
/*********
|
||||
** Fetch data files
|
||||
*********/
|
||||
const [modsByTypeRows, contentPacksRows, costsRows] = await Promise.all([
|
||||
const [modsByTypeRows, contentPacksRows, costsRows, dnsData] = await Promise.all([
|
||||
fetchJsonLinesAsync(options.modsByTypeUri),
|
||||
fetchJsonLinesAsync(options.contentPacksByFormatUri),
|
||||
fetchJsonLinesAsync(options.smapiCostsUri)
|
||||
fetchJsonLinesAsync(options.smapiCostsUri),
|
||||
fetchJsonAsync(options.smapiDnsQueriesUri)
|
||||
]);
|
||||
if (!modsByTypeRows || !contentPacksRows || !costsRows) {
|
||||
if (!modsByTypeRows || !contentPacksRows || !costsRows || !dnsData) {
|
||||
this.isDataFailed = true;
|
||||
return;
|
||||
}
|
||||
@@ -319,6 +342,25 @@ smapi.statsPage = function (options) {
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Process DNS queries
|
||||
*********/
|
||||
{
|
||||
const xLabels = Object.keys(dnsData);
|
||||
const values = Object.values(dnsData);
|
||||
|
||||
data.dnsQueries = {
|
||||
xLabels,
|
||||
values
|
||||
};
|
||||
|
||||
this.dnsQueries = {
|
||||
lastUpdated: formatMonthYear(xLabels[xLabels.length - 1]),
|
||||
latestCount: values[values.length - 1]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Process SMAPI costs
|
||||
*********/
|
||||
@@ -858,6 +900,70 @@ smapi.statsPage = function (options) {
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** 'DNS queries over time' bar chart
|
||||
*********/
|
||||
{
|
||||
const curData = data.dnsQueries;
|
||||
|
||||
const chart = echarts.init(document.getElementById("dnsQueriesOverTime"));
|
||||
|
||||
chart.setOption({
|
||||
title: {
|
||||
text: "Web traffic",
|
||||
textStyle: createTitleStyle()
|
||||
},
|
||||
grid: {
|
||||
top: 55,
|
||||
bottom: this.showAdvancedControls ? 80 : 60,
|
||||
left: 70,
|
||||
right: 20
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: curData.xLabels,
|
||||
axisLabel: {
|
||||
rotate: 90,
|
||||
fontSize: 11
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
name: "DNS queries (millions)",
|
||||
axisLabel: {
|
||||
formatter: value => (value / 1_000_000).toFixed(0)
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "bar",
|
||||
data: curData.values,
|
||||
color: "#3388cc",
|
||||
markLine: createMarkLine(p => p.name, config.dnsQueries.notableEvents)
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
formatter: params => {
|
||||
const value = params[0].value;
|
||||
|
||||
const lines = [
|
||||
params[0].axisValue, // date
|
||||
value > 1_000_000
|
||||
? `${(Math.round(value / 100_000) / 10).toLocaleString()} million`
|
||||
: value.toLocaleString()
|
||||
];
|
||||
|
||||
return lines.join("<br />");
|
||||
}
|
||||
},
|
||||
toolbox: createToolbox(true, this.showAdvancedControls)
|
||||
});
|
||||
|
||||
charts.push(chart);
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** 'SMAPI costs over time' stacked bar chart
|
||||
*********/
|
||||
@@ -960,6 +1066,19 @@ smapi.statsPage = function (options) {
|
||||
return rawJsonLines.trim().split(/\r?\n/).filter(row => !!row).map(JSON.parse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and parse data from a JSON URI.
|
||||
* @param {string} fetchUri The URI from which to fetch the JSON data.
|
||||
* @returns {null|object} Returns the parsed data if it was found, else `null`.
|
||||
*/
|
||||
async function fetchJsonAsync(fetchUri) {
|
||||
const response = await fetch(fetchUri);
|
||||
if (!response.ok)
|
||||
return null;
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted date string.
|
||||
* @param {Date|string} date The ISO 8601 date-only string to format.
|
||||
|
||||
Reference in New Issue
Block a user