routerconsole/jsp/js/peers.js

/**
 * @module peers
 * @description Handles auto-refresh, table sorting, and bandwidth tier count
 * display for the /peers pages (NTCP, SSU, and summary views).
 * @author dr|z3d
 * @license AGPL3 or later
 */

import { refreshElements } from '/js/refreshElements.js';

(function () {
  const ntcpConn = document.getElementById("ntcpconnections");
  const ntcpH3 = document.getElementById("ntcpcon");
  const ntcpTfoot = document.querySelector("#ntcpconnections tfoot");
  const peersNTCP = document.getElementById("peersNTCP");
  const peersSSU = document.getElementById("peersSSU");
  const ssuConn = document.getElementById("udpconnections");
  const ssuH3 = document.getElementById("udpcon");
  const ssuTfoot = document.querySelector("#udpconnections tfoot");
  const summary = document.getElementById("transportSummary");
  const url = document.location;
  const path = location.pathname;
  const query = location.search;
  const queryParams = new URL(url.href).searchParams;
  const REFRESH_INTERVAL = summary ? 5 * 1000 : 10 * 1000;

  let sorterNTCP, sorterSSU;

  if (peersNTCP && typeof sorterNTCP === "undefined") {
    sorterNTCP = new Tablesort(ntcpConn, { descending: true });
  }

  if (peersSSU && typeof sorterSSU === "undefined") {
    sorterSSU = new Tablesort(ssuConn, { descending: true });
  }

  /**
   * Initializes refresh listeners and table sort event handlers.
   * @function initRefresh
   * @returns {void}
   */
  function initRefresh() {
    addSortListeners();
  }

  /**
   * Adds progress bar handlers for beforeSort/afterSort events on peer tables.
   * @function addSortListeners
   * @returns {void}
   */
  function addSortListeners() {
    if (ntcpConn) {
      ntcpConn.addEventListener("beforeSort", function () {
        progressx.show(theme);
        progressx.progress(0.5);
      });
      ntcpConn.addEventListener("afterSort", function () {
        progressx.hide();
      });
    }

    if (ssuConn) {
      ssuConn.addEventListener("beforeSort", function () {
        progressx.show(theme);
        progressx.progress(0.5);
      });
      ssuConn.addEventListener("afterSort", function () {
        progressx.hide();
      });
    }
  }

  /**
   * Counts peers by bandwidth tier (L, M, N, O, P, X) and floodfill status,
   * displaying the counts in the top count element.
   * @function countTiers
   * @returns {void}
   */
  function countTiers() {
    if (ssuTfoot || ntcpTfoot) {
      const tierL = document.querySelectorAll(".rbw.L");
      const tierM = document.querySelectorAll(".rbw.M");
      const tierN = document.querySelectorAll(".rbw.N");
      const tierO = document.querySelectorAll(".rbw.O");
      const tierP = document.querySelectorAll(".rbw.P");
      const tierX = document.querySelectorAll(".rbw.X");
      const isFF = document.querySelectorAll(".isff");
      const isU = document.querySelectorAll(".isU");
      const countL = tierL.length;
      const countM = tierM.length;
      const countN = tierN.length;
      const countO = tierO.length;
      const countP = tierP.length;
      const countX = tierX.length;
      const countFF = isFF.length;
      const countU = isU.length;
      const topCount = document.getElementById("topCount");

      const counter = "<table><tr>" +
        (countL > 0 ? "<td class=rbw>L<span> " + countL + "</span></td>" : "") +
        (countM > 0 ? "<td class=rbw>M<span> " + countM + "</span></td>" : "") +
        (countN > 0 ? "<td class=rbw>N<span> " + countN + "</span></td>" : "") +
        (countO > 0 ? "<td class=rbw>O<span> " + countO + "</span></td>" : "") +
        (countP > 0 ? "<td class=rbw>P<span> " + countP + "</span></td>" : "") +
        (countX > 0 ? "<td class=rbw>X<span> " + countX + "</span></td>" : "") +
        (countU > 0 ? "<td class=rbw id=u>U<span> " + countU + "</span></td>" : "") +
        (countFF > 0 ? "<td class=rbw id=ff>F<span> " + countFF + "</span></td>" : "") +
        "</tr></table>";

      if (topCount) {
        topCount.innerHTML = counter;
      }
    }
  }

  /**
   * Sets up periodic element refresh for the given selector.
   * @function setupEngineRefresh
   * @param {string} selector - CSS selector for tbody elements to refresh
   * @param {string} tbodyId - tbody element id for row-level diffing
   * @returns {void}
   */
  function setupEngineRefresh(selector, tbodyId) {
    const connUrl = queryParams.has("transport") ? path + query : "/peers";
    refreshElements(selector, connUrl, REFRESH_INTERVAL, false, false, tbodyId, tbodyId);
  }

  /**
   * Sets up periodic element refresh for the given selector, requesting
   * only the named server-side elements when fragmentIds is provided.
   * @function setupRefresh
   * @param {string} selector - CSS selector for elements to refresh
   * @param {string} fragmentIds - Comma-joined element ids for contentonly mode
   * @returns {void}
   */
  function setupRefresh(selector, fragmentIds) {
    const fetchUrl = queryParams.has("transport") ? path + query : "/peers";
    refreshElements(selector, fetchUrl, REFRESH_INTERVAL, false, false, fragmentIds);
  }

  /**
   * Sets up periodic refresh for the transport summary section.
   * @function setupSummaryRefresh
   * @returns {void}
   */
  function setupSummaryRefresh() {
    if (!summary) {return;}
    refreshElements("#transportSummary", "/peers", REFRESH_INTERVAL);
  }

  progressx.hide();

  document.addEventListener("DOMContentLoaded", () => {
    initRefresh();
    countTiers();

    if (peersNTCP) {
      setupEngineRefresh("#peersNTCP", "peersNTCP");
      setupRefresh("#ntcpcon, #ntcpconnections tfoot", "ntcpcon,ntcpfoot");
    } else if (peersSSU) {
      setupEngineRefresh("#peersSSU", "peersSSU");
      setupRefresh("#udpcon, #udpconnections tfoot", "ssucon,ssufoot");
    } else {
      setupSummaryRefresh();
    }

    document.addEventListener("refreshComplete", () => {
      if (sorterNTCP) { sorterNTCP.refresh(); }
      else if (sorterSSU) { sorterSSU.refresh(); }
      countTiers();
    });
  });
})();