Development
This commit is contained in:
parent
6f0dac01b0
commit
e9166d8a6a
3 changed files with 159 additions and 159 deletions
|
|
@ -1131,6 +1131,9 @@ def build_items(items, tokens, inherited_req=None):
|
|||
req = item.get('client_requirement', inherited_req)
|
||||
if not passes(req, level):
|
||||
continue
|
||||
data_req = item.get('data_requirement')
|
||||
if data_req and not tokens.get(data_req):
|
||||
continue
|
||||
parts.append(build_item(item, tokens, req))
|
||||
return ''.join(parts)
|
||||
|
||||
|
|
@ -1209,7 +1212,8 @@ def build_item(item, tokens, inherited_req=None):
|
|||
label = e(apply_tokens(item.get('label', ''), tokens))
|
||||
raw_value = apply_tokens(item.get('value', ''), tokens)
|
||||
value = e(raw_value)
|
||||
sub = e(apply_tokens(item.get('sub', ''), tokens))
|
||||
sub_raw = apply_tokens(item.get('sub', ''), tokens)
|
||||
sub = sub_raw if sub_raw.startswith('<') else e(sub_raw)
|
||||
variant = item.get('variant', '')
|
||||
cls = f'stat-card{(" stat-card-" + variant) if variant else ""}'
|
||||
edit_action = item.get('edit_action', '')
|
||||
|
|
|
|||
|
|
@ -50,15 +50,28 @@
|
|||
"type": "stat_card",
|
||||
"label": "DHCP Leases",
|
||||
"value": "%STAT_LEASE_COUNT%",
|
||||
"sub": "active leases",
|
||||
"sub": "%STAT_LEASES_LINK%",
|
||||
"variant": "accent"
|
||||
},
|
||||
{
|
||||
"type": "stat_card",
|
||||
"label": "DNS Queries",
|
||||
"value": "%DNS_STAT_QUERIES%",
|
||||
"sub": "since %DNS_METRICS_SINCE%"
|
||||
},
|
||||
{
|
||||
"type": "stat_card",
|
||||
"label": "DNS Cache",
|
||||
"value": "%DNS_STAT_HITS% (%DNS_STAT_HIT_RATE% hit rate)",
|
||||
"sub": "cache size: %DNS_CACHE_SIZE%, evictions: %DNS_STAT_CACHE_EVICTIONS%"
|
||||
},
|
||||
{
|
||||
"type": "stat_card",
|
||||
"label": "Queries Blocked",
|
||||
"value": "%STAT_BLOCKED_TODAY%",
|
||||
"sub": "in last 24h",
|
||||
"variant": "warning"
|
||||
"value": "%STAT_BLOCKED_ALLTIME%",
|
||||
"sub": "all time",
|
||||
"variant": "warning",
|
||||
"data_requirement": "HAS_QUERY_LOGGING"
|
||||
},
|
||||
{
|
||||
"type": "stat_card",
|
||||
|
|
@ -68,126 +81,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"label": "Network",
|
||||
"client_requirement": "client_is_viewer+",
|
||||
"items": [
|
||||
{
|
||||
"type": "grid",
|
||||
"rows": [
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "WAN Interface"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%GENERAL_WAN_INTERFACE%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "VLANs"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%OVERVIEW_VLAN_NAMES%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "Firewall"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%STAT_NFTABLES_STATUS%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "System Uptime"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%STAT_UPTIME%"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"label": "DNS Blocking",
|
||||
"client_requirement": "client_is_viewer+",
|
||||
"items": [
|
||||
{
|
||||
"type": "grid",
|
||||
"rows": [
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "Blocked Domains"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%STAT_BLOCKED_DOMAINS%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "Active Blocklists"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%STAT_BLOCKLIST_COUNT% lists"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "Last Refreshed"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%STAT_BL_LAST_UPDATE%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"type": "grid_label",
|
||||
"text": "Active IP Bans"
|
||||
},
|
||||
{
|
||||
"type": "grid_value",
|
||||
"text": "%STAT_BANNED_IP_COUNT% rules"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"label": "DNS Statistics",
|
||||
|
|
@ -245,6 +138,30 @@
|
|||
"html": "%DNS_PROVIDERS_TABLE%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"label": "Blocked Domains",
|
||||
"client_requirement": "client_is_viewer+",
|
||||
"data_requirement": "HAS_QUERY_LOGGING",
|
||||
"items": [
|
||||
{
|
||||
"type": "raw_html",
|
||||
"html": "%BLOCKED_DOMAINS_TABLE%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"label": "Client Activity",
|
||||
"client_requirement": "client_is_viewer+",
|
||||
"data_requirement": "HAS_QUERY_LOGGING",
|
||||
"items": [
|
||||
{
|
||||
"type": "raw_html",
|
||||
"html": "%CLIENT_ACTIVITY_TABLE%"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ def _dns_providers_table(servers):
|
|||
)
|
||||
return (
|
||||
'<table class="data-table" style="margin-top:0.75rem"><thead><tr>'
|
||||
'<th class="table-header">Server</th>'
|
||||
'<th class="table-header">Upstream DNS Provider</th>'
|
||||
'<th class="table-header">Queries Sent</th>'
|
||||
'<th class="table-header">Retried</th>'
|
||||
'<th class="table-header">Failed</th>'
|
||||
|
|
@ -89,9 +89,92 @@ def load_dns_metrics():
|
|||
return empty
|
||||
|
||||
|
||||
def count_blocked_today():
|
||||
out = factory.run("journalctl -u 'dnsmasq-routlin-*' --since '24 hours ago' --no-pager 2>/dev/null | grep -c ' is 0\\.0\\.0\\.0'")
|
||||
return out.strip() or '0'
|
||||
DNS_QUERIES_DB = f'{config_utils.CONFIGS_DIR}/dns-queries.db'
|
||||
|
||||
|
||||
def has_query_logging(cfg):
|
||||
return any(v.get('dnsmasq_log_queries') for v in cfg.get('vlans', []))
|
||||
|
||||
|
||||
def blocked_domains_table():
|
||||
no_data = '<p class="text-muted" style="margin:0.5rem 0 0">No query data collected yet.</p>'
|
||||
try:
|
||||
import sqlite3
|
||||
if not os.path.exists(DNS_QUERIES_DB):
|
||||
return no_data
|
||||
con = sqlite3.connect(DNS_QUERIES_DB)
|
||||
rows = con.execute(
|
||||
'SELECT domain, COUNT(*) as cnt FROM dns_queries WHERE blocked=1 '
|
||||
'GROUP BY domain ORDER BY cnt DESC LIMIT 10'
|
||||
).fetchall()
|
||||
con.close()
|
||||
if not rows:
|
||||
return no_data
|
||||
trs = ''.join(
|
||||
f'<tr><td class="table-cell">{factory.e(r[0])}</td>'
|
||||
f'<td class="table-cell">{r[1]:,}</td></tr>'
|
||||
for r in rows
|
||||
)
|
||||
return (
|
||||
'<table class="data-table"><thead><tr>'
|
||||
'<th class="table-header">Domain</th>'
|
||||
'<th class="table-header">Times Blocked</th>'
|
||||
'</tr></thead><tbody>' + trs + '</tbody></table>'
|
||||
)
|
||||
except Exception:
|
||||
return no_data
|
||||
|
||||
|
||||
def client_activity_table():
|
||||
no_data = '<p class="text-muted" style="margin:0.5rem 0 0">No query data collected yet.</p>'
|
||||
try:
|
||||
import sqlite3
|
||||
if not os.path.exists(DNS_QUERIES_DB):
|
||||
return no_data
|
||||
con = sqlite3.connect(DNS_QUERIES_DB)
|
||||
rows = con.execute(
|
||||
'SELECT client_ip, COUNT(*) as total, SUM(blocked) as blocked '
|
||||
'FROM dns_queries GROUP BY client_ip ORDER BY total DESC LIMIT 10'
|
||||
).fetchall()
|
||||
con.close()
|
||||
if not rows:
|
||||
return no_data
|
||||
trs = []
|
||||
for client_ip, total, blocked in rows:
|
||||
pct = f'{blocked / total * 100:.0f}%' if total else '0%'
|
||||
trs.append(
|
||||
f'<tr><td class="table-cell">{factory.e(client_ip)}</td>'
|
||||
f'<td class="table-cell">{total:,}</td>'
|
||||
f'<td class="table-cell">{int(blocked):,} ({pct})</td></tr>'
|
||||
)
|
||||
return (
|
||||
'<table class="data-table"><thead><tr>'
|
||||
'<th class="table-header">Client</th>'
|
||||
'<th class="table-header">Total Queries</th>'
|
||||
'<th class="table-header">Blocked</th>'
|
||||
'</tr></thead><tbody>' + ''.join(trs) + '</tbody></table>'
|
||||
)
|
||||
except Exception:
|
||||
return no_data
|
||||
|
||||
|
||||
def all_time_blocked_display():
|
||||
try:
|
||||
import sqlite3
|
||||
if not os.path.exists(DNS_QUERIES_DB):
|
||||
return '-'
|
||||
con = sqlite3.connect(DNS_QUERIES_DB)
|
||||
row = con.execute(
|
||||
'SELECT SUM(blocked), COUNT(*) FROM dns_queries'
|
||||
).fetchone()
|
||||
con.close()
|
||||
blocked, total = row
|
||||
if not blocked:
|
||||
return '-'
|
||||
pct = f'{blocked / total * 100:.0f}' if total else '0'
|
||||
return f'{int(blocked):,} blocked ({pct}%)'
|
||||
except Exception:
|
||||
return '-'
|
||||
|
||||
|
||||
def count_blocked_domains():
|
||||
|
|
@ -118,35 +201,31 @@ def bl_last_update():
|
|||
|
||||
def collect_tokens(cfg):
|
||||
tokens = config_utils.collect_layout_tokens(cfg)
|
||||
vlans = cfg.get('vlans', [])
|
||||
non_vpn_vlans = [v for v in vlans if not v.get('is_vpn')]
|
||||
vlan_names = [v.get('name', '') for v in vlans]
|
||||
net = cfg.get('network_interfaces', {})
|
||||
dns = cfg.get('upstream_dns', {})
|
||||
non_vpn_vlans = [v for v in cfg.get('vlans', []) if not v.get('is_vpn')]
|
||||
dns = cfg.get('upstream_dns', {})
|
||||
dns_stats = load_dns_metrics()
|
||||
ddns = factory.load_ddns()
|
||||
ip_str, domains_sub, last_obtained = public_ip_info(ddns)
|
||||
ddns = factory.load_ddns()
|
||||
ip_str, domains_sub, _ = public_ip_info(ddns)
|
||||
|
||||
tokens['GENERAL_WAN_INTERFACE'] = str(net.get('wan_interface', '-'))
|
||||
tokens['OVERVIEW_VLAN_NAMES'] = ', '.join(vlan_names) or '-'
|
||||
tokens['STAT_VLAN_COUNT'] = str(len(non_vpn_vlans))
|
||||
tokens['STAT_LEASE_COUNT'] = str(len(live_dhcp_leases()))
|
||||
tokens['STAT_BANNED_IP_COUNT'] = str(sum(1 for b in cfg.get('banned_ips', []) if b.get('enabled', True)))
|
||||
tokens['STAT_BLOCKLIST_COUNT'] = str(len(cfg.get('dns_blocking', {}).get('blocklists', [])))
|
||||
tokens['STAT_BLOCKED_TODAY'] = count_blocked_today()
|
||||
tokens['STAT_BLOCKED_DOMAINS'] = count_blocked_domains()
|
||||
tokens['STAT_BL_LAST_UPDATE'] = bl_last_update()
|
||||
tokens['STAT_UPTIME'] = factory.run('uptime -p') or '-'
|
||||
tokens['STAT_NFTABLES_STATUS'] = 'Active' if factory.run('nft list tables 2>/dev/null').strip() else 'Inactive'
|
||||
tokens['STAT_PUBLIC_IP'] = ip_str
|
||||
tokens['STAT_DDNS_HOSTNAME'] = domains_sub
|
||||
tokens['DNS_CACHE_SIZE'] = str(dns.get('cache_size', '-'))
|
||||
tokens['DNS_STAT_QUERIES'] = dns_stats['queries']
|
||||
tokens['DNS_STAT_HITS'] = dns_stats['hits']
|
||||
tokens['DNS_STAT_HIT_RATE'] = dns_stats['hit_rate']
|
||||
tokens['DNS_STAT_FORWARDED'] = dns_stats['forwarded']
|
||||
tokens['DNS_STAT_CACHE_EVICTIONS'] = dns_stats['cache_evictions']
|
||||
tokens['DNS_METRICS_UPDATED'] = dns_stats['updated']
|
||||
tokens['DNS_METRICS_SINCE'] = dns_stats['since']
|
||||
tokens['DNS_PROVIDERS_TABLE'] = _dns_providers_table(dns_stats['servers'])
|
||||
lease_count = len(live_dhcp_leases())
|
||||
tokens['STAT_LEASE_COUNT'] = str(lease_count)
|
||||
tokens['STAT_LEASES_LINK'] = f'<a href="/dhcpleases">{lease_count} active lease{"s" if lease_count != 1 else ""}</a>'
|
||||
tokens['STAT_VLAN_COUNT'] = str(len(non_vpn_vlans))
|
||||
tokens['STAT_PUBLIC_IP'] = ip_str
|
||||
tokens['STAT_DDNS_HOSTNAME'] = domains_sub
|
||||
|
||||
tokens['DNS_CACHE_SIZE'] = str(dns.get('cache_size', '-'))
|
||||
tokens['DNS_STAT_QUERIES'] = dns_stats['queries']
|
||||
tokens['DNS_STAT_HITS'] = dns_stats['hits']
|
||||
tokens['DNS_STAT_HIT_RATE'] = dns_stats['hit_rate']
|
||||
tokens['DNS_STAT_FORWARDED'] = dns_stats['forwarded']
|
||||
tokens['DNS_STAT_CACHE_EVICTIONS'] = dns_stats['cache_evictions']
|
||||
tokens['DNS_METRICS_UPDATED'] = dns_stats['updated']
|
||||
tokens['DNS_METRICS_SINCE'] = dns_stats['since']
|
||||
tokens['DNS_PROVIDERS_TABLE'] = _dns_providers_table(dns_stats['servers'])
|
||||
|
||||
tokens['STAT_BLOCKED_ALLTIME'] = all_time_blocked_display()
|
||||
tokens['HAS_QUERY_LOGGING'] = '1' if has_query_logging(cfg) else ''
|
||||
tokens['BLOCKED_DOMAINS_TABLE'] = blocked_domains_table()
|
||||
tokens['CLIENT_ACTIVITY_TABLE'] = client_activity_table()
|
||||
return tokens
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue