static/plugins/mailstats.js (169 lines of code) (raw):
let mailstats_json = null;
async function seed_mail_stats() {
mailstats_json = await (await fetch("/api/mailstats")).json();
show_mailstats("collated");
}
async function render_dashboard_mailstats() {
await OAuthGate(seed_mail_stats);
}
function show_mailstats(hostname="collated") {
document.getElementById('page_title').innerText = `Mail Transfer Statistics, ${hostname}`;
document.getElementById('page_description').innerText = "This page is only available to infrastructure staff. If you see a blank chart, that is why.";
const outer_chart_area = document.getElementById('chart_area');
outer_chart_area.innerText = "";
const hostselector = document.createElement('div');
for (const hostoption of Object.keys(mailstats_json)) {
const hostbox = document.createElement('input');
hostbox.type = "radio";
hostbox.checked = hostname === hostoption;
hostbox.id = `hostoption_${hostoption}`;
hostbox.name = "hostoption";
hostbox.addEventListener('click', () => show_mailstats(hostoption));
hostselector.appendChild(hostbox);
const hostlabel = document.createElement('label');
hostlabel.style.marginRight = "24px";
hostlabel.className = "p-1"
hostlabel.setAttribute('for', hostbox.id);
hostlabel.innerText = hostoption;
hostselector.appendChild(hostlabel);
}
outer_chart_area.appendChild(hostselector);
outer_chart_area.appendChild(document.createElement('hr'));
const pending_collated = [];
const my_stats = mailstats_json[hostname];
for (const entry of my_stats) {
pending_collated.push([entry.ts, entry.pending]);
}
const queue_chart = chart_line(
"Mail queue size, past day",
null,
{"Queue size": pending_collated},
{
height: "250px",
width: "1200px"
},
true,
true
);
outer_chart_area.appendChild(queue_chart);
outer_chart_area.appendChild(document.createElement('hr'));
// ---------- RECIPIENTS --------------
// recipient domain breakdown
const r_array = [];
for (const [k,v] of Object.entries(my_stats[my_stats.length-1].pending_by_recipient)) {
r_array.push({name: k, value: v});
}
const r_array_sorted = r_array.slice();
r_array_sorted.sort((a,b) => b.value-a.value);
r_array_sorted.splice(9);
if (r_array_sorted.length < r_array.length) {
const sumval = r_array.reduce((psum, a) => (psum.value ? psum.value : psum) + (r_array_sorted.includes(a) ? 0 : a.value));
r_array_sorted.push({
name: "(other domains)",
value: sumval,
itemStyle: {
color: "#999"
}
});
}
const donut_recipients = chart_pie("Mail Queue by Recipient Domain", "", r_array_sorted, {width: "690px", height: "380px"}, donut=true);
donut_recipients.style.maxWidth = "580px";
donut_recipients.style.height = "380px";
outer_chart_area.appendChild(donut_recipients);
// timeline, recipients - only top 20
let top_domains = {};
let recipient_timeline_collated = {};
let all_domains = [];
for (const entry of my_stats.slice(my_stats.length-48, my_stats.length)) {
for (const domain in entry.pending_by_recipient) {
if (!all_domains.includes(domain)) all_domains.push(domain);
}
}
for (const entry of my_stats.slice(my_stats.length-48, my_stats.length)) {
for (const domain of all_domains) {
const value = entry.pending_by_recipient[domain] || 0;
top_domains[domain] = (top_domains[domain] || 0) + value;
if (!recipient_timeline_collated[domain]) {
recipient_timeline_collated[domain] = [];
}
recipient_timeline_collated[domain].push([entry.ts, value]);
}
}
let top_domains_sorted = Object.keys(top_domains).sort((a,b) => top_domains[b]-top_domains[a]).slice(0,19);
for (const domain in recipient_timeline_collated) {
if (!top_domains_sorted.includes(domain)) delete recipient_timeline_collated[domain];
}
let rd_sorted_for_chart = {};
for (const domain of top_domains_sorted) {
rd_sorted_for_chart[domain] = recipient_timeline_collated[domain];
}
let recipient_timeline_chart = chart_bar(
"Recipients over time, top 20 recipient domains",
"",
rd_sorted_for_chart,
{
height: "360px",
width: "800px"
},
true,
true
);
outer_chart_area.appendChild(recipient_timeline_chart);
outer_chart_area.appendChild(document.createElement('hr'));
// ------------------- SENDERS ---------------
// sender domain breakdown
const s_array = [];
for (const [k,v] of Object.entries(my_stats[my_stats.length-1].pending_by_sender)) {
s_array.push({name: k, value: v});
}
const s_array_sorted = s_array.slice();
s_array_sorted.sort((a,b) => b.value-a.value);
s_array_sorted.splice(9);
if (s_array_sorted.length < s_array.length) {
const sumval = s_array.reduce((psum, a) => (psum.value ? psum.value : psum) + (s_array_sorted.includes(a) ? 0 : a.value));
s_array_sorted.push({
name: "(other domains)",
value: sumval,
itemStyle: {
color: "#999"
}
});
}
const donut_sender = chart_pie("Mail Queue by Sender Domain", "", s_array_sorted, {width: "690px", height: "380px"}, donut=true);
donut_sender.style.maxWidth = "580px";
donut_sender.style.height = "380px";
outer_chart_area.appendChild(donut_sender);
// timeline, sender domains - only top 20
top_domains = {};
let sender_timeline_collated = {};
all_domains = [];
for (const entry of my_stats.slice(my_stats.length-48, my_stats.length)) {
for (const domain in entry.pending_by_sender) {
if (!all_domains.includes(domain)) all_domains.push(domain);
}
}
for (const entry of my_stats.slice(my_stats.length-48, my_stats.length)) {
for (const domain of all_domains) {
const value = entry.pending_by_sender[domain] || 0;
top_domains[domain] = (top_domains[domain] || 0) + value;
if (!sender_timeline_collated[domain]) {
sender_timeline_collated[domain] = [];
}
sender_timeline_collated[domain].push([entry.ts, value]);
}
}
top_domains_sorted = Object.keys(top_domains).sort((a,b) => top_domains[b]-top_domains[a]).slice(0,19);
for (const domain in recipient_timeline_collated) {
if (!top_domains_sorted.includes(domain)) delete sender_timeline_collated[domain];
}
const sd_sorted_for_chart = {};
for (const domain of top_domains_sorted) {
sd_sorted_for_chart[domain] = sender_timeline_collated[domain];
}
const sender_timeline_chart = chart_bar(
"Sender domains over time, top 20 domains",
"",
sd_sorted_for_chart,
{
height: "360px",
width: "800px"
},
true,
true
);
outer_chart_area.appendChild(sender_timeline_chart);
}