ntopng/http_src/vue/page-stats.vue
Alfredo Cardigliano bbb96a17ab Fix epoch begin/end
2025-03-28 15:31:35 +01:00

857 lines
35 KiB
Vue

<!-- (C) 2022 - ntop.org -->
<template>
<div class="col-12 mb-2 mt-2">
<AlertInfo></AlertInfo>
<div class="card h-100 overflow-hidden">
<DateTimeRangePicker style="margin-top:0.5rem;" class="ms-1" :id="id_date_time_picker"
:enable_refresh="true" ref="date_time_picker" @epoch_change="epoch_change"
:min_time_interval_id="min_time_interval_id" :custom_time_interval_list="time_preset_list">
<template v-slot:begin>
</template>
<template v-slot:extra_buttons>
<button v-if="enable_snapshots" class="btn btn-link btn-sm" @click="show_modal_snapshot"
:title="_i18n('page_stats.manage_snapshots_btn')"><i
class="fas fa-lg fa-camera-retro"></i></button>
<button v-if="traffic_extraction_permitted" class="btn btn-link btn-sm"
@click="show_modal_traffic_extraction" :title="_i18n('traffic_recording.pcap_download')"><i
class="fas fa-lg fa-download"></i></button>
<button :disabled="is_safari" class="btn btn-link btn-sm" @click="show_modal_download_file"
:title="image_button_title"><i class="fas fa-lg fa-file-image"></i></button>
<button v-if="is_history_enabled" class="btn btn-link btn-sm" @click="jump_to_historical_flows"
:title="_i18n('page_stats.historical_flows')"><i class="fas fa-search-plus"></i></button>
</template>
</DateTimeRangePicker>
<!-- select metric -->
<div v-show="ts_menu_ready" class="form-group ms-1 mt-2 d-flex align-items-center">
<div class="inline select2-size me-2">
<SelectSearch v-model:selected_option="selected_metric" :options="metrics"
@select_option="select_metric">
</SelectSearch>
</div>
<div class="inline select2-size me-2">
<SelectSearch v-model:selected_option="current_groups_options_mode" :options="groups_options_modes"
@select_option="change_groups_options_mode">
</SelectSearch>
</div>
<button type="button" @click="show_manage_timeseries" class="btn btn-sm btn-primary inline"
style='vertical-align: super;' v-if="is_ntop_pro">
Manage Timeseries
</button>
</div>
<template v-for="(item, i) in charts_options_items" :key="item.key">
<TimeseriesChart :id="id_chart + i" :ref="el => { charts[i] = el }" :chart_type="chart_type"
:register_on_status_change="false" :get_custom_chart_options="get_f_get_custom_chart_options(i)"
@zoom="epoch_change" @chart_reloaded="chart_reloaded">
</TimeseriesChart>
</template>
</div>
<div class="mt-4 card card-shadow" v-if="enable_stats_table">
<div class="card-body">
<BootstrapTable id="page_stats_bootstrap_table" :columns="stats_columns" :rows="stats_rows"
:print_html_column="(col) => print_stats_column(col)"
:print_html_row="(col, row) => print_stats_row(col, row)">
</BootstrapTable>
</div>
</div>
<div class="mt-4 card card-shadow" v-if="is_ntop_pro">
<div class="card-body">
<div v-if="selected_top_table?.table_config_def" class="inline select2-size me-2 mt-2">
<SelectSearch v-model:selected_option="selected_top_table" :options="top_table_options">
</SelectSearch>
</div>
<Datatable v-if="selected_top_table?.table_config_def" :key="selected_top_table?.value"
ref="top_table_ref" :table_buttons="selected_top_table.table_config_def.table_button"
:columns_config="selected_top_table.table_config_def.columns_config"
:data_url="selected_top_table.table_config_def.data_url"
:enable_search="selected_top_table.table_config_def.enable_search"
:table_config="selected_top_table.table_config_def.table_config">
</Datatable>
</div>
</div>
</div>
<ModalSnapshot v-if="enable_snapshots" ref="modal_snapshot" :csrf="csrf" :page="page_snapshots"
@added_snapshot="refresh_snapshots" @deleted_snapshots="refresh_snapshots"
@deleted_all_snapshots="refresh_snapshots">
</ModalSnapshot>
<ModalTimeseries v-if="is_ntop_pro" ref="modal_timeseries" :sources_types_enabled="sources_types_enabled"
@apply="apply_modal_timeseries">
</ModalTimeseries>
<ModalTrafficExtraction id="page_stats_modal_traffic_extraction" ref="modal_traffic_extraction">
</ModalTrafficExtraction>
<ModalDownloadFile ref="modal_download_file" :title="_i18n('page_stats.title_modal_download_file')" ext="png"
@download="download_chart_png">
</ModalDownloadFile>
</template>
<script setup>
import { ref, onMounted, onBeforeMount, computed, watch } from "vue";
import { default as TimeseriesChart } from "./timeseries-chart.vue";
import { default as DateTimeRangePicker } from "./date-time-range-picker.vue";
import { default as ModalSnapshot } from "./modal-snapshot.vue";
import { default as ModalTimeseries } from "./modal-timeseries.vue";
import { default as ModalTrafficExtraction } from "./modal-traffic-extraction.vue";
import { default as ModalDownloadFile } from "./modal-download-file.vue";
import { default as AlertInfo } from "./alert-info.vue";
import { default as dataUtils } from "../utilities/data-utils.js";
import { default as SelectSearch } from "./select-search.vue";
import { default as Datatable } from "./datatable.vue";
import { default as BootstrapTable } from "./bootstrap-table.vue";
import { ntopng_utility, ntopng_url_manager, ntopng_status_manager } from "../services/context/ntopng_globals_services.js";
import timeseriesUtils from "../utilities/timeseries-utils.js";
import metricsManager from "../utilities/metrics-manager.js";
import formatterUtils from "../utilities/formatter-utils";
import NtopUtils from "../utilities/ntop-utils";
const props = defineProps({
csrf: String,
is_ntop_pro: Boolean,
source_value_object: Object,
sources_types_enabled: Object,
sources_types_top_enabled: Object,
enable_snapshots: Boolean,
is_history_enabled: Boolean,
traffic_extraction_permitted: Boolean,
is_dark_mode: Boolean,
});
//ntopng_utility.check_and_set_default_time_interval();
const _i18n = (t) => i18n(t);
let id_chart = "chart";
let id_date_time_picker = "date_time_picker";
let chart_type = ntopChartApex.typeChart.TS_LINE;
const config_app_table = ref({});
const init_config_table = ref(false);
const charts = ref([]);
const date_time_picker = ref(null);
const top_table_ref = ref(null);
const modal_timeseries = ref(null);
const modal_snapshot = ref(null);
const modal_download_file = ref(null);
const is_safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
const image_button_title = is_safari ? _i18n('page_stats.download_image_disabled') : _i18n('page_stats.title_modal_download_file');
const min_time_interval_id = ref(null);
const metrics = ref([]);
const selected_metric = ref({});
const source_type = metricsManager.get_current_page_source_type();
const enable_stats_table = ref(false);
const enable_top_table = ref(false);
/**
* { key: identifier of Chart component, if change Chart will be destroyed and recreated,
* chart_options: chart options }[]
**/
const charts_options_items = ref([]);
/**
* Modes that represent how it's possible display timeseries.
*/
const groups_options_modes = ntopng_utility.object_to_array(timeseriesUtils.groupsOptionsModesEnum);
/**
* Current display timeseries mode.
*/
const current_groups_options_mode = ref(init_groups_option_mode());
let last_timeseries_groups_loaded = null;
const custom_metric = { label: i18n('page_stats.custom_metrics'), currently_active: false };
const page_snapshots = "timeseries";
const ts_menu_ready = ref(false);
const time_preset_list = [
{ value: "10_min", label: i18n('show_alerts.presets.10_min'), currently_active: false },
{ value: "30_min", label: i18n('show_alerts.presets.30_min'), currently_active: true },
{ value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
{ value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
{ value: "6_hours", label: i18n('show_alerts.presets.6_hours'), currently_active: false },
{ value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
{ value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
{ value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
{ value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
{ value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
{ value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
];
function init_groups_option_mode() {
let groups_mode = ntopng_url_manager.get_url_entry("timeseries_groups_mode");
if (groups_mode != null && groups_mode != "") {
return timeseriesUtils.getGroupOptionMode(groups_mode);
}
return groups_options_modes[0];
}
function set_default_source_object_in_url() {
if (props.source_value_object == null) { return; }
let source_type = metricsManager.get_current_page_source_type();
metricsManager.set_source_value_object_in_url(source_type, props.source_value_object);
}
onBeforeMount(async () => {
if (ntopng_url_manager.get_url_entry("page") == "va_historical") {
let columns_tmp = [];
stats_columns.forEach((item) => {
if (item.va) {
columns_tmp.push(item);
}
})
stats_columns = columns_tmp;
}
if (props.source_value_object.is_va) {
min_time_interval_id.value = "hour";
ntopng_utility.check_and_set_default_time_interval("day");
};
set_default_source_object_in_url();
});
onMounted(async () => {
init();
await Promise.all([
ntopng_sync.on_ready(id_date_time_picker),
]);
// chart.value.register_status();
});
async function init() {
//get_default_timeseries_groups
let push_custom_metric = true;
let timeseries_groups = await metricsManager.get_timeseries_groups_from_url(http_prefix);
let metric_ts_schema;
let metric_query;
if (timeseries_groups == null) {
push_custom_metric = false;
metric_ts_schema = ntopng_url_manager.get_url_entry("ts_schema");
let ts_query = ntopng_url_manager.get_url_entry("ts_query");
if (ts_query != null && ts_query != "") {
metric_query = metricsManager.get_metric_query_from_ts_query(ts_query);
}
if (metric_ts_schema == "") { metric_ts_schema = null; }
timeseries_groups = await metricsManager.get_default_timeseries_groups(http_prefix, metric_ts_schema, metric_query);
}
metrics.value = await get_metrics(push_custom_metric);
if (push_custom_metric == true) {
selected_metric.value = custom_metric;
} else {
selected_metric.value = metricsManager.get_default_metric(metrics.value, metric_ts_schema, metric_query);
}
ts_menu_ready.value = true;
await load_page_stats_data(timeseries_groups, true, true);
}
let last_push_custom_metric = null;
async function get_metrics(push_custom_metric, force_refresh) {
let metrics = await metricsManager.get_metrics(http_prefix);
if (!force_refresh && last_push_custom_metric == push_custom_metric) { return metrics.value; }
if (push_custom_metric) {
metrics.push(custom_metric);
}
if (cache_snapshots == null || force_refresh) {
cache_snapshots = await get_snapshots_metrics();
}
if (props.enable_snapshots) {
let snapshots_metrics = cache_snapshots;
snapshots_metrics.forEach((sm) => metrics.push(sm));
}
/* Order Metrics */
if (metrics.length > 0)
metrics.sort(NtopUtils.sortAlphabetically);
return metrics;
}
async function get_snapshots_metrics() {
if (!props.enable_snapshots) { return; }
let url = `${http_prefix}/lua/pro/rest/v2/get/filters/snapshots.lua?page=${page_snapshots}`;
let snapshots_obj = await ntopng_utility.http_request(url);
let snapshots = ntopng_utility.object_to_array(snapshots_obj);
let metrics_snapshots = snapshots.map((s) => {
return {
...s,
is_snapshot: true,
label: `${s.name}`,
group: "Snapshots",
};
});
return metrics_snapshots;
}
async function get_selected_timeseries_groups() {
let metric = selected_metric.value;
return get_timeseries_groups_from_metric(metric);
}
async function get_timeseries_groups_from_metric(metric) {
let source_type = metricsManager.get_current_page_source_type();
let source_array = await metricsManager.get_default_source_array(http_prefix, source_type);
let ts_group = metricsManager.get_ts_group(source_type, source_array, metric);
let timeseries_groups = [ts_group];
return timeseries_groups;
}
const add_ts_group_from_source_value_dict = async (source_type_id, source_value_dict, metric_schema) => {
let source_type = metricsManager.get_source_type_from_id(source_type_id);
let source_array = await metricsManager.get_source_array_from_value_dict(http_prefix, source_type, source_value_dict);
let metric = await metricsManager.get_metric_from_schema(http_prefix, source_type, source_array, metric_schema);
let ts_group = metricsManager.get_ts_group(source_type, source_array, metric);
add_ts_group(ts_group);
};
const add_metric_from_metric_schema = async (metric_schema, metric_query) => {
let metric = metrics.value.find((m) => m.schema == metric_schema && m.query == metric_query);
if (metric == null) {
console.error(`metric = ${metric_schema}, query = ${metric_query} not found.`);
return;
}
let timeseries_groups = await get_timeseries_groups_from_metric(metric);
// modal_timeseries.value.set_timeseries_groups(last_timeseries_groups_loaded);
// modal_timeseries.value.add_ts_group(timeseries_groups[0], true);
add_ts_group(timeseries_groups[0]);
};
function add_ts_group(ts_group) {
modal_timeseries.value.set_timeseries_groups(last_timeseries_groups_loaded);
modal_timeseries.value.add_ts_group(ts_group, true);
}
async function select_metric(metric) {
if (metric.is_snapshot == true) {
let url_parameters = metric.filters;
let timeseries_url_params = ntopng_url_manager.get_url_entry("timeseries_groups", url_parameters);
let timeseries_groups = await metricsManager.get_timeseries_groups_from_url(http_prefix, timeseries_url_params);
current_groups_options_mode.value = timeseriesUtils.getGroupOptionMode(ntopng_url_manager.get_url_entry("timeseries_groups_mode", url_parameters));
await load_page_stats_data(timeseries_groups, true, false);
} else {
await load_selected_metric_page_stats_data();
refresh_metrics(false);
}
}
async function load_selected_metric_page_stats_data() {
let timeseries_groups = await get_selected_timeseries_groups();
await load_page_stats_data(timeseries_groups, true, false);
}
function epoch_change(new_epoch) {
let push_custom_metric = selected_metric.value.label == custom_metric.label;
load_page_stats_data(last_timeseries_groups_loaded, true, false, new_epoch.refresh_data);
refresh_top_table();
refresh_metrics(push_custom_metric, true);
}
function chart_reloaded(chart_options) {
}
function show_modal_snapshot() {
modal_snapshot.value.show();
}
function show_manage_timeseries() {
if (last_timeseries_groups_loaded == null) { return; }
modal_timeseries.value.show(last_timeseries_groups_loaded);
};
/**
* Function called by Chart component to draw or update that return chart options.
**/
function get_f_get_custom_chart_options(chart_index) {
return async (url) => {
return charts_options_items.value[chart_index].chart_options;
}
}
let cache_snapshots = null;
function refresh_snapshots() {
let push_custom_metric = selected_metric.value.label == custom_metric.label;
refresh_metrics(push_custom_metric, true);
}
async function refresh_metrics(push_custom_metric, force_refresh) {
metrics.value = await get_metrics(push_custom_metric, force_refresh);
if (push_custom_metric) {
selected_metric.value = custom_metric;
}
}
async function apply_modal_timeseries(timeseries_groups) {
refresh_metrics(true);
await load_page_stats_data(timeseries_groups, true, true);
}
function change_groups_options_mode() {
load_page_stats_data(last_timeseries_groups_loaded, false, false);
}
let ts_charts_options;
/* This function load the chart data and options, doing the request and then setting the options */
async function load_page_stats_data(timeseries_groups, reload_charts_data, reload_top_table_options, refreshed_time_interval) {
/* Get the information necessary for the request, like epoch ecc. */
let status = ntopng_status_manager.get_status();
let ts_compare = get_ts_compare(status);
if (reload_charts_data) {
/* Do the request to the backend; the answer is formatted as
* [
* {
* metadata: { ... } // Containing various info regarding the series returned
* series: { ... } // Containing the series with the data, labels and statistics
* }
* ]
*/
if (timeseries_groups == null) {
timeseries_groups = [];
console.warn("Empty timeseries_groups request");
return;
}
ts_charts_options = await timeseriesUtils.getTsChartsOptions(http_prefix, status, ts_compare, timeseries_groups, props.is_ntop_pro);
}
/* Update timeseries label to display */
set_timeseries_groups_source_label(timeseries_groups, ts_charts_options);
/* Format the options for the timeseries library */
let charts_options = timeseriesUtils.tsArrayToOptionsArray(ts_charts_options, timeseries_groups, current_groups_options_mode.value, ts_compare);
if (refreshed_time_interval) {
update_charts(charts_options);
} else {
set_charts_options_items(charts_options);
}
set_stats_rows(ts_charts_options, timeseries_groups, status);
if (reload_top_table_options) {
set_top_table_options(timeseries_groups, status);
}
// set last_timeseries_groupd_loaded
last_timeseries_groups_loaded = timeseries_groups;
// update url params
update_url_params();
}
/* This function returns set the label of the timeseries; if available it should be
* found in response.metadata.label field
*/
function set_timeseries_groups_source_label(timeseries_groups, ts_charts_options) {
timeseries_groups.forEach((ts_group, i) => {
let ts_options = ts_charts_options[i];
let label = ts_options?.metadata?.label;
if (label != null) {
let source_index = timeseriesUtils.getMainSourceDefIndex(ts_group);
let source = ts_group.source_array[source_index];
source.label = label;
}
});
}
function update_url_params() {
ntopng_url_manager.set_key_to_url("timeseries_groups_mode", current_groups_options_mode.value.value);
metricsManager.set_timeseries_groups_in_url(last_timeseries_groups_loaded);
}
function update_charts(charts_options) {
charts_options.forEach((options, i) => {
// charts.value[i].update_chart_options({ yaxis: options.yaxis });
charts.value[i].update_chart_series(options?.serie);
});
}
function set_charts_options_items(charts_options) {
charts_options_items.value = charts_options.map((options, i) => {
return {
key: ntopng_utility.get_random_string(),
chart_options: options,
};
});
}
function get_ts_compare(status) {
// 5m, 30m, 1h, 1d, 1w, 1M, 1Y
let r = Number.parseInt((status.epoch_end - status.epoch_begin) / 60);
if (r <= 5) {
return "5m";
} else if (r <= 10) {
return "10m";
} else if (r <= 30) {
return "30m";
} else if (r <= 60) {
return "1h";
} else if (r <= 60 * 2) {
return "2h";
} else if (r <= 60 * 6) {
return "6h";
} else if (r <= 60 * 12) {
return "12h";
} else if (r <= 60 * 24) {
return "1d";
} else if (r <= 60 * 24 * 7) {
return "1w";
} else if (r <= 60 * 24 * 31) {
return "1M";
} else {
return "1Y";
}
}
function get_top_table_url(ts_group, table_value, table_view, table_source_def_value_dict, status) {
if (!status) {
status = ntopng_status_manager.get_status();
}
if (!status.epoch_begin) {
status.epoch_begin = ntopng_url_manager.get_url_entry("epoch_begin");
}
if (!status.epoch_end) {
status.epoch_end = ntopng_url_manager.get_url_entry("epoch_end");
}
let ts_query = timeseriesUtils.getTsQuery(ts_group, true, table_source_def_value_dict);
let v = table_value;
let data_url = `${http_prefix}/lua/pro/rest/v2/get/${v}/top/ts_stats.lua`;
//todo: get ts_query
let p_obj = {
zoom: '5m',
ts_query,
// ts_query: `ifid:${ntopng_url_manager.get_url_entry('ifid')}`,
epoch_begin: `${status.epoch_begin}`,
epoch_end: `${status.epoch_end}`,
detail_view: `${table_view}`,
new_charts: `true`
};
let p_url_request = ntopng_url_manager.add_obj_to_url(p_obj, '');
return `${data_url}?${p_url_request}`;
}
async function refresh_top_table() {
if (!props.is_ntop_pro) { return; }
let table_config = selected_top_table.value?.table_config_def;
if (table_config == null) { return; }
// NtopUtils.showOverlays();
let data_url = get_top_table_url(table_config.ts_group, table_config.table_def.table_value, table_config.table_def.view, table_config.table_source_def_value_dict);
top_table_ref.value.update_url(data_url);
top_table_ref.value.reload();
// NtopUtils.hideOverlays();
}
const top_table_options = ref([]);
const selected_top_table = ref({});
function set_top_table_options(timeseries_groups, status) {
if (!props.is_ntop_pro) { return; }
if (timeseries_groups == null) {
timeseries_groups = last_timeseries_groups_loaded;
}
if (status == null) {
status = ntopng_status_manager.get_status();
}
let sources_types_tables = metricsManager.sources_types_tables;
let ts_group_dict = {}; // dictionary with 1 ts_group for each (source_type, source_array)
timeseries_groups.forEach((ts_group) => {
let source_type = ts_group.source_type;
// let source_type_tables = sources_types_tables[source_type.id];
// let table_source_def_value_dict = source_type_tables.table_source_def_value_dict
let id = metricsManager.get_ts_group_id(ts_group.source_type, ts_group.source_array);
ts_group_dict[id] = ts_group;
});
let top_table_id_dict = {};
top_table_options.value = [];
for (let id in ts_group_dict) {
let ts_group = ts_group_dict[id];
let main_source_index = timeseriesUtils.getMainSourceDefIndex(ts_group);
let main_source = ts_group.source_array[main_source_index];
let source_type = ts_group.source_type;
let source_type_tables = sources_types_tables[source_type.id];
if (source_type_tables == null) { continue; }
source_type_tables.forEach((table_def) => {
let enables_table_value = props.sources_types_top_enabled[table_def.table_value];
if (enables_table_value == null) { return; }
let enable_table_def = enables_table_value[table_def.view];
if (!enable_table_def) { return; }
let table_source_def_value_dict = table_def.table_source_def_value_dict
let data_url = get_top_table_url(ts_group, table_def.table_value, table_def.view, table_source_def_value_dict, status);
let table_id = metricsManager.get_ts_group_id(ts_group.source_type, ts_group.source_array, null, table_source_def_value_dict, true);
table_id = `${table_id}_${table_def.view}`;
if (top_table_id_dict[table_id] != null) { return; }
top_table_id_dict[table_id] = true;
let value = `${table_def.table_value}_${table_def.view}_${table_id}`;
let label;
if (table_def.f_get_label == null) {
label = `${table_def.title} - ${source_type.label} ${main_source.label}`;
} else {
label = table_def.f_get_label(ts_group)
}
const table_config_def = {
ts_group,
table_def,
// table_value: table_def.table_value,
// table_view: table_def.view,
table_buttons: [],
data_url,
enable_search: true,
table_config: {
serverSide: false,
order: [[table_def.default_sorting_columns, 'desc']],
columnDefs: table_def.columnDefs || [],
}
};
// it should be here in this instance the vuetify object with its properties
table_config_def.columns_config = table_def.columns.map((column) => {
let render_if_context = {
is_history_enabled: props.is_history_enabled
};
let c = {
visible: !column.render_if || column.render_if(render_if_context),
...column,
};
if (c.className == null) { c.className = "text-nowrap"; }
if (c.responsivePriority == null) { c.responsivePriority = 1; }
c.render = column.render.bind({
add_metric_from_metric_schema,
add_ts_group_from_source_value_dict,
sources_types_enabled: props.sources_types_enabled,
status, source_type, source_array: ts_group.source_array,
});
return c;
});
let option = { value, label, table_config_def };
top_table_options.value.push(option);
});
}
if (selected_top_table.value != null && top_table_options.value.find((option) => option.value == selected_top_table.value.value)) {
return;
}
selected_top_table.value = top_table_options.value.find((option) => option.table_config_def.default == true);
if (selected_top_table.value == null) {
selected_top_table.value = top_table_options.value[0];
}
}
let stats_columns = [
{ id: "metric", label: _i18n("page_stats.metric"), va: true },
{ id: "avg", label: _i18n("page_stats.average"), class: "text-end", va: true },
{ id: "perc_95", label: _i18n("page_stats.95_perc"), class: "text-end", va: true },
{ id: "max", label: _i18n("page_stats.max"), class: "text-end", va: true },
{ id: "min", label: _i18n("page_stats.min"), class: "text-end", va: true },
{ id: "total", label: _i18n("page_stats.total"), class: "text-end", va: false },
];
const stats_rows = ref([]);
function set_stats_rows(ts_charts_options, timeseries_groups, status) {
const extend_serie_name = ts_charts_options.length > 1;
enable_stats_table.value = timeseries_groups.map((ts_group) => !ts_group.source_type.disable_stats).reduce((res, el) => res | el, false);
if (!enable_stats_table.value) { return; }
const f_get_total_formatter_type = (type) => {
let map_type = {
"bps": "bytes",
"fps": "flows",
"alertps": "alerts",
"hitss": "hits",
"pps": "packets",
};
if (map_type[type] != null) {
return map_type[type];
}
return type;
};
stats_rows.value = [];
ts_charts_options.forEach((options, i) => {
let ts_group = timeseries_groups[i];
if (ts_group.source_type.disable_stats == true) { return; }
options.series?.forEach((s, j) => {
let ts_id = timeseriesUtils.getSerieId(s);
let s_metadata = ts_group.metric.timeseries[ts_id];
let formatter = formatterUtils.getFormatter(ts_group.metric.measure_unit);
let ts_stats;
let name = s_metadata.label;
if (s_metadata.hidden) {
/* Skip in case it's requested to hide the Timeserie */
return;
}
if (s_metadata.use_serie_name == true) {
name = s.name;
}
if (s?.data.length > j) {
ts_stats = s.statistics;
}
if (ts_stats == null) {
return;
}
if (s.ext_label) {
name = s.ext_label
}
if (s.label) {
name = s.label
}
name = timeseriesUtils.getSerieName(name, ts_id, ts_group, extend_serie_name);
let total_formatter_type = f_get_total_formatter_type(ts_group.metric.measure_unit);
let total_formatter = formatterUtils.getFormatter(total_formatter_type);
let row = {
metric: name,
total: total_formatter(ts_stats.total),
perc_95: formatter(ts_stats["95th_percentile"]),
avg: formatter(ts_stats.average),
max: formatter(ts_stats.max_val),
min: formatter(ts_stats.min_val),
};
stats_rows.value.push(row);
});
});
}
function print_stats_column(col) {
return col.label;
}
function print_stats_row(col, row) {
let label = row[col.id];
const historical_filter = props.source_value_object.historical_link_to_table;
if (historical_filter && historical_filter.add_historical_link_to_table && historical_filter.historical_link_col_id == col.id) {
const element = historical_filter.historical_filters.find((el) => { return el.table_label === label })
if (element) {
let params = create_historical_params()
params[element.historical_filter] = params[historical_filter.historical_filter_to_change]
delete params[historical_filter.historical_filter_to_change]
params["aggregated"] = false;
params["query_preset"] = "";
params["count"] = "THROUGHPUT";
if (!params["ifid"]) {
params["ifid"] = historical_filter.historical_interface_id
}
let url_params = ntopng_url_manager.obj_to_url_params(params);
label = `<a href="${http_prefix}/lua/pro/db_search.lua?${url_params}">${label}</a>`
}
}
return label;
}
function create_historical_params() {
let status = ntopng_status_manager.get_status();
let params = { epoch_begin: status.epoch_begin, epoch_end: status.epoch_end };
/* Add the source elements to the redirect, like host, snmp, ecc. */
if (last_timeseries_groups_loaded && last_timeseries_groups_loaded.length > 0) {
const source_array = last_timeseries_groups_loaded[0].source_array
const source_def = last_timeseries_groups_loaded[0].source_type.source_def_array
/* Use the first element */
if (source_array) {
let probe = ''
source_array.forEach((elem, i) => {
if (!dataUtils.isEmptyOrNull(elem.label) && source_def[i]) {
const value = source_def[i].value
switch (value) {
case 'device':
probe = elem.value
params["probe_ip"] = `${elem.value};eq`
break;
case 'port':
case 'if_index':
params["snmp_interface"] = `${probe}_${elem.value};eq`
break;
case 'host':
params["ip"] = `${elem.value};eq`
break;
case 'mac':
params["mac"] = `${elem.value};eq`
break;
case 'subnet':
params["network"] = `${elem.value};eq`
break;
case 'asn':
params["asn"] = `${elem.value};eq`
break;
case 'country':
params["country"] = `${elem.value};eq`
break;
case 'vlan':
params["vlan_id"] = `${elem.value};eq`
break;
case 'pool':
params["host_pool_id"] = `${elem.value};eq`
break;
default:
break;
}
}
})
}
}
return params
}
function jump_to_historical_flows() {
const params = create_historical_params()
let url_params = ntopng_url_manager.obj_to_url_params(params);
const historical_url = `${http_prefix}/lua/pro/db_search.lua?${url_params}`;
ntopng_url_manager.go_to_url(historical_url);
}
const modal_traffic_extraction = ref(null);
function show_modal_traffic_extraction() {
modal_traffic_extraction.value.show();
}
function show_modal_download_file() {
if (!ts_charts_options?.length) { return; }
let ts_group = last_timeseries_groups_loaded[0];
let filename = timeseriesUtils.getSerieName(null, null, ts_group);
modal_download_file.value.show(filename);
}
async function download_chart_png(filename) {
let chart_image_array_promise = charts.value.map(async (chart) => {
let canvas = new Image();
chart.get_image(canvas);
return new Promise(async (resolve, reject) => {
canvas.onload = function () {
resolve(canvas);
};
});
});
let height = 0;
let chart_image_array = await Promise.all(chart_image_array_promise);
chart_image_array.forEach((image) => {
height += image.height;
});
let canvas = document.createElement('canvas');
let canvas_context = canvas.getContext('2d');
canvas.width = chart_image_array[0].width;
canvas.height = height;
height = 0;
chart_image_array.forEach((image) => {
canvas_context.drawImage(image, 0, height, image.width, image.height);
height += image.height;
});
ntopng_utility.download_URI(canvas.toDataURL(), filename);
}
</script>
<style scoped>
.inline {
display: inline-block;
}
.select2-size {
min-width: 18rem;
}
</style>