v8.1.2: Add IP calculator and improve nettools forms

Implemented an IP calculator feature using the netaddr library and added corresponding JS and HTML form elements. Enhanced existing nettools forms by utilizing Flask-Pydantic for validation and AJAX request improvements. Removed deprecated alert selection methods in the database checker module. Updated language translations and incremented version to 8.1.2.
pull/411/head
Aidaho 2024-11-18 11:34:25 +03:00
parent 95cd8be699
commit 5c28a47cae
12 changed files with 190 additions and 166 deletions

View File

@ -692,7 +692,7 @@ def update_db_v_8_1_2():
def update_ver():
try:
Version.update(version='8.1.1.1').execute()
Version.update(version='8.1.2').execute()
except Exception:
print('Cannot update version')

View File

@ -146,84 +146,3 @@ def select_all_alerts(group_id: int):
return query.execute()
except Exception as e:
out_error(e)
# For deleting after realising new version
def select_alert(**kwargs):
if kwargs.get("group") is not None:
query = Server.select(Server.ip).where(
(Server.haproxy_alert == 1) & (Server.enabled == 1) & (Server.group_id == kwargs.get('group'))
)
else:
query = Server.select(Server.ip).where((Server.haproxy_alert == 1) & (Server.enabled == 1))
try:
query_res = query.execute()
except Exception as e:
out_error(e)
else:
return query_res
# For deleting after realising new version
def select_nginx_alert(**kwargs):
if kwargs.get("group") is not None:
query = Server.select(Server.ip).where(
(Server.nginx_alert == 1)
& (Server.enabled == 1)
& (Server.group_id == kwargs.get('group'))
& (Server.nginx == 1)
)
else:
query = Server.select(Server.ip).where(
(Server.nginx_alert == 1)
& (Server.enabled == 1)
& (Server.nginx == 1)
)
try:
query_res = query.execute()
except Exception as e:
out_error(e)
else:
return query_res
# For deleting after realising new version
def select_apache_alert(**kwargs):
if kwargs.get("group") is not None:
query = Server.select(Server.ip).where(
(Server.apache_alert == 1)
& (Server.enabled == 1)
& (Server.group_id == kwargs.get('group'))
& (Server.apache == 1)
)
else:
query = Server.select(Server.ip).where((Server.apache_alert == 1) & (Server.enabled == 1) & (Server.apache == 1))
try:
query_res = query.execute()
except Exception as e:
out_error(e)
else:
return query_res
# For deleting after realising new version
def select_keepalived_alert(**kwargs):
if kwargs.get("group") is not None:
query = Server.select(Server.ip).where(
(Server.keepalived_alert == 1)
& (Server.enabled == 1)
& (Server.group_id == kwargs.get('group'))
& (Server.keepalived == 1)
)
else:
query = Server.select(Server.ip).where(
(Server.keepalived_alert == 1)
& (Server.enabled == 1)
& (Server.keepalived == 1)
)
try:
query_res = query.execute()
except Exception as e:
out_error(e)
else:
return query_res

View File

@ -506,3 +506,14 @@ class HaproxyDefaultsRequest(BaseModel):
maxconn: Optional[int] = 5000
type: Optional[Literal['defaults']] = 'defaults'
action: Optional[Literal['save', 'test', 'reload', 'restart']] = "save"
class NettoolsRequest(BaseModel):
server_from: Optional[EscapedString] = None
server_to: Optional[Union[IPvAnyAddress, DomainName]] = None
port: Optional[Annotated[int, Gt(1), Le(65535)]] = None
action: Optional[Literal['ping', 'trace']] = None
dns_name: Optional[DomainName] = None
record_type: Optional[Literal['a', 'aaaa', 'caa', 'cname', 'mx', 'ns', 'ptr', 'sao', 'src', 'txt']] = None
ip: Optional[IPvAnyAddress] = None
netmask: Optional[int] = None

View File

@ -1,6 +1,7 @@
import json
import whois
import netaddr
from flask import Response, stream_with_context
import app.modules.server.ssh as mod_ssh
@ -134,3 +135,17 @@ def whois_check(domain_name: str) -> str:
output += f'<b>Organization:</b> {whois_data["org"]} <br />'
return output
def ip_calc(ip_add: str, netmask: int) -> dict[str, str]:
ip = netaddr.IPNetwork(f'{ip_add}/{netmask}')
ip_output = {
'address': str(ip.ip),
'network': str(ip.network),
'netmask': str(ip.netmask),
'broadcast': str(ip.broadcast),
'hosts': str(ip.size),
'min': str(ip[1]),
'max': str(ip[-2])
}
return ip_output

View File

@ -3,6 +3,7 @@ import os
from flask import render_template, request, g, abort, jsonify, redirect, url_for, send_from_directory
from flask_jwt_extended import jwt_required
from flask_pydantic.exceptions import ValidationError
from flask_pydantic import validate
from app import app, cache, jwt
from app.routes.main import bp
@ -18,7 +19,7 @@ import app.modules.roxywi.nettools as nettools_mod
import app.modules.roxywi.common as roxywi_common
import app.modules.service.common as service_common
import app.modules.service.haproxy as service_haproxy
from app.modules.roxywi.class_models import ErrorResponse
from app.modules.roxywi.class_models import ErrorResponse, NettoolsRequest
@app.template_filter('strftime')
@ -155,37 +156,40 @@ def nettools():
@bp.post('/nettools/<check>')
@validate(body=NettoolsRequest)
@jwt_required()
def nettools_check(check):
server_from = common.checkAjaxInput(request.form.get('server_from'))
server_to = common.is_ip_or_dns(request.form.get('server_to'))
action = common.checkAjaxInput(request.form.get('nettools_action'))
port_to = common.checkAjaxInput(request.form.get('nettools_telnet_port_to'))
dns_name = common.checkAjaxInput(request.form.get('nettools_nslookup_name'))
dns_name = common.is_ip_or_dns(dns_name)
record_type = common.checkAjaxInput(request.form.get('nettools_nslookup_record_type'))
domain_name = common.is_ip_or_dns(request.form.get('nettools_whois_name'))
def nettools_check(check, body: NettoolsRequest):
if check == 'icmp':
try:
return nettools_mod.ping_from_server(server_from, server_to, action)
return nettools_mod.ping_from_server(body.server_from, str(body.server_to), body.action)
except Exception as e:
return str(e)
return ErrorResponse(error=f'Cannot ping: {e}').model_dump(mode='json'), 500
elif check == 'tcp':
try:
return nettools_mod.telnet_from_server(server_from, server_to, port_to)
return nettools_mod.telnet_from_server(body.server_from, str(body.server_to), body.port)
except Exception as e:
return str(e)
return ErrorResponse(error=f'Cannot check port: {e}').model_dump(mode='json'), 500
elif check == 'dns':
try:
return nettools_mod.nslookup_from_server(server_from, dns_name, record_type)
return nettools_mod.nslookup_from_server(body.server_from, body.dns_name, body.record_type)
except Exception as e:
return str(e)
return ErrorResponse(error=f'Cannot lookup: {e}').model_dump(mode='json'), 500
elif check == 'whois':
try:
return jsonify(nettools_mod.whois_check(domain_name))
return jsonify(nettools_mod.whois_check(body.dns_name))
except Exception as e:
return str(e)
return ErrorResponse(error=f'Cannot make whois: {e}').model_dump(mode='json'), 500
elif check == 'ipcalc':
try:
ip_add = str(body.ip)
netmask = int(body.netmask)
except Exception as e:
return ErrorResponse(error=f'Cannot calc: {e}').model_dump(mode='json'), 500
try:
return jsonify(nettools_mod.ip_calc(ip_add, netmask))
except Exception as e:
return ErrorResponse(error=f'Cannot calc: {e}').model_dump(mode='json'), 500
else:
return 'error: Wrong check'

View File

@ -4,7 +4,7 @@ $( function() {
});
$("#nettools_telnet_form").on("click", ":submit", function (e) {
$('#ajax-nettools').html('');
let frm = $('#nettools_telnet_form');
var frm = $('#nettools_telnet_form');
if ($('#nettools_telnet_server_from option:selected').val() == '------') {
toastr.warning('Choose a server From');
return false;
@ -19,11 +19,13 @@ $( function() {
}
$.ajax({
url: frm.attr('action'),
data: frm.serialize(),
data: getFormData(frm),
type: frm.attr('method'),
contentType: "application/json; charset=utf-8",
success: function (data) {
data = data.replace('\n', "<br>");
if (data.indexOf('error: ') != '-1' || data.indexOf('Fatal') != '-1' || data.indexOf('Error(s)') != '-1') {
if (data.status === 'failed') {
toastr.error(data);
} else if (data.indexOf('error: ') != '-1' || data.indexOf('Fatal') != '-1' || data.indexOf('Error(s)') != '-1') {
$('#ajax-nettools').html('<div class="ping_pre">' + data + '</div>');
} else if (data.indexOf('warning: ') != '-1') {
toastr.clear();
@ -42,7 +44,7 @@ $( function() {
});
$("#nettools_nslookup_form").on("click", ":submit", function (e) {
$('#ajax-nettools').html('');
let frm = $('#nettools_nslookup_form');
var frm = $('#nettools_nslookup_form');
if ($('#nettools_nslookup_server_from option:selected').val() == '------') {
toastr.warning('Choose a server From');
return false;
@ -53,16 +55,12 @@ $( function() {
}
$.ajax({
url: frm.attr('action'),
data: frm.serialize(),
data: getFormData(frm),
type: frm.attr('method'),
contentType: "application/json; charset=utf-8",
success: function (data) {
data = data.replace('\n', "<br>");
if (data.indexOf('error: ') != '-1' || data.indexOf('Fatal') != '-1' || data.indexOf('Error(s)') != '-1') {
toastr.clear();
toastr.error(data);
} else if (data.indexOf('warning: ') != '-1') {
toastr.clear();
toastr.warning(data)
if (data.status === 'failed') {
toastr.warning(data);
} else {
toastr.clear();
$('#ajax-nettools').html('<div class="ping_pre">' + data + '</div>');
@ -74,64 +72,63 @@ $( function() {
$("#nettools_icmp_form").on("click", ":submit", function (e) {
$('#ajax-nettools').html('');
let frm = $('#nettools_icmp_form');
if ($('#nettools_icmp_server_from option:selected').val() == '------') {
if ($('#nettools_icmp_server_from option:selected').val() === '------') {
toastr.warning('Choose a server From');
return false;
}
if ($('#nettools_icmp_server_to').val() == '') {
if ($('#nettools_icmp_server_to').val() === '') {
toastr.warning('Enter a server To');
return false;
}
let data = getFormData(frm);
data = JSON.parse(data);
data['action'] = $(this).val();
$.ajax({
url: frm.attr('action'),
data: frm.serialize() + "&nettools_action=" + $(this).val(),
data: JSON.stringify(data),
type: frm.attr('method'),
contentType: "application/json; charset=utf-8",
xhrFields: {
onprogress: function (e) {
console.log(e.currentTarget.responseText);
$('#ajax-nettools').html(e.currentTarget.responseText);
try {
data = JSON.parse(e.currentTarget.responseText);
toastr.warning(data.error);
} catch (error) {
$('#ajax-nettools').html(e.currentTarget.responseText);
}
}
},
dataType: 'text',
success: function (data) {
data = data.replace('\n', "<br>");
if (data.indexOf('error: ') != '-1' || data.indexOf('Fatal') != '-1' || data.indexOf('Error(s)') != '-1') {
toastr.clear();
if (data.status === 'failed') {
toastr.error(data);
} else if (data.indexOf('warning: ') != '-1') {
toastr.clear();
toastr.warning(data)
} else {
toastr.clear();
}
}
});
event.preventDefault();
});
$("#nettools_portscanner_form").on("click", ":submit", function (e) {
let port_server = $('#nettools_portscanner_server').val();
$('#ajax-nettools').html('');
if (port_server == '') {
if ($('#nettools_portscanner_server').val() == '') {
toastr.warning('Enter an address');
return false;
}
$.ajax({
url: "/portscanner/scan",
data: JSON.stringify({'ip': port_server}),
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/portscanner/scan/" + $('#nettools_portscanner_server').val(),
success: function (data) {
if (data.status === 'failed') {
toastr.error(data.error);
data = data.replace(/\s+/g, ' ');
if (data.indexOf('danger') != '-1' || data.indexOf('unique') != '-1' || data.indexOf('error:') != '-1') {
toastr.error(data);
} else {
toastr.clear();
$("#show_scans_ports_body").html(data.data);
$("#show_scans_ports_body").html(data);
var close_word = $('#translate').attr('data-close');
$("#show_scans_ports").dialog({
resizable: false,
height: "auto",
width: 360,
modal: true,
title: "Opened ports",
title: "{{lang.words.opened|title()}} {{lang.words.ports}}",
buttons: [{
text: close_word,
click: function () {
@ -147,31 +144,74 @@ $( function() {
});
$("#nettools_whois_form").on("click", ":submit", function (e) {
$('#ajax-nettools').html('');
let frm = $('#nettools_whois_form');
var frm = $('#nettools_whois_form');
if ($('#nettools_whois_name').val() == '') {
toastr.warning('Enter a Domain name');
return false;
}
$.ajax({
url: frm.attr('action'),
data: frm.serialize() + "&nettools_action=" + $(this).val(),
data: getFormData(frm),
type: frm.attr('method'),
dataType: 'text',
contentType: "application/json; charset=utf-8",
success: function (data) {
data = data.replaceAll('"', '');
if (data.indexOf('error: ') != '-1' || data.indexOf('Fatal') != '-1' || data.indexOf('Error(s)') != '-1') {
toastr.clear();
if (data.status === 'failed') {
toastr.error(data);
} else if (data.indexOf('warning: ') != '-1') {
toastr.clear();
toastr.warning(data)
} else {
toastr.clear();
console.log(data)
$('#ajax-nettools').html('<div class="ping_pre">' + data + '</div>');
}
}
});
event.preventDefault();
});
$("#nettools_ipcalc_form").on("click", ":submit", function (e) {
$('#ajax-nettools').html('');
let frm = $('#nettools_ipcalc_form');
let ip = $('#nettools_address').val();
let netmask = $('#nettools_netmask').val();
if (ip === '') {
toastr.warning('Enter a valid IP address');
return false;
}
if (netmask === '') {
toastr.warning('Enter a valid Netmask');
return false;
}
$.ajax({
url: frm.attr('action'),
data: JSON.stringify({'ip': ip, 'netmask': netmask}),
type: frm.attr('method'),
contentType: "application/json; charset=utf-8",
success: function (data) {
if (data.status === 'failed') {
toastr.clear();
toastr.error(data.error);
} else {
toastr.clear();
$('#ajax-nettools').html(
'<div class="ping_pre"><b>Address</b>: ' + data.address + '<br />' +
'<b>Netmask</b>: ' + data.netmask + '<br />' +
'<b>Network</b>: ' + data.network + '<br />' +
'<b>Broadcast</b>: ' + data.broadcast + '<br />' +
'<b>Host min</b>: ' + data.min + '<br />' +
'<b>Host max</b>: ' + data.max + '<br />' +
'<b>Hosts</b>: ' + data.hosts +
'</div>'
);
}
}
});
event.preventDefault();
});
});
function getFormData($form){
let unindexed_array = $form.serializeArray();
let indexed_array = {};
$.map(unindexed_array, function(n, i){
indexed_array[n['name']] = n['value'];
});
return JSON.stringify(indexed_array);
}

View File

@ -955,5 +955,9 @@
"where": "where",
"shared": "shared",
"retries": "retries",
"address": "address",
"calculate": "calculate",
"calculator": "calculator",
"netmask": "Netmask",
}
%}

View File

@ -955,5 +955,9 @@
"where": "où",
"shared": "commun",
"retries": "tentatives",
"address": "adresse",
"calculate": "calculer",
"calculator": "calculatrice",
"netmask": "Masque de réseau",
}
%}

View File

@ -955,5 +955,9 @@
"where": "onde",
"shared": "partilhado",
"retries": "novas tentativas",
"address": "morada",
"calculate": "calcular",
"calculator": "calculadora",
"netmask": "Máscara de rede",
}
%}

View File

@ -955,5 +955,9 @@
"where": "где",
"shared": "общий",
"retries": "повторные попытки",
"address": "адрес",
"calculate": "рассчитать",
"calculator": "калькулятор",
"netmask": "Сетевая маска",
}
%}

View File

@ -3,7 +3,7 @@
{% block h2 %}{{ lang.menu_links.monitoring.net_tools }}{% endblock %}
{% block content %}
{% from 'include/input_macros.html' import input, checkbox, select %}
<script src="/static/js/nettools.js"></script>
<script src="{{ url_for('static', filename='js/nettools.js')}}"></script>
<form name="nettools_icmp_form" id="nettools_icmp_form" method="post" action="/nettools/icmp">
<table class="overview">
<caption><h3>ICMP</h3></caption>
@ -17,21 +17,20 @@
<td class="padding10 first-collumn">
<select autofocus required name="server_from" id="nettools_icmp_server_from">
<option disabled selected>------</option>
<option value="localhost">Roxy-WI</option>
<option value="localhost">Roxy-WI server</option>
{% for server in g.user_params['servers'] %}
<option value="{{ server.2 }}">{{ server.1 }}</option>
{% endfor %}
</select>
</td>
<td class="padding10 first-collumn">
{{ input('nettools_icmp_server_to', name='server_to', title=lang.nettools_page.ip_or_name) }}
{{ input('token', value=token, type='hidden') }}
{{ input('nettools_icmp_server_to', name='server_to', title='Enter IP or Name') }}
</td>
<td class="padding10 first-collumn">
<button type="submit" title="{{lang.words.run|title()}} Ping" id="nettools_ping" name="nettools_ping" value="nettools_ping">Ping</button>
<button type="submit" title="Run Ping" id="nettools_ping" name="nettools_ping" value="ping">Ping</button>
</td>
<td>
<button type="submit" title="{{lang.words.run|title()}} Traceroute" id="nettools_trace" name="nettools_trace" value="nettools_trace">Traceroute</button>
<button type="submit" title="Run Traceroute" id="nettools_trace" name="nettools_trace" value="trace">Traceroute</button>
</td>
</tr>
</table>
@ -49,18 +48,17 @@
<td class="padding10 first-collumn">
<select autofocus required name="server_from" id="nettools_telnet_server_from">
<option disabled selected>------</option>
<option value="localhost">Roxy-WI</option>
<option value="localhost">Roxy-WI server</option>
{% for server in g.user_params['servers'] %}
<option value="{{ server.2 }}">{{ server.1 }}</option>
{% endfor %}
</select>
</td>
<td class="padding10 first-collumn">
{{ input('nettools_telnet_server_to', name='server_to', title=lang.nettools_page.ip_or_name) }}
{{ input('token', value=token, type='hidden') }}
{{ input('nettools_telnet_server_to', name='server_to', title='Enter IP or Name') }}
</td>
<td class="padding10 first-collumn">
{{ input('nettools_telnet_port_to', title=lang.words.port|title() + ' ' + lang.words.port, type='number', style='width: 60px;') }}
{{ input('nettools_telnet_port_to', name='port', title='Enter port', type='number', style='width: 60px;') }}
</td>
<td>
<button type="submit" title="{{lang.words.run|title()}} Telnet" id="nettools_telnet" name="nettools_telnet" value="nettools_telnet">{{lang.words.connect|title()}}</button>
@ -81,7 +79,7 @@
<td class="padding10 first-collumn">
<select autofocus required name="server_from" id="nettools_nslookup_server_from">
<option disabled selected>------</option>
<option value="localhost">Roxy-WI</option>
<option value="localhost">Roxy-WI server</option>
{% for server in g.user_params['servers'] %}
<option value="{{ server.2 }}">{{ server.1 }}</option>
{% endfor %}
@ -89,13 +87,12 @@
</td>
<td class="padding10 first-collumn">
{% set values = dict() %}
{% set values = {'A':'A','AAA':'AAA', 'CNAME':'CNAME', 'MX':'MX', 'TXT':'TXT', 'PTR':'PTR', 'SRV':'SRV',
{% set values = {'a':'A','aaa':'AAA', 'cname':'CNAME', 'mx':'MX', 'txt':'TXT', 'ptr':'PTR', 'srv':'SRV',
'SOA':'SOA', 'CAA':'CAA'} %}
{{ select('nettools_nslookup_record_type', values=values, selected='A', required='required') }}
{{ select('nettools_nslookup_record_type', name='record_type', values=values, selected='A', required='required') }}
</td>
<td class="padding10 first-collumn">
{{ input('nettools_nslookup_name', title=lang.nettools_page.dns_name) }}
{{ input('token', value=token, type='hidden') }}
{{ input('nettools_nslookup_name', name='dns_name', title='Enter a DNS name') }}
</td>
<td>
<button type="submit" title="{{lang.words.run|title()}} Telnet" id="nettools_nslookup" name="nettools_nslookup" value="nettools_nslookup">{{lang.words.check|title()}}</button>
@ -104,7 +101,7 @@
</table>
</form>
<form name="nettools_portscanner_form" id="nettools_portscanner_form" method="post" action="/nettools/portscan">
<table class="overview" style="width: 40%;float: left;">
<table class="overview" style="width: 30%;float: left;">
<caption><h3>Port scanner</h3></caption>
<tr class="overviewHead">
<th class="padding10 first-collumn">{{lang.words.server|title()}}</th>
@ -112,7 +109,7 @@
</tr>
<tr>
<td class="padding10 first-collumn">
{{ input('nettools_portscanner_server', title=lang.nettools_page.server_portscann) }}
{{ input('nettools_portscanner_server', title='Enter a server for port scanning') }}
</td>
<td class="padding10" style="width: 0">
<button type="submit" title="{{lang.words.run|title()}} port scanning" id="nettools_portscan" name="nettools_portscan" value="nettools_portscan">{{lang.words.run|title()}}</button>
@ -121,7 +118,7 @@
</table>
</form>
<form name="nettools_whois_form" id="nettools_whois_form" method="post" action="/nettools/whois">
<table class="overview" style="width: 60%;">
<table class="overview" style="width: 30%;float: left">
<caption><h3>Whois</h3></caption>
<tr class="overviewHead">
<th class="padding10 first-collumn">{{lang.words.name|title()}}</th>
@ -129,7 +126,7 @@
</tr>
<tr>
<td class="padding10 first-collumn">
{{ input('nettools_whois_name', title=lang.nettools_page.dns_name) }}
{{ input('nettools_whois_name', name='dns_name', title=lang.nettools_page.dns_name) }}
</td>
<td class="padding10" style="width: 0">
<button type="submit" title="{{lang.words.check|title()}}" id="nettools_whois" name="nettools_whois" value="nettools_whois">{{lang.words.check|title()}}</button>
@ -137,6 +134,27 @@
</tr>
</table>
</form>
<form name="nettools_ip_calc_form" id="nettools_ipcalc_form" method="post" action="/nettools/ipcalc">
<table class="overview" style="width: 40%;">
<caption><h3>IP {{lang.words.calculator}}</h3></caption>
<tr class="overviewHead">
<th class="padding10 first-collumn">IP {{lang.words.address|title()}}</th>
<th>{{lang.words.netmask}}</th>
<th></th>
</tr>
<tr>
<td class="padding10 first-collumn">
{{ input('nettools_address', name='ip', placeholder='192.168.0.1') }}
</td>
<td>
{{ input('nettools_netmask', name='netmask', placeholder='24', type='number') }}
</td>
<td class="padding10" style="width: 0">
<button type="submit" title="{{lang.words.calculate|title()}}" id="nettools_ipcalc_form" name="nettools_ipcalc_form" value="nettools_ipcalc_form">{{lang.words.calculate|title()}}</button>
</td>
</tr>
</table>
</form>
<div id="ajax-nettools" style="padding: 20px;"></div>
<div id="show_scans_ports" style="display: none; padding: 0;">
<div id="show_scans_ports_body"></div>

View File

@ -87,6 +87,7 @@
{% endif %}
{%- elif service == 'apache' %}
showApachekBytes(server_ip)
setInterval(showApachekBytes, 60000, server_ip)
{% if s.4.0.27 %}
getApacheChartData(server_ip)
{% endif %}