feat: hourly and daily terms for ap analytics

This commit is contained in:
Julian Lam
2026-04-01 14:32:50 -04:00
parent ab3c39eb62
commit 54893c81b4
4 changed files with 61 additions and 30 deletions

View File

@@ -68,5 +68,8 @@
"analytics.intro": "From this page you can view the state of your instance's federation with other servers",
"analytics.by-hostname": "Filter by Hostname",
"analytics.received": "Received Activities",
"analytics.sent": "Sent Activites"
"analytics.sent": "Sent Activites",
"analytics.term": "Time scale",
"analytics.hourly": "Hourly",
"analytics.daily": "Daily"
}

View File

@@ -21,21 +21,40 @@ Chart.register(
Filler
);
let charts;
const labels = new Map([
['hourly', utils.getHoursArray().map(function (text, idx) {
return idx % 3 ? '' : text;
})],
['daily', utils.getDaysArray().map(function (text, idx) {
return idx % 3 ? '' : text;
})],
]);
export function init() {
const charts = initializeCharts();
charts = initializeCharts();
const hostFilterEl = document.getElementById('hostFilter');
const termEl = document.getElementById('term');
if (hostFilterEl) {
hostFilterEl.addEventListener('change', async function () {
const data = await get(`/api${ajaxify.data.url}?host=${this.value}`);
['received', 'sent'].forEach((name) => {
const chart = charts.get(name);
chart.data.datasets[0].data = data[name];
chart.update();
});
});
hostFilterEl.addEventListener('change', updateCharts);
}
if (termEl) {
termEl.addEventListener('change', updateCharts);
}
}
async function updateCharts() {
const hostFilterEl = document.getElementById('hostFilter');
const termEl = document.getElementById('term');
const data = await get(`/api${ajaxify.data.url}?host=${hostFilterEl.value}&term=${termEl.value}`);
['received', 'sent'].forEach((name) => {
const chart = charts.get(name);
chart.data.labels = labels.get(termEl.value || 'hourly');
chart.data.datasets[0].data = data[name];
chart.update();
});
}
function initializeCharts() {
@@ -43,12 +62,6 @@ function initializeCharts() {
const sentCanvas = document.getElementById('sent');
// const topicsCanvas = document.getElementById('topics:daily');
// const postsCanvas = document.getElementById('posts:daily');
const hourlyLabels = utils.getHoursArray().map(function (text, idx) {
return idx % 3 ? '' : text;
});
// const dailyLabels = utils.getDaysArray().map(function (text, idx) {
// return idx % 3 ? '' : text;
// });
if (utils.isMobile()) {
Chart.defaults.plugins.tooltip.enabled = false;
@@ -64,7 +77,7 @@ function initializeCharts() {
const data = {
'received': {
labels: hourlyLabels,
labels: labels.get('hourly'),
datasets: [
{
...commonDataSetOpts,
@@ -77,7 +90,7 @@ function initializeCharts() {
],
},
'sent': {
labels: hourlyLabels,
labels: labels.get('hourly'),
datasets: [
{
...commonDataSetOpts,

View File

@@ -56,14 +56,20 @@ federationController.safety = async function (req, res) {
federationController.analytics = async function (req, res) {
const instances = await activitypub.instances.list();
let { host } = req.query;
let { host, term } = req.query;
if (!instances.includes(host)) {
host = undefined;
}
let method = 'getHourlyStatsForSet';
let count = 24;
if (term === 'daily') {
method = 'getDailyStatsForSet';
count = 30;
}
const set = host ? `activities:byHost:${host}` : 'activities';
const sentSet = host ? `ap.out:byHost:${host}` : 'ap:out';
const received = await analytics.getHourlyStatsForSet(set, Date.now(), 24);
const sent = await analytics.getHourlyStatsForSet(sentSet, Date.now(), 24);
const received = await analytics[method](set, Date.now(), count);
const sent = await analytics[method](sentSet, Date.now(), count);
res.render('admin/federation/analytics', {
title: '[[admin/menu:federation/analytics]]',

View File

@@ -6,14 +6,23 @@
<div class="mb-4">
<p class="lead">[[admin/settings/activitypub:analytics.intro]]</p>
<label class="fs-5 fw-bold tracking-tight settings-header mb-3">[[admin/settings/activitypub:analytics.by-hostname]] ({instances.length})</label>
<div class="mb-3">
<select class="form-select" autocomplete="off" id="hostFilter">
<option value="">All instances</option>
{{{ each instances }}}
<option value="{@value}">{@value}</option>
{{{ end }}}
</select>
<div class="mb-3 row">
<div class="col-6">
<label class="form-label" for="hostFilter">[[admin/settings/activitypub:analytics.by-hostname]] ({instances.length})</label>
<select class="form-select" autocomplete="off" id="hostFilter">
<option value="">All instances</option>
{{{ each instances }}}
<option value="{@value}">{@value}</option>
{{{ end }}}
</select>
</div>
<div class="col-6">
<label class="form-label" for="term">[[admin/settings/activitypub:analytics.term]]</label>
<select class="form-select" autocomplete="off" id="term">
<option value="hourly">[[admin/settings/activitypub:analytics.hourly]]</option>
<option value="daily">[[admin/settings/activitypub:analytics.daily]]</option>
</select>
</div>
</div>
</div>