mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 15:39:33 +00:00
1517 lines
50 KiB
JavaScript
1517 lines
50 KiB
JavaScript
// 2014-20 - ntop.org
|
|
Date.prototype.format = function (format) { //author: meizz
|
|
var o = {
|
|
"M+": this.getMonth() + 1, //month
|
|
"d+": this.getDate(), //day
|
|
"h+": this.getHours(), //hour
|
|
"m+": this.getMinutes(), //minute
|
|
"s+": this.getSeconds(), //second
|
|
"q+": Math.floor((this.getMonth() + 3) / 3), //quarter
|
|
"S": this.getMilliseconds() //millisecond
|
|
}
|
|
|
|
if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
|
|
(this.getFullYear() + "").substr(4 - RegExp.$1.length));
|
|
for (var k in o) if (new RegExp("(" + k + ")").test(format))
|
|
format = format.replace(RegExp.$1,
|
|
RegExp.$1.length == 1 ? o[k] :
|
|
("00" + o[k]).substr(("" + o[k]).length));
|
|
return format;
|
|
}
|
|
|
|
// Extended disable function
|
|
jQuery.fn.extend({
|
|
disable: function (state) {
|
|
return this.each(function () {
|
|
var $this = $(this);
|
|
if ($this.is('input, button, textarea, select'))
|
|
this.disabled = state;
|
|
else
|
|
$this.toggleClass('disabled', state);
|
|
});
|
|
}
|
|
});
|
|
|
|
const NTOPNG_MIN_VISUAL_VALUE = 0.005;
|
|
|
|
const REGEXES = {
|
|
ipv4: String.raw`^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`,
|
|
ipv6: String.raw`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*)$`,
|
|
domainName: String.raw`^([a-zA-Z]([a-zA-Z]|[0-9])?\.[a-zA-Z]{2,13}|[a-zA-Z0-9]([\-_.a-zA-Z0-9]{1,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,13}|[a-zA-Z0-9]([\-_.a-zA-Z0-9]{1,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,30}\.[a-zA-Z]{2,3})$`,
|
|
port: String.raw`^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`,
|
|
latency: String.raw`^([0-9]*[.])?[0-9]+$`,
|
|
url: String.raw`^(https?\:\/\/[^\/\s]+(\/.*)?)$`,
|
|
emailUrl: String.raw`^smtps?:\/\/[\-a-zA-Z0-9:.]{1,256}$`,
|
|
macAddress: String.raw`^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$`,
|
|
hostname: String.raw`^(?!\s*$)[a-zA-Z0-9._: \-\/]{1,250}|^[a-zA-Z0-9._: \-\/]{1,250}@[0-9]{0,5}`,
|
|
username: String.raw`^[a-zA-Z0-9._@!-?]{3,30}$`,
|
|
singleword: String.raw`^(?=[a-zA-Z0-9._:\-]{3,253}$)(?!.*[_.:\-]{2})[^_.:\-].*[^_.:\-]$`,
|
|
multiword: String.raw`^([a-zA-Z0-9._:\-\s]{3,253})$`,
|
|
email: String.raw`^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[a-zA-Z]{2,})$`,
|
|
emailCommaList: String.raw`^((?:[a-zA-Z0-9.!#$%&'*+\-\/=?^_\`\|~]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*)|([a-zA-Z\d.!#$%&'*+\-\/=?^_\`\|~]{1,128}))(?:,((?:[a-zA-Z0-9.!#$%&'*+\-\/=?^_\`\|~]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*)|([a-zA-Z\d.!#$%&'*+\-\/=?^_\`\|~]{1,128})))*$`,
|
|
https: String.raw`^https?:\/\/.+$`,
|
|
token: String.raw`^[0-9a-f]{32}`,
|
|
score: String.raw`^[0-9]{1,5}`,
|
|
telegram_channel: String.raw`^[0-9\-]{1,15}`,
|
|
password: String.raw`^[\w\/$!\/()=?^*@_-]{5,31}$`,
|
|
tls_certificate: String.raw`^[^=,]+=[^=,]+(,\s[^=,]+=[^=,]+)*$`,
|
|
domain_name_not_strict: String.raw`^[a-zA-Z0-9\-_~]+((\.[a-zA-Z0-9\-_~]+)+)$`,
|
|
non_quoted_text: String.raw`^[a-zA-Z0-9.-_]+$`,
|
|
};
|
|
|
|
export default class NtopUtils {
|
|
|
|
/* Show an overlay to hide loading */
|
|
static toggleOverlays(time = 500) {
|
|
$(`.overlay`).toggle(time);
|
|
}
|
|
|
|
static showOverlays(time = 500) {
|
|
$(`.overlay`).fadeIn(time);
|
|
}
|
|
|
|
static hideOverlays(time = 500) {
|
|
$(`.overlay`).fadeOut(time);
|
|
}
|
|
|
|
static get REGEXES() {
|
|
return REGEXES;
|
|
}
|
|
|
|
static getIPv4RegexWithCIDR() {
|
|
const length = REGEXES.ipv4.length;
|
|
return `${REGEXES.ipv4.substring(0, length - 1)}(\\/?)(\\b([0-9]|[12][0-9]|3[0-2])?\\b)$`;
|
|
}
|
|
|
|
static getIPv6RegexWithCIDR() {
|
|
const length = REGEXES.ipv6.length;
|
|
return `${REGEXES.ipv6.substring(0, length - 1)}(\\/?)\\b([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8])?\\b$`;
|
|
}
|
|
|
|
/**
|
|
* Resolve a hostname by doing a DNS Resolve.
|
|
* @param {string} hostname The hostname to resolve
|
|
*/
|
|
static async resolveDNS(hostname = "ntop.org") {
|
|
|
|
// resolve the hostname by doing a fetch request to the backend
|
|
try {
|
|
const request = await fetch(`${http_prefix}/lua/rest/v2/get/dns/resolve.lua?hostname=${hostname}`);
|
|
const response = await request.json();
|
|
return response;
|
|
}
|
|
catch (err) {
|
|
// prints out the error if the request fails
|
|
console.error(`Something went wrong when resolving hostname: ${err}`)
|
|
}
|
|
|
|
// if the request has failed return a placeholder response
|
|
// indicating the failure
|
|
return { rc: -1, rc_str: "FAILED_HTTP_REQUEST" };
|
|
}
|
|
|
|
/**
|
|
* Replace the inputs which contain the [data-pattern] attribute
|
|
* with the [pattern] attribute.
|
|
*/
|
|
static initDataPatterns() {
|
|
// for each input with the data-pattern attribute
|
|
// substitute the data-pattern with the right regexes
|
|
$(`input[data-pattern]`).each(function () {
|
|
|
|
// if the pattern is empty then print a warn inside the console
|
|
const dataPattern = $(this).data('pattern');
|
|
if (!dataPattern) {
|
|
console.warn(`An empty data-pattern on an input was found!`, this);
|
|
return;
|
|
}
|
|
|
|
// build the regexp pattern for the input
|
|
const pattern = dataPattern.split('|').map(p => REGEXES[p].toString()).join('|');
|
|
// load the pattern
|
|
$(this).attr('pattern', pattern);
|
|
// remove the data-pattern from the input
|
|
$(this).removeAttr('data-pattern');
|
|
});
|
|
}
|
|
|
|
static is_good_ipv4(ipv4) {
|
|
return new RegExp(REGEXES.ipv4).test(ipv4);
|
|
}
|
|
|
|
static is_good_ipv6(ipv6) {
|
|
return new RegExp(REGEXES.ipv6).test(ipv6);
|
|
}
|
|
|
|
static is_mac_address(mac) {
|
|
return new RegExp(REGEXES.macAddress).test(mac);
|
|
}
|
|
|
|
static isNumeric(value) {
|
|
return /^\d+$/.test(value);
|
|
}
|
|
|
|
static is_network_mask(what, optional_mask) {
|
|
var elems = what.split("/");
|
|
var mask = null;
|
|
var ip_addr;
|
|
|
|
if (elems.length != 2) {
|
|
if (!optional_mask)
|
|
return null;
|
|
else
|
|
ip_addr = what;
|
|
} else {
|
|
ip_addr = elems[0];
|
|
|
|
if (!NtopUtils.isNumeric(elems[1]))
|
|
return null;
|
|
|
|
mask = parseInt(elems[1]);
|
|
|
|
if (mask < 0)
|
|
return null;
|
|
}
|
|
|
|
if (NtopUtils.is_good_ipv4(ip_addr)) {
|
|
if (mask === null)
|
|
mask = 32;
|
|
else if (mask > 32)
|
|
return null;
|
|
|
|
return {
|
|
type: "ipv4",
|
|
address: ip_addr,
|
|
mask: mask
|
|
};
|
|
} else if (NtopUtils.is_good_ipv6(elems[0])) {
|
|
if (mask === null)
|
|
mask = 128;
|
|
else if (mask > 128)
|
|
return (false);
|
|
|
|
return {
|
|
type: "ipv6",
|
|
address: ip_addr,
|
|
mask: mask
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static fbits(bits) {
|
|
const sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
|
|
|
if (typeof (bits) === "undefined")
|
|
return "-";
|
|
|
|
if (bits == 0) return '0';
|
|
if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' bps');
|
|
var bits_log1000 = Math.log(bits) / Math.log(1000)
|
|
var i = parseInt(Math.floor(bits_log1000));
|
|
if (i < 0 || isNaN(i)) {
|
|
i = 0;
|
|
} else if (i >= sizes.length) { // prevents overflows
|
|
return "> " + sizes[sizes.length - 1]
|
|
}
|
|
|
|
if (i <= 1) {
|
|
return Math.round(bits / Math.pow(1000, i) * 100) / 100 + ' ' + sizes[i]
|
|
}
|
|
else {
|
|
var ret = parseFloat(bits / Math.pow(1000, i)).toFixed(2)
|
|
if (ret % 1 == 0)
|
|
ret = Math.round(ret)
|
|
return ret + ' ' + sizes[i]
|
|
}
|
|
}
|
|
|
|
static export_rate(eps) {
|
|
if (typeof (eps) === "undefined")
|
|
return "-";
|
|
|
|
var sizes = ['exp/s', 'Kexp/s'];
|
|
if (eps == 0) return '0';
|
|
if ((eps > 0) && (eps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' exps/s');
|
|
var res = NtopUtils.scaleValue(eps, sizes, 1000);
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static exports_format(exports) {
|
|
if (typeof (exports) === "undefined")
|
|
return "-";
|
|
|
|
var exports_label = i18n_ext.exports.toLowerCase();
|
|
|
|
var sizes = [exports_label, 'K ' + exports_label];
|
|
if (exports == 0) return '0';
|
|
if ((exports > 0) && (exports < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' exps/s');
|
|
var res = NtopUtils.scaleValue(exports, sizes, 1000);
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static fbits_from_bytes(bytes) {
|
|
if (typeof (bytes) === "undefined")
|
|
return "-";
|
|
return (NtopUtils.fbits(bytes * 8));
|
|
}
|
|
|
|
static fpackets(pps) {
|
|
if (typeof (pps) === "undefined")
|
|
return "-";
|
|
|
|
var sizes = ['pps', 'Kpps', 'Mpps', 'Gpps', 'Tpps'];
|
|
if (pps == 0) return '0';
|
|
if ((pps > 0) && (pps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' pps');
|
|
var res = NtopUtils.scaleValue(pps, sizes, 1000);
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static fpoints(pps) {
|
|
if (typeof (pps) === "undefined")
|
|
return "-";
|
|
|
|
var sizes = ['pt/s', 'Kpt/s', 'Mpt/s', 'Gpt/s', 'Tpt/s'];
|
|
if (pps == 0) return '0';
|
|
if ((pps > 0) && (pps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' pt/s');
|
|
var res = NtopUtils.scaleValue(pps, sizes, 1000);
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static fflows(fps) {
|
|
if (typeof (fps) === "undefined")
|
|
return "-";
|
|
|
|
var sizes = ['fps', 'Kfps', 'Mfps', 'Gfps', 'Tfps'];
|
|
if (fps == 0) return '0';
|
|
if ((fps > 0) && (fps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' fps');
|
|
var res = NtopUtils.scaleValue(fps, sizes, 1000);
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static fmsgs(mps) {
|
|
if (typeof (mps) === "undefined")
|
|
return "-";
|
|
|
|
var sizes = ['msg/s', 'Kmsg/s', 'Msg/s', 'Gmsg/s', 'Tmsg/s'];
|
|
if (mps == 0) return '0';
|
|
if ((mps > 0) && (mps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' mps');
|
|
var res = NtopUtils.scaleValue(mps, sizes, 1000);
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static fmillis(ms) {
|
|
|
|
if (ms === undefined) return '-';
|
|
const sizes = ['ms'];
|
|
const res = NtopUtils.scaleValue(ms, sizes, 1000);
|
|
return Math.round(res[0] * 100) / 100 + ' ' + res[1];
|
|
}
|
|
|
|
static fnone(val) {
|
|
|
|
if (val === undefined) return '-';
|
|
return Math.round(val * 100) / 100;
|
|
}
|
|
|
|
static falerts(aps) {
|
|
if (typeof (aps) === "undefined")
|
|
return "-";
|
|
|
|
// Round to two decimal digits
|
|
return Math.round(aps * 100) / 100 + ' alerts/s';
|
|
}
|
|
|
|
static fint(value) {
|
|
if (typeof (value) === "undefined")
|
|
return "-";
|
|
|
|
var x = Math.round(value);
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
static ffloat(value) {
|
|
if (typeof (value) === "undefined")
|
|
return "-";
|
|
|
|
var x = Math.round(value * 100) / 100.;
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
static fpercent(value) {
|
|
if (typeof (value) === "undefined")
|
|
return "-";
|
|
|
|
return Math.round(value * 100) / 100 + " %";
|
|
}
|
|
|
|
static percentage(value, total) {
|
|
if (total > 0) {
|
|
var pctg = Math.round((value * 10000) / total)
|
|
|
|
if (pctg > 0) {
|
|
/* Two decimals */
|
|
return (" [ " + (pctg / 100) + " % ] ")
|
|
}
|
|
}
|
|
|
|
return ("")
|
|
}
|
|
|
|
static fdate(when) {
|
|
var epoch = when * 1000;
|
|
var d = new Date(epoch);
|
|
|
|
return (d);
|
|
}
|
|
|
|
static capitaliseFirstLetter(string) {
|
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
}
|
|
|
|
static get_trend(actual, before) {
|
|
if ((actual === undefined) || (before === undefined) || (actual == before)) {
|
|
return ("<i class=\"fas fa-minus\"></i>");
|
|
} else if (actual > before) {
|
|
return ("<i class=\"fas fa-arrow-up\"></i>");
|
|
} else {
|
|
return ("<i class=\"fas fa-arrow-down\"></i>");
|
|
}
|
|
}
|
|
|
|
static abbreviateString(str, len) {
|
|
if (!str)
|
|
return "";
|
|
if (str.length < len)
|
|
return str;
|
|
return str.substring(0, len) + "...";
|
|
}
|
|
|
|
static toFixed2(num) {
|
|
if (!num) return "";
|
|
return num.toFixed(2);
|
|
}
|
|
|
|
// Convert bytes to human readable format
|
|
static bytesToSize(bytes) {
|
|
if (typeof (bytes) === "undefined")
|
|
return "-";
|
|
|
|
var precision = 2;
|
|
var kilobyte = 1024;
|
|
var megabyte = kilobyte * 1024;
|
|
var gigabyte = megabyte * 1024;
|
|
var terabyte = gigabyte * 1024;
|
|
|
|
if ((bytes >= 0) && (bytes < kilobyte))
|
|
if (bytes != 0)
|
|
return parseFloat(bytes.toFixed(precision)) + " Bytes";
|
|
else
|
|
return parseFloat(bytes) + " Bytes";
|
|
|
|
else if ((bytes >= kilobyte) && (bytes < megabyte))
|
|
return parseFloat((bytes / kilobyte).toFixed(precision)) + ' KB';
|
|
else if ((bytes >= megabyte) && (bytes < gigabyte))
|
|
return parseFloat((bytes / megabyte).toFixed(precision)) + ' MB';
|
|
else if ((bytes >= gigabyte) && (bytes < terabyte))
|
|
return parseFloat((bytes / gigabyte).toFixed(precision)) + ' GB';
|
|
else if (bytes >= terabyte)
|
|
return parseFloat((bytes / terabyte).toFixed(precision)) + ' TB';
|
|
else
|
|
return parseFloat(bytes.toFixed(precision)) + ' Bytes';
|
|
}
|
|
|
|
static drawTrend(current, last, withColor) {
|
|
if (current == last) {
|
|
return ("<i class=\"fas fa-minus\"></i>");
|
|
} else if (current > last) {
|
|
return ("<i class=\"fas fa-arrow-up\"" + withColor + "></i>");
|
|
} else {
|
|
return ("<i class=\"fas fa-arrow-down\"></i>");
|
|
}
|
|
}
|
|
|
|
static toggleAllTabs(enabled) {
|
|
if (enabled === true)
|
|
$("#historical-tabs-container").find("li").removeClass("disabled").find("a").attr("data-toggle", "tab");
|
|
else
|
|
$("#historical-tabs-container").find("li").addClass("disabled").find("a").removeAttr("data-toggle");
|
|
}
|
|
|
|
static disableAllDropdownsAndTabs() {
|
|
$("select").each(function () {
|
|
$(this).prop("disabled", true);
|
|
});
|
|
NtopUtils.toggleAllTabs(false)
|
|
}
|
|
|
|
static enableAllDropdownsAndTabs() {
|
|
$("select").each(function () {
|
|
$(this).prop("disabled", false);
|
|
});
|
|
NtopUtils.toggleAllTabs(true)
|
|
}
|
|
|
|
static capitalize(s) {
|
|
return s && s[0].toUpperCase() + s.slice(1);
|
|
}
|
|
|
|
static addCommas(nStr) {
|
|
nStr += '';
|
|
var x = nStr.split('.');
|
|
var x1 = x[0];
|
|
var x2 = x.length > 1 ? '.' + x[1] : '';
|
|
var rgx = /(\d+)(\d{3})/;
|
|
while (rgx.test(x1)) {
|
|
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
|
}
|
|
return x1 + x2;
|
|
}
|
|
|
|
static scaleValue(val, sizes, scale, decimals) {
|
|
if (val == 0) return [0, sizes[0]];
|
|
let factor = decimals ? (10 * decimals) : 10;
|
|
|
|
var i = parseInt(Math.floor(Math.log(val) / Math.log(scale)));
|
|
if (i < 0 || isNaN(i)) {
|
|
i = 0;
|
|
} else if (i >= sizes.length) {
|
|
i = sizes.length - 1;
|
|
}
|
|
|
|
return [Math.round((val / Math.pow(scale, i)) * factor) / factor, sizes[i]];
|
|
}
|
|
|
|
static formatValue(val, decimals) {
|
|
var sizes = ['', 'K', 'M', 'G', 'T'];
|
|
if (val == 0) return '0';
|
|
if ((val > 0) && (val < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE);
|
|
if (decimals == undefined) decimals = 0;
|
|
var res = NtopUtils.scaleValue(val, sizes, 1000, decimals);
|
|
|
|
return res[0] + res[1];
|
|
}
|
|
|
|
static formatPackets(n) {
|
|
return (NtopUtils.addCommas(n.toFixed(0)) + " Pkts");
|
|
}
|
|
|
|
static bytesToVolume(bytes) {
|
|
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
if (bytes == 0) return '0 Bytes';
|
|
if ((bytes > 0) && (bytes < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " Bytes");
|
|
var res = NtopUtils.scaleValue(bytes, sizes, 1024);
|
|
|
|
return parseFloat(res[0]) + " " + res[1];
|
|
};
|
|
|
|
static bytesToVolumeAndLabel(bytes) {
|
|
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
if (bytes == 0) return '0 Bytes';
|
|
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
|
return [(bytes / Math.pow(1024, i)).toFixed(2), sizes[i]];
|
|
};
|
|
|
|
static bitsToSize(bits, factor) {
|
|
factor = factor || 1000;
|
|
var sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
|
if (bits == 0) return '0 bps';
|
|
if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " bps");
|
|
var res = NtopUtils.scaleValue(bits, sizes, factor);
|
|
|
|
return res[0].toFixed(2) + " " + res[1];
|
|
};
|
|
|
|
static bitsToSize_no_comma(bits, factor) {
|
|
factor = factor || 1000;
|
|
var sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
|
if (bits == 0) return '0 bps';
|
|
if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " bps");
|
|
var res = NtopUtils.scaleValue(bits, sizes, factor);
|
|
|
|
return res[0] + " " + res[1];
|
|
};
|
|
|
|
static secondsToTime(seconds) {
|
|
|
|
if (seconds < 1) {
|
|
return ("< 1 sec")
|
|
}
|
|
|
|
let days = Math.floor(seconds / 86400)
|
|
let hours = Math.floor((seconds / 3600) - (days * 24))
|
|
let minutes = Math.floor((seconds / 60) - (days * 1440) - (hours * 60))
|
|
let sec = seconds % 60
|
|
let msg = "", msg_array = []
|
|
|
|
if (days > 0) {
|
|
let years = Math.floor(days / 365)
|
|
|
|
if (years > 0) {
|
|
days = days % 365
|
|
|
|
msg = years + " year"
|
|
if (years > 1) {
|
|
msg += "s"
|
|
}
|
|
|
|
msg_array.push(msg)
|
|
msg = ""
|
|
}
|
|
msg = days + " day"
|
|
if (days > 1) { msg += "s" }
|
|
msg_array.push(msg)
|
|
msg = ""
|
|
}
|
|
|
|
if (hours > 0) {
|
|
if (hours < 10) { msg = "0" }
|
|
msg += hours + ":";
|
|
}
|
|
|
|
if (minutes < 10) { msg += "0" }
|
|
msg += minutes + ":";
|
|
if (sec < 10) { msg += "0" }
|
|
msg += sec;
|
|
msg_array.push(msg)
|
|
|
|
return msg_array.join(", ")
|
|
}
|
|
|
|
static msecToTime(msec) {
|
|
if (msec >= 1000) {
|
|
return NtopUtils.secondsToTime(msec / 1000);
|
|
} else {
|
|
var x = Math.round(msec * 1000) / 1000.;
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " ms";
|
|
}
|
|
}
|
|
|
|
static epoch2Seen(epoch) {
|
|
/* 08/01/13 15:12:37 [18 min, 13 sec ago] */
|
|
var d = new Date(epoch * 1000);
|
|
var tdiff = Math.floor(((new Date()).getTime() / 1000) - epoch);
|
|
|
|
return (d.format("dd/MM/yyyy hh:mm:ss") + " [" + NtopUtils.secondsToTime(tdiff) + " ago]");
|
|
}
|
|
|
|
/* ticks for graph x axis */
|
|
static graphGetXAxisTicksFormat(diff_epoch) {
|
|
var tickFormat;
|
|
|
|
if (diff_epoch <= 86400) {
|
|
tickFormat = "%H:%M:%S";
|
|
} else if (diff_epoch <= 2 * 86400) {
|
|
tickFormat = "%b %e, %H:%M:%S";
|
|
} else {
|
|
tickFormat = "%b %e";
|
|
}
|
|
|
|
return (tickFormat);
|
|
}
|
|
|
|
static paramsExtend(defaults, override) {
|
|
return $.extend({}, defaults, override);
|
|
}
|
|
|
|
static paramsToForm(form, params) {
|
|
form = $(form);
|
|
|
|
for (var k in params) {
|
|
if (params.hasOwnProperty(k)) {
|
|
var input = $('<input type="hidden" name="' + k + '" value="' + params[k] + '">');
|
|
input.appendTo(form);
|
|
}
|
|
}
|
|
|
|
return form;
|
|
}
|
|
|
|
/*
|
|
* This function creates a javascript object where each k->v pair of the input object
|
|
* translates into two pairs in the output object: a key_[i]->k and a val_[i]->v, where
|
|
* i is an incremental index.
|
|
*
|
|
* The output object can then be serialized to an URL. This conversion is required for
|
|
* handling special characters: since ntopng strips special characters in _GET keys,
|
|
* _GET values must be used.
|
|
*
|
|
* This function performs the inverse conversion of lua paramsPairsDecode.
|
|
*
|
|
*/
|
|
static paramsPairsEncode(params) {
|
|
var i = 0;
|
|
var res = {};
|
|
|
|
for (var k in params) {
|
|
res["key_" + i] = k;
|
|
res["val_" + i] = params[k];
|
|
i = i + 1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hostkey2hostInfo(host_key) {
|
|
var info;
|
|
|
|
host_key = host_key.replace(/____/g, ":");
|
|
host_key = host_key.replace(/___/g, "/");
|
|
host_key = host_key.replace(/__/g, ".");
|
|
|
|
info = host_key.split("@");
|
|
return (info);
|
|
}
|
|
|
|
static handle_tab_state(nav_object, default_tab) {
|
|
$('a', nav_object).click(function (e) {
|
|
e.preventDefault();
|
|
});
|
|
|
|
// store the currently selected tab in the hash value
|
|
$(" > li > a", nav_object).on("shown.bs.tab", function (e) {
|
|
var id = $(e.target).attr("href").substr(1);
|
|
if (history.replaceState) {
|
|
// this will prevent the 'jump' to the hash
|
|
history.replaceState(null, null, "#" + id);
|
|
} else {
|
|
// fallback
|
|
window.location.hash = id;
|
|
}
|
|
});
|
|
|
|
// on load of the page: switch to the currently selected tab
|
|
var hash = window.location.hash;
|
|
if (!hash) hash = "#" + default_tab;
|
|
$('a[href="' + hash + '"]', nav_object).tab('show');
|
|
}
|
|
|
|
static _add_find_host_link(form, name, data) {
|
|
$('<input>').attr({
|
|
type: 'hidden',
|
|
id: name,
|
|
name: name,
|
|
value: data,
|
|
}).appendTo(form);
|
|
}
|
|
|
|
/* Used while searching hosts a and macs with typeahead */
|
|
static makeFindHostBeforeSubmitCallback(http_prefix) {
|
|
return function (form, data) {
|
|
if (data.context && data.context == "historical") {
|
|
form.attr("action", http_prefix + "/lua/pro/db_search.lua");
|
|
if (data.type == "ip") {
|
|
NtopUtils._add_find_host_link(form, "ip", data.ip);
|
|
} else if (data.type == "mac") {
|
|
NtopUtils._add_find_host_link(form, "mac", data.mac);
|
|
} else if (data.type == "community_id") {
|
|
NtopUtils._add_find_host_link(form, "community_id", data.community_id);
|
|
} else if (data.type == "ja3_client") {
|
|
NtopUtils._add_find_host_link(form, "ja3_client", data.ja3_client);
|
|
} else if (data.type == "ja3_server") {
|
|
NtopUtils._add_find_host_link(form, "ja3_server", data.ja3_server);
|
|
} else /* "hostname" */ {
|
|
NtopUtils._add_find_host_link(form, "name", data.hostname ? data.hostname : data.name);
|
|
}
|
|
} else {
|
|
if (data.type == "mac") {
|
|
form.attr("action", http_prefix + "/lua/mac_details.lua");
|
|
} else if (data.type == "network") {
|
|
form.attr("action", http_prefix + "/lua/hosts_stats.lua");
|
|
NtopUtils._add_find_host_link(form, "network", data.network);
|
|
} else if (data.type == "snmp") {
|
|
form.attr("action", http_prefix + "/lua/pro/enterprise/snmp_interface_details.lua");
|
|
NtopUtils._add_find_host_link(form, "snmp_port_idx", data.snmp_port_idx);
|
|
} else if (data.type == "snmp_device") {
|
|
form.attr("action", http_prefix + "/lua/pro/enterprise/snmp_device_details.lua");
|
|
} else if (data.type == "asn") {
|
|
form.attr("action", http_prefix + "/lua/hosts_stats.lua");
|
|
NtopUtils._add_find_host_link(form, "asn", data.asn);
|
|
} else {
|
|
form.attr("action", http_prefix + "/lua/host_details.lua");
|
|
NtopUtils._add_find_host_link(form, "mode", "restore");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static tstampToDateString(html_tag, format, tdiff) {
|
|
tdiff = tdiff || 0;
|
|
var timestamp = parseInt(html_tag.html()) + tdiff;
|
|
var localized = d3.time.format(format)(new Date(timestamp * 1000));
|
|
html_tag.html(localized).removeClass("hidden");
|
|
return localized;
|
|
}
|
|
|
|
static noHtml(s) {
|
|
return s.replace(/<[^>]+>/g, '');
|
|
}
|
|
|
|
static cleanCustomHostUrl(host) {
|
|
/* Remove starting http(s). */
|
|
return host
|
|
.replace(/^http:\/\//gi, '')
|
|
.replace(/^https:\/\//gi, '')
|
|
/* Remove starting www. */
|
|
.replace(/^www\./gi, '')
|
|
/* Remove non-allowed characters */
|
|
.replace(/[^0-9a-zA-Z\.:\/_-]/gi, '');
|
|
}
|
|
|
|
/* https://stackoverflow.com/questions/2090551/parse-query-string-in-javascript */
|
|
static parseQuery(queryString) {
|
|
var query = {};
|
|
var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
|
|
for (var i = 0; i < pairs.length; i++) {
|
|
var pair = pairs[i].split('=');
|
|
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
|
|
}
|
|
return query;
|
|
}
|
|
|
|
static getHistoryParameters(params) {
|
|
var cur_params = NtopUtils.parseQuery(window.location.search);
|
|
var new_params = $.extend(cur_params, params);
|
|
var new_query = "?" + $.param(new_params, true);
|
|
var baseUrl = [location.protocol, '//', location.host, location.pathname].join('');
|
|
|
|
return baseUrl + new_query;
|
|
}
|
|
|
|
// return true if the status code is different from 200
|
|
static check_status_code(status_code, status_text, $error_label) {
|
|
|
|
const is_different = status_code != 200;
|
|
|
|
if (is_different && $error_label != null) {
|
|
|
|
let message = i18n_ext.request_failed_message;
|
|
if (status_code && status_text) {
|
|
message += `: ${status_code} - ${status_text}`;
|
|
}
|
|
|
|
$error_label.text(message).show();
|
|
}
|
|
else if (is_different && $error_label == null) {
|
|
alert(`${i18n_ext.request_failed_message}: ${status_code} - ${status_text}`);
|
|
}
|
|
|
|
return is_different;
|
|
}
|
|
|
|
// To be used in conjunction with httpdocs/templates/config_list_components/import_modal.html
|
|
static importModalHelper(params) {
|
|
|
|
if (!params.loadConfigXHR) { throw ("importModalHelper:: Missing 'loadConfigXHR' param"); }
|
|
|
|
$(`input#import-input`).on('change', function () {
|
|
$(`#btn-confirm-import`).removeAttr("disabled");
|
|
});
|
|
|
|
$(`#import-modal`).on('hidden.bs.modal', function () {
|
|
$(`#import-input`).val('');
|
|
$("#import-error").hide().removeClass('text-warning').addClass('invalid-feedback');
|
|
$(`#btn-confirm-import`).attr("disabled", "disabled");
|
|
});
|
|
|
|
$("#import-modal").on("submit", "form", function (e) {
|
|
|
|
e.preventDefault();
|
|
|
|
const $button = $('#btn-confirm-import');
|
|
$button.attr("disabled", "");
|
|
|
|
// read configuration file
|
|
const file = $('#import-input')[0].files[0];
|
|
|
|
if (!file) {
|
|
$("#import-error").text(`${i18n_ext.no_file}`).show();
|
|
$button.removeAttr("disabled");
|
|
return;
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
reader.readAsText(file, "UTF-8");
|
|
|
|
reader.onload = function (responseJSON) {
|
|
// Client-side configuration file format check
|
|
let jsonConfiguration = null
|
|
try {
|
|
jsonConfiguration = JSON.parse(reader.result);
|
|
} catch (e) {
|
|
|
|
ToastUtils.showToast({
|
|
id: 'import-error-toast',
|
|
level: 'error',
|
|
title: i18n("error"),
|
|
body: e,
|
|
delay: 3000
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!jsonConfiguration) {
|
|
$("#import-error").text(i18n_ext.rest_consts[responseJSON.rc_str] || 'Not Implemented Yet').show();
|
|
$button.removeAttr("disabled");
|
|
return;
|
|
}
|
|
|
|
// Submit configuration file
|
|
params.loadConfigXHR(reader.result)
|
|
.done((response, status, xhr) => {
|
|
|
|
if (response.rc < 0) {
|
|
$("#import-error").text(response.rc_str).show();
|
|
return;
|
|
}
|
|
|
|
// if the operation was successful call the successCallback
|
|
if (params.successCallback) {
|
|
params.successCallback(response);
|
|
}
|
|
|
|
const key = $(`input[name='configuration']:checked`).val();
|
|
|
|
const body = (key == 'all')
|
|
? i18n("manage_configurations.messages.import_all_success")
|
|
: i18n("manage_configurations.messages.import_success");
|
|
|
|
// show a success alert message
|
|
ToastUtils.showToast({
|
|
id: 'import-configuration-alert',
|
|
level: 'success',
|
|
title: i18n("success"),
|
|
body: body,
|
|
delay: 2000
|
|
});
|
|
|
|
$("#import-modal").modal('hide');
|
|
|
|
})
|
|
.fail(({ responseJSON }) => {
|
|
|
|
const PARTIAL_IMPORT_RC = -28;
|
|
|
|
if (params.failureCallback) {
|
|
params.failureCallback(responseJSON);
|
|
}
|
|
|
|
if (responseJSON && responseJSON.rc > 0) return;
|
|
if (responseJSON.rc == PARTIAL_IMPORT_RC)
|
|
$(`#import-error`).removeClass('invalid-feedback').addClass('text-warning');
|
|
|
|
$("#import-error").text(i18n_ext.rest_consts[responseJSON.rc_str] || i18n_ext.FAILED_HTTP_REQUEST).show();
|
|
|
|
})
|
|
.always(() => {
|
|
$button.removeAttr("disabled");
|
|
});
|
|
};
|
|
});
|
|
}
|
|
|
|
static serializeFormArray(serializedArray) {
|
|
const serialized = {};
|
|
serializedArray.forEach((obj) => {
|
|
/* if the object is an array */
|
|
if (obj.name.includes('[]')) {
|
|
return;
|
|
}
|
|
else {
|
|
|
|
// clean the string
|
|
if (typeof obj.value === "string") {
|
|
obj.value = obj.value.trim();
|
|
}
|
|
serialized[obj.name] = obj.value;
|
|
}
|
|
});
|
|
return serialized;
|
|
}
|
|
|
|
static cleanForm(formSelector) {
|
|
/* remove validation fields and tracks */
|
|
$(formSelector).find('input,select,textarea').each(function (i, input) {
|
|
$(this).removeClass(`is-valid`).removeClass(`is-invalid`);
|
|
});
|
|
/* reset all the values */
|
|
$(formSelector)[0].reset();
|
|
}
|
|
|
|
/**
|
|
* Make a fetch call with a timeout option
|
|
*/
|
|
static fetchWithTimeout(uri, options = {}, time = 5000) {
|
|
|
|
const controller = new AbortController()
|
|
const config = { ...options, signal: controller.signal }
|
|
|
|
return fetch(uri, config)
|
|
.then((response) => {
|
|
if (!response.ok) {
|
|
throw new Error(`${response.status}: ${response.statusText}`)
|
|
}
|
|
return response
|
|
})
|
|
.catch((error) => {
|
|
if (error.name === 'AbortError') {
|
|
throw new Error('Response timed out')
|
|
}
|
|
})
|
|
}
|
|
|
|
static setPref(action, csrf, success, failure) {
|
|
|
|
if (action == undefined) {
|
|
console.warn("An action key must be defined to set a preference!");
|
|
return;
|
|
}
|
|
|
|
const empty = () => { };
|
|
const request = $.post(`${http_prefix}/lua/update_prefs.lua`, { action: action, csrf: csrf });
|
|
request.done(success || empty);
|
|
request.fail(failure || empty);
|
|
}
|
|
|
|
/**
|
|
* Glue strings contained in array separated by a comma.
|
|
* @param {array} array The array of strings. I.e. ["Hello", "World"]
|
|
* @param {number} limit How many words the string contains
|
|
*
|
|
* @return {string} A string built by array's elements. i.e: "Hello, World"
|
|
*/
|
|
static arrayToListString(array, limit) {
|
|
|
|
if (array == undefined) return "";
|
|
|
|
if (array.length > limit) {
|
|
return array.slice(0, limit).join(", ") + ` ${i18n_ext.and_x_more.replace('$num', array.length - limit)}`;
|
|
}
|
|
|
|
return array.slice(0, limit).join(", ");
|
|
}
|
|
|
|
static buildURL(location, params = {}, hasReferer = false, refererParams = {}) {
|
|
|
|
const url = new URL(location, window.location);
|
|
|
|
for (const [name, value] of Object.entries(params)) {
|
|
if (value || value === 0)
|
|
url.searchParams.set(name, value);
|
|
continue;
|
|
}
|
|
|
|
if (hasReferer) {
|
|
|
|
const refUrl = new URL(window.location.href);
|
|
for (const [name, value] of Object.entries(refererParams)) {
|
|
if (!value) continue;
|
|
refUrl.searchParams.set(name, value);
|
|
}
|
|
|
|
url.searchParams.set('referer', refUrl.toString());
|
|
}
|
|
|
|
return url.toString();
|
|
}
|
|
|
|
static getEditPoolLink(href, poolId) {
|
|
const url = new URL(href, window.location);
|
|
url.searchParams.set('pool_id', poolId);
|
|
return url.toString();
|
|
}
|
|
|
|
static getPoolLink(poolType, poolId = 0) {
|
|
return `${http_prefix}/lua/rest/v2/get/${poolType}/pools.lua?pool=${poolId}`;
|
|
}
|
|
|
|
static async getPool(poolType, id = 0) {
|
|
|
|
if (poolType === null) throw 'A pool type must be defined!';
|
|
|
|
try {
|
|
|
|
const request = await fetch(NtopUtils.getPoolLink(poolType, id));
|
|
const pool = await request.json();
|
|
|
|
if (pool.rc < 0) {
|
|
return [false, {}];
|
|
}
|
|
|
|
return [true, pool.rsp[0]];
|
|
}
|
|
catch (err) {
|
|
return [false, {}];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save the scale of element inside the local storage
|
|
* @param {object} $element
|
|
* @param {object} scale
|
|
*/
|
|
static saveElementScale($element, scale = { width: 0, height: 0 }) {
|
|
|
|
const key = NtopUtils.generateScaleElementKey($element);
|
|
localStorage.setItem(key, JSON.stringify(scale));
|
|
}
|
|
|
|
static generateScaleElementKey($element) {
|
|
let identificator;
|
|
const page = location.pathname;
|
|
const elementId = $element.attr('id');
|
|
|
|
if (elementId !== "") {
|
|
identificator = elementId;
|
|
}
|
|
else {
|
|
const className = $element.attr('class');
|
|
identificator = className;
|
|
}
|
|
|
|
const key = `${identificator}-${page}-scale`;
|
|
return key;
|
|
}
|
|
|
|
/**
|
|
* Load the old scale value ofx element from the local storage
|
|
* @param {object} $element
|
|
*/
|
|
static loadElementScale($element) {
|
|
|
|
const key = NtopUtils.generateScaleElementKey($element);
|
|
const currentValue = localStorage.getItem(key);
|
|
if (currentValue == null) return undefined;
|
|
|
|
return JSON.parse(currentValue);
|
|
}
|
|
|
|
static fillFieldIfValid($field, value) {
|
|
|
|
if (value === undefined) {
|
|
$field.val('');
|
|
}
|
|
else {
|
|
$field.val(value);
|
|
}
|
|
|
|
}
|
|
|
|
static copyToClipboard(text, item) {
|
|
const el = document.createElement('textarea');
|
|
el.value = text;
|
|
el.setAttribute('readonly', '');
|
|
el.style.position = 'absolute';
|
|
el.style.left = '-9999px';
|
|
document.body.appendChild(el);
|
|
el.select();
|
|
document.execCommand('copy');
|
|
document.body.removeChild(el);
|
|
$(item).attr("title", "Copied!").tooltip("dispose").tooltip().tooltip("show");
|
|
$(item).removeAttr("data-bs-original-title")
|
|
$(item).attr("title", text)
|
|
}
|
|
|
|
static stripTags(html) {
|
|
let t = document.createElement("div");
|
|
t.innerHTML = html;
|
|
return t.textContent || t.innerText || "";
|
|
}
|
|
|
|
static shortenLabel(label, len, last_char) {
|
|
let shortened_label = label
|
|
if (label.length > len + 5) {
|
|
if (last_char) {
|
|
let last_index = label.lastIndexOf(last_char)
|
|
const requested_label = label.slice(last_index)
|
|
if (len > last_index)
|
|
len = last_index
|
|
shortened_label = label.slice(0, len) + "... " + requested_label
|
|
} else {
|
|
shortened_label = label.slice(0, len) + "...";
|
|
}
|
|
}
|
|
|
|
return shortened_label
|
|
}
|
|
|
|
static sortAlphabetically(a, b) {
|
|
const nameA = a.label?.toUpperCase(); // ignore upper and lowercase
|
|
const nameB = b.label?.toUpperCase(); // ignore upper and lowercase
|
|
if (nameA < nameB) { return -1; }
|
|
if (nameA > nameB) { return 1; }
|
|
return 0;
|
|
}
|
|
|
|
/* This function, given a name and a value, return a string
|
|
* formatted in the following way:
|
|
* name [value]
|
|
* If max_name_len is different from 0, then it's going to cut the name string
|
|
* to max_name_len
|
|
*/
|
|
static formatNameValue(name, value, max_name_len) {
|
|
let label = name;
|
|
if (name != value) {
|
|
if (max_name_len && typeof (max_name_len) == 'number')
|
|
label = this.shortenLabel(label, max_name_len, '.');
|
|
|
|
label = `${label} [${value}]`
|
|
}
|
|
return label
|
|
}
|
|
|
|
/* This function, remove from a string the VLAN 0
|
|
* name@0 -> name
|
|
*/
|
|
static removeVlan(name) {
|
|
let label = name
|
|
const vlan_index = label.lastIndexOf('@');
|
|
if (vlan_index != -1) {
|
|
const vlan = label.slice(vlan_index + 1);
|
|
if (vlan == 0) {
|
|
label = label.slice(0, vlan_index);
|
|
}
|
|
}
|
|
|
|
return label
|
|
}
|
|
|
|
/* Format an object with label and value from a column row */
|
|
static formatGenericObj(obj, row) {
|
|
let label = obj.label ? obj.label : obj.value;
|
|
let key = obj.value;
|
|
return label;
|
|
}
|
|
|
|
/* Format a country from a column object */
|
|
static formatCountry(obj, row) {
|
|
let country_code = obj.value;
|
|
let label = obj.label ? obj.label : obj.value;
|
|
return `${label} <img src="/dist/images/blank.gif" class="flag flag-${country_code.toLowerCase()}">`;
|
|
}
|
|
|
|
/* Format an host from a column object */
|
|
static formatHost(obj, row, is_client) {
|
|
let label = "";
|
|
|
|
if (!obj) {
|
|
return label;
|
|
}
|
|
|
|
/* Link */
|
|
let host_key = obj.ip;
|
|
if (row.vlan_id && row.vlan_id.value)
|
|
host_key = host_key + '@' + row.vlan_id.value;
|
|
|
|
/* Label */
|
|
label = obj.label ? obj.label : obj.value;
|
|
if (row.vlan_id && row.vlan_id.label)
|
|
label += `@${row.vlan_id.label}`;
|
|
|
|
const url = NtopUtils.buildURL(`${http_prefix}/lua/host_details.lua`, { host: host_key });
|
|
label = `<a href="${url}">${label}</a>`;
|
|
|
|
/* Country */
|
|
let country_obj = is_client ? row.cli_country : row.srv_country;
|
|
if (!country_obj && row.country) country_obj = row.country;
|
|
if (country_obj && country_obj.value)
|
|
label += ` <img src="${http_prefix}/dist/images/blank.gif" class="flag flag-${country_obj.value.toLowerCase()}" title="${country_obj.title}"></a>`;
|
|
|
|
return label;
|
|
}
|
|
|
|
/* Format a network from a column object */
|
|
static formatNetwork(obj, row) {
|
|
let label = "";
|
|
|
|
if (!obj) {
|
|
return label;
|
|
}
|
|
|
|
/* Link */
|
|
let network_key = obj.value;
|
|
|
|
/* Label */
|
|
label = obj.label ? obj.label : obj.value;
|
|
if (row.vlan_id && row.vlan_id.label)
|
|
label += `@${row.vlan_id.label}`;
|
|
|
|
const url = NtopUtils.buildURL(`${http_prefix}/lua/hosts_stats.lua`, { network: network_key });
|
|
label = `<a href="${url}">${label}</a>`;
|
|
|
|
return label;
|
|
}
|
|
|
|
/* This function converts a mac address to a string*/
|
|
static convertMACAddress(a) {
|
|
return a.toLowerCase().replace(/[^a-f0-9]/g, '');
|
|
}
|
|
/* This function converts an ip to a number equale to the ip but without . or :: in case of ipv6
|
|
* this is needed in case of ordering
|
|
*/
|
|
static convertIPAddress(a) {
|
|
var i, item;
|
|
var m, n, t;
|
|
var x, xa;
|
|
|
|
if (!a) {
|
|
return 0;
|
|
}
|
|
|
|
a = a.replace(/<[\s\S]*?>/g, "");
|
|
//IPv4:Port
|
|
t = a.split(":");
|
|
if (t.length == 2) {
|
|
m = t[0].split(".");
|
|
}
|
|
else {
|
|
m = a.split(".");
|
|
}
|
|
n = a.split(":");
|
|
x = "";
|
|
xa = "";
|
|
|
|
if (m.length == 4) {
|
|
// IPV4
|
|
for (i = 0; i < m.length; i++) {
|
|
item = m[i];
|
|
|
|
if (item.length == 1) {
|
|
x += "00" + item;
|
|
}
|
|
else if (item.length == 2) {
|
|
x += "0" + item;
|
|
}
|
|
else {
|
|
x += item;
|
|
}
|
|
}
|
|
}
|
|
else if (n.length > 0) {
|
|
// IPV6
|
|
var count = 0;
|
|
for (i = 0; i < n.length; i++) {
|
|
item = n[i];
|
|
|
|
if (i > 0) {
|
|
xa += ":";
|
|
}
|
|
|
|
if (item.length === 0) {
|
|
count += 0;
|
|
}
|
|
else if (item.length == 1) {
|
|
xa += "000" + item;
|
|
count += 4;
|
|
}
|
|
else if (item.length == 2) {
|
|
xa += "00" + item;
|
|
count += 4;
|
|
}
|
|
else if (item.length == 3) {
|
|
xa += "0" + item;
|
|
count += 4;
|
|
}
|
|
else {
|
|
xa += item;
|
|
count += 4;
|
|
}
|
|
}
|
|
|
|
// Padding the ::
|
|
n = xa.split(":");
|
|
var paddDone = 0;
|
|
|
|
for (i = 0; i < n.length; i++) {
|
|
item = n[i];
|
|
|
|
if (item.length === 0 && paddDone === 0) {
|
|
for (var padding = 0; padding < (32 - count); padding++) {
|
|
x += "0";
|
|
paddDone = 1;
|
|
}
|
|
}
|
|
else {
|
|
x += item;
|
|
}
|
|
}
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
/* Format an AS from a column object */
|
|
static formatASN(obj, row) {
|
|
let label = "";
|
|
|
|
if (!obj) {
|
|
return label;
|
|
}
|
|
|
|
/* Link */
|
|
let asn_key = obj.value;
|
|
|
|
/* Label */
|
|
label = obj.label ? obj.label : obj.value;
|
|
|
|
const url = NtopUtils.buildURL(`${http_prefix}/lua/hosts_stats.lua`, { asn: asn_key });
|
|
label = `<a href="${url}">${label}</a>`;
|
|
|
|
return label;
|
|
}
|
|
|
|
static createProgressBar(percentage) {
|
|
return `<div class="d-flex flex-row align-items-center">
|
|
<div class="col-9 progress">
|
|
<div class="progress-bar bg-warning" aria-valuenow="${percentage}" aria-valuemin="0" aria-valuemax="100" style="width: ${percentage}%;">
|
|
</div>
|
|
</div>
|
|
<div class="col"> ${percentage} %</div>
|
|
</div>`
|
|
}
|
|
|
|
static createBreakdown(percentage_1, percentage_2, label_1, label_2) {
|
|
if (percentage_1 == 0 && percentage_2 == 0) return ''
|
|
return `<div class="d-flex flex-row align-items-center">
|
|
<div class="col-12 progress">
|
|
<div class="progress-bar bg-warning" aria-valuenow="${percentage_1}" aria-valuemin="0" aria-valuemax="100" style="width: ${percentage_1}%;">${label_1}</div>
|
|
<div class="progress-bar bg-success" aria-valuenow="${percentage_2}" aria-valuemin="0" aria-valuemax="100" style="width: ${percentage_2}%;">${label_2}</div>
|
|
</div>
|
|
</div>`
|
|
}
|
|
|
|
/* Return the number of rows available in a table */
|
|
static getNumTableRows() {
|
|
return [10, 20, 50, 100];
|
|
}
|
|
|
|
static formatApexChartLabelFromXandName({ series, seriesIndex, dataPointIndex, w }) {
|
|
const serie = w.config.series[seriesIndex]["data"][dataPointIndex];
|
|
const name = serie["name"]
|
|
const y_value = serie["y"];
|
|
const host_name = serie["meta"]["label"];
|
|
|
|
const x_axis_title = w.config.xaxis.title.text;
|
|
const y_axis_title = w.config.yaxis[0].title.text;
|
|
|
|
return (`
|
|
<div class='apexcharts-theme-light apexcharts-active' id='test'>
|
|
<div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
|
|
${host_name}
|
|
</div>
|
|
<div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
|
|
<div class='apexcharts-tooltip-text text-left'>
|
|
<b>${x_axis_title}</b>: ${name}
|
|
</div>
|
|
<div class='apexcharts-tooltip-text text-left'>
|
|
<b>${y_axis_title}</b>: ${y_value}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`)
|
|
}
|
|
|
|
static apexChartJumpToAlerts(event, chartContext, config) {
|
|
const { seriesIndex, dataPointIndex } = config;
|
|
const { series } = config.config;
|
|
if (seriesIndex === -1) return;
|
|
if (series === undefined) return;
|
|
|
|
const serie = series[seriesIndex];
|
|
const base_url = serie.base_url || series[0]['base_url']
|
|
const default_url = serie.start_url || series[0]['start_url']
|
|
if (base_url != null && default_url != null) {
|
|
const search = serie.data[dataPointIndex].meta.url_query;
|
|
location.href = `${base_url}?${default_url}${search}`;
|
|
}
|
|
}
|
|
|
|
|
|
static apexChartJumpToHostDetails(event, chartContext, config) {
|
|
const { seriesIndex, dataPointIndex } = config;
|
|
const { series } = config.config;
|
|
if (seriesIndex === -1) return;
|
|
if (series === undefined) return;
|
|
|
|
const serie = series[seriesIndex];
|
|
|
|
const base_url = serie.base_url || series[0]['base_url']
|
|
|
|
if (base_url != null) {
|
|
const url = `${base_url}?${serie.data[dataPointIndex].meta.url_query}`;
|
|
ntopng_url_manager.go_to_url(url);
|
|
}
|
|
}
|
|
|
|
|
|
static formatApexChartLabelFromXandY({ series, seriesIndex, dataPointIndex, w }) {
|
|
const serie = w.config.series[seriesIndex]["data"][dataPointIndex];
|
|
|
|
const x_value = serie["x"];
|
|
const y_value = serie["y"];
|
|
const host_name = serie["meta"]["label"];
|
|
|
|
const x_axis_title = w.config.xaxis.title.text;
|
|
const y_axis_title = w.config.yaxis[0].title.text;
|
|
|
|
return (`
|
|
<div class='apexcharts-theme-light apexcharts-active' id='test'>
|
|
<div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
|
|
${host_name}
|
|
</div>
|
|
<div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
|
|
<div class='apexcharts-tooltip-text text-left'>
|
|
<b>${x_axis_title}</b>: ${x_value}
|
|
</div>
|
|
<div class='apexcharts-tooltip-text text-left'>
|
|
<b>${y_axis_title}</b>: ${y_value}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`)
|
|
}
|
|
|
|
static loadBSTooltips() {
|
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
|
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
return new bootstrap.Tooltip(tooltipTriggerEl)
|
|
})
|
|
}
|
|
|
|
static disposeBSTooltips() {
|
|
document.querySelectorAll('.tooltip').forEach(tooltip => {
|
|
const tooltipInstance = bootstrap.Tooltip.getInstance(tooltip);
|
|
if (tooltipInstance) {
|
|
tooltipInstance.dispose();
|
|
}
|
|
tooltip.remove();
|
|
});
|
|
}
|
|
|
|
static reloadBSTooltips() {
|
|
this.disposeBSTooltips();
|
|
this.loadBSTooltips();
|
|
}
|
|
}
|
|
|
|
$(function () {
|
|
// if there are inputs with 'pattern' data attribute
|
|
// then initialize them
|
|
NtopUtils.initDataPatterns();
|
|
});
|
|
|