Files
nitter-status/server/static/admin.js
T
4cuck 98ac43723e Gather .health instance stats
Reworks instance checks to use the same timestamp and be transactional. Also prevents long DB lock times during list updates.
2023-11-27 00:27:07 +01:00

247 lines
8.5 KiB
JavaScript

document.addEventListener("DOMContentLoaded", function() {
const startDateInput = document.getElementById('startDate');
const endDateInput = document.getElementById('endDate');
const submitTime = document.getElementById('submitDateRange');
// Set initial values in UTC
const initialEndDate = moment.utc();
const initialStartDate = moment().subtract(30, 'days').utc();
startDateInput.value = initialStartDate.format('YYYY-MM-DD');
endDateInput.value = initialEndDate.format('YYYY-MM-DD');;
submitTime.addEventListener('click', function() {
const startDate = moment(startDateInput.value, 'YYYY-MM-DD');
const endDate = moment(endDateInput.value, 'YYYY-MM-DD');
if (!startDate.isValid() || !endDate.isValid()) {
alert('Invalid date format. Please use the YYYY-MM-DD format.');
} else if (startDate.isAfter(endDate)) {
alert('Invalid date range. Start date must be before the end date.');
} else {
fetchDataAndCreateChart(startDate,endDate);
}
});
fetchDataAndCreateChart(initialStartDate,initialEndDate);
});
let chart = undefined;
async function fetchDataAndCreateChart(startDate,endDate) {
let data;
try {
const response = await fetch('/admin/api/history', {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({"start": startDate.utc(), "end": endDate.add(1, 'days').utc()})
});
data = await response.json();
} catch (error) {
console.error('Failed to fetch data:', error);
}
// Create arrays for y-values for both datasets
const healthyData = data.global.map(entry => {
return {x: moment.unix(entry.time), y: entry.alive};
});
const unhealthyData = data.global.map(entry => {
return {x: moment.unix(entry.time), y: entry.dead};
});
let statsData = [];
let statsKeys = [];
if (data.stats.length > 0) {
statsKeys = Object.keys(data.stats[0]).filter(key => key !== 'time');
statsData = statsKeys.map(key => ({"key": key, data: data.stats.map(entry => ({x: moment.unix(entry.time), y: entry[key]}))}));
}
const userUnhealthyData = data.user.filter(entry => entry.dead > 0).map(entry => ({"x": entry.time * 1000, "y": entry.dead}));
const paramData = {"healthyData": healthyData, "unhealthyData": unhealthyData, "user": userUnhealthyData, "stats": statsData, "statsKeys": statsKeys};
createChartOverview(paramData);
}
function mapKey(key) {
const keyMappings = {
"limited_accs_max": "Max Lim. Accs",
"limited_accs_avg": "Avg Lim. Accs",
"total_accs_max": "Max Accs",
"total_accs_avg": "Avg Accs",
"total_requests_max": "Max Requests Req.",
"total_requests_avg": "Avg Requests Req.",
"req_photo_rail_max": "Max PhotoRail Req.",
"req_photo_rail_avg": "Avg PhotoRail Req.",
"req_user_screen_name_max": "Max UserScreenName Req.",
"req_user_screen_name_avg": "Avg UserScreenName Req.",
"req_search_max": "Max Search Req.",
"req_search_avg": "Avg Search Req.",
"req_list_tweets_max": "Max ListTweets Req.",
"req_list_tweets_avg": "Avg ListTweets Req.",
"req_user_media_max": "Max UserMedia Req.",
"req_user_media_avg": "Avg UserMedia Req.",
"req_tweet_detail_max": "Max TweetDetail Req.",
"req_tweet_detail_avg": "Avg TweetDetail Req.",
"req_list_max": "Max List Req.",
"req_list_avg": "Avg List Req.",
"req_user_tweets_max": "Max UserTweets Req.",
"req_user_tweets_avg": "Avg UserTweets Req.",
"req_user_tweets_and_replies_max": "Max UserTweetsAndReplies Req.",
"req_user_tweets_and_replies_avg": "Avg UserTweetsAndReplies Req.",
}
return keyMappings[key];
}
function nameToColor(name) {
const colors = ["#fafa6e"
,"#eafb71"
,"#dafb75"
,"#cafb7b"
,"#b9fa81"
,"#a8f989"
,"#97f890"
,"#85f799"
,"#71f5a1"
,"#5cf3aa"
,"#42f1b3"
,"#15efbc"
,"#00ecc4"
,"#00e9cd"
,"#00e6d5"
,"#00e3dd"
,"#00e0e4"
,"#00dcea"
,"#00d8f0"
,"#00d5f5"
,"#00d0f9"
,"#00ccfd"
,"#00c8ff"
,"#00c3ff"
,"#00beff"
,"#00b9ff"
,"#00b4ff"];
var hash = hashStr(name);
var index = hash % colors.length;
return colors[index];
}
function hashStr(str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
var charCode = str.charCodeAt(i);
hash += charCode;
}
return hash;
}
function createChartOverview(data) {
const ctx = document.getElementById('graph-health').getContext('2d');
let high_data = data.stats.length > 1000;
if (chart) {
chart.destroy();
}
let datasets = [{
label: 'Healthy',
data: data.healthyData,
borderColor: 'green',
backgroundColor: 'rgba(0, 255, 0, 0.2)',
fill: true
},
{
label: 'Unhealthy',
data: data.unhealthyData,
borderColor: 'orange',
backgroundColor: 'rgba(241, 90, 34, 0.2)',
fill: true
},
{
label: 'Own Instances Unhealthy',
data: data.user,
pointRadius: 5,
pointBackgroundColor: 'rgba(255, 0, 0, 1)',
fill: false,
datasetIndex: "x",
}];
datasets = datasets.concat(data.stats.map(entry => ({
yAxisID: 'yStats',
label: mapKey(entry.key),
data: entry.data,
borderColor: nameToColor(entry.key),
fill: false,
hidden: (entry.key !== 'total_requests_avg' && entry.key !== 'limited_accs_avg'),
})));
console.log(datasets);
chart = new Chart(ctx, {
type: 'line',
data: {
datasets: datasets,
},
options: {
responsive: false,
maintainAspectRatio: false,
plugins: {
colors: {
enabled: true
},
decimation: {
enabled: high_data,
algorithm: 'min-max',
},
tooltip: {
// callbacks: {
// label: function(context) {
// console.log(context);
// if (context.dataset) {
// if (context.dataset.yAxisID == 'yStats') {
// let dataset = context.dataset.data[context.dataIndex];
// if (!dataset) {
// console.warn(context.dataset);
// console.warn(context.dataset.data[context.dataIndex]);
// }
// let mainValue = dataset.y;
// let detailValue = dataset.additional.total_requests_max;
// return [`${context.dataset.label}: ${mainValue}`,`Max Total Requests: ${detailValue}`];
// } else {
// return `${context.dataset.label}: ${context.formattedValue}`;
// }
// } else {
// console.log(context);
// return null;
// }
// // let label = context.dataset.label || '';
// // if (label) {
// // label += ': ';
// // }
// // if (context.parsed.y !== null) {
// // label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed.y);
// // }
// return 'asdf';
// }
// }
}
},
animation: !high_data,
scales: {
x: {
type: 'time',
time: {
unit: 'day'
},
autoSkip: true,
},
y: {
beginAtZero: true
},
yStats: {
beginAtZero: false
}
},
interaction: {
mode: 'index',
intersect: false
},
}
});
}