Development
This commit is contained in:
parent
d0cfffac52
commit
adcfe55c7c
24 changed files with 405 additions and 359 deletions
|
|
@ -4,7 +4,7 @@ import json, re, subprocess, os, sys, html as html_mod
|
|||
import sanitize
|
||||
import validation as validate
|
||||
from datetime import datetime, timezone
|
||||
from config_utils import core_hash, get_pending_entries, get_dashboard_pending, get_dashboard_done, load_snapshot_for_uuid, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime, WEB_APP_DISPLAY_NAME, CONFIGS_DIR, DATA_DIR
|
||||
from config_utils import config_hash, get_pending_entries, get_dashboard_pending, get_dashboard_done, load_snapshot_for_uuid, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime, WEB_APP_DISPLAY_NAME, CONFIGS_DIR, DATA_DIR
|
||||
|
||||
bp = Blueprint('view_page', __name__)
|
||||
|
||||
|
|
@ -44,8 +44,8 @@ def _load_json(path):
|
|||
print(f'[view_page] ERROR loading {path}: {ex}', file=sys.stderr)
|
||||
return {}
|
||||
|
||||
def _load_core(): return _load_json(f'{CONFIGS_DIR}/core.json')
|
||||
def _load_ddns(): return _load_core().get('ddns', {})
|
||||
def _load_config(): return _load_json(f'{CONFIGS_DIR}/config.json')
|
||||
def _load_ddns(): return _load_config().get('ddns', {})
|
||||
def _load_accounts(): return _load_json(f'{DATA_DIR}/authorized_accounts.json')
|
||||
|
||||
def _load_css():
|
||||
|
|
@ -149,17 +149,17 @@ def _iface_status(iface):
|
|||
return 'INVALID'
|
||||
|
||||
|
||||
def _resolve_iface(vlan, core):
|
||||
def _resolve_iface(vlan, cfg):
|
||||
"""Compute interface name from is_vpn + derived vlan_id + general.lan_interface."""
|
||||
if vlan.get('is_vpn'):
|
||||
wg_vlans = [v for v in core.get('vlans', []) if v.get('is_vpn')]
|
||||
wg_vlans = [v for v in cfg.get('vlans', []) if v.get('is_vpn')]
|
||||
wg_sorted = sorted(wg_vlans, key=lambda v: (
|
||||
validate.derive_vlan_id(v.get('subnet', ''), v.get('subnet_mask', 24)) is None,
|
||||
validate.derive_vlan_id(v.get('subnet', ''), v.get('subnet_mask', 24)) or 0,
|
||||
))
|
||||
idx = next((i for i, v in enumerate(wg_sorted) if v is vlan), 0)
|
||||
return f'wg{idx}'
|
||||
lan = core.get('network_interfaces', {}).get('lan_interface', 'eth0')
|
||||
lan = cfg.get('network_interfaces', {}).get('lan_interface', 'eth0')
|
||||
vid = validate.derive_vlan_id(vlan.get('subnet', ''), vlan.get('subnet_mask', 24)) or 1
|
||||
return lan if vid == 1 else f'{lan}.{vid}'
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ def _live_dhcp_leases():
|
|||
|
||||
def _vlan_name_for_ip(ip):
|
||||
import ipaddress
|
||||
for vlan in _load_core().get('vlans', []):
|
||||
for vlan in _load_config().get('vlans', []):
|
||||
subnet = vlan.get('subnet', '')
|
||||
mask = vlan.get('subnet_mask', 24)
|
||||
if not subnet:
|
||||
|
|
@ -254,11 +254,11 @@ def _fmt_bytes(n):
|
|||
# Config data loaders ===============================================
|
||||
|
||||
def _config_datasource(name):
|
||||
core = _load_core()
|
||||
vlans = core.get('vlans', [])
|
||||
cfg = _load_config()
|
||||
vlans = cfg.get('vlans', [])
|
||||
|
||||
if name == 'interfaces':
|
||||
gen = core.get('network_interfaces', {})
|
||||
gen = cfg.get('network_interfaces', {})
|
||||
wan = gen.get('wan_interface', '')
|
||||
lan = gen.get('lan_interface', '')
|
||||
return [
|
||||
|
|
@ -267,14 +267,14 @@ def _config_datasource(name):
|
|||
]
|
||||
|
||||
if name == 'banned_ips':
|
||||
return core.get('banned_ips', [])
|
||||
return cfg.get('banned_ips', [])
|
||||
|
||||
if name == 'host_overrides':
|
||||
return core.get('host_overrides', [])
|
||||
return cfg.get('host_overrides', [])
|
||||
|
||||
if name == 'blocklists':
|
||||
rows = []
|
||||
for bl in core.get('dns_blocking', {}).get('blocklists', []):
|
||||
for bl in cfg.get('dns_blocking', {}).get('blocklists', []):
|
||||
row = dict(bl)
|
||||
bl_path = f'{CONFIGS_DIR}/blocklists/{bl.get("save_as", "")}'
|
||||
try:
|
||||
|
|
@ -288,12 +288,12 @@ def _config_datasource(name):
|
|||
return rows
|
||||
|
||||
if name == 'vlans':
|
||||
bl_desc = {b['name']: b.get('description', b['name']) for b in core.get('dns_blocking', {}).get('blocklists', []) if 'name' in b}
|
||||
bl_desc = {b['name']: b.get('description', b['name']) for b in cfg.get('dns_blocking', {}).get('blocklists', []) if 'name' in b}
|
||||
rows = []
|
||||
for v in sorted(vlans, key=lambda x: validate.derive_vlan_id(x.get('subnet', ''), x.get('subnet_mask', 24)) or 0):
|
||||
row = {k: v.get(k) for k in ('name', 'subnet', 'subnet_mask', 'radius_default', 'mdns_reflection', 'is_vpn', 'dnsmasq_log_queries')}
|
||||
row['vlan_id'] = validate.derive_vlan_id(v.get('subnet', ''), v.get('subnet_mask', 24))
|
||||
row['interface'] = _resolve_iface(v, core)
|
||||
row['interface'] = _resolve_iface(v, cfg)
|
||||
row['use_blocklists'] = json.dumps([
|
||||
{'n': bl, 'd': bl_desc.get(bl, bl)} for bl in v.get('use_blocklists', [])
|
||||
])
|
||||
|
|
@ -301,10 +301,10 @@ def _config_datasource(name):
|
|||
return rows
|
||||
|
||||
if name == 'inter_vlan_exceptions':
|
||||
return core.get('inter_vlan_exceptions', [])
|
||||
return cfg.get('inter_vlan_exceptions', [])
|
||||
|
||||
if name == 'port_forwarding':
|
||||
return core.get('port_forwarding', [])
|
||||
return cfg.get('port_forwarding', [])
|
||||
|
||||
if name == 'dhcp_reservations':
|
||||
rows = []
|
||||
|
|
@ -418,10 +418,10 @@ def _bl_last_update():
|
|||
except Exception:
|
||||
return '-'
|
||||
|
||||
def _blocklist_stats_html(core):
|
||||
def _blocklist_stats_html(cfg):
|
||||
bl_dir = f'{CONFIGS_DIR}/blocklists'
|
||||
rows = ''
|
||||
for bl in core.get('dns_blocking', {}).get('blocklists', []):
|
||||
for bl in cfg.get('dns_blocking', {}).get('blocklists', []):
|
||||
name = e(bl.get('name', ''))
|
||||
save_as = bl.get('save_as', '')
|
||||
bl_path = f'{bl_dir}/{save_as}' if save_as else ''
|
||||
|
|
@ -546,7 +546,7 @@ def _ddns_last_checked():
|
|||
return 'Last checked: ---'
|
||||
|
||||
def _vpn_info():
|
||||
for vlan in _load_core().get('vlans', []):
|
||||
for vlan in _load_config().get('vlans', []):
|
||||
if 'vpn_information' in vlan:
|
||||
return vlan['vpn_information']
|
||||
return {}
|
||||
|
|
@ -556,11 +556,11 @@ def _vpn_info():
|
|||
|
||||
def collect_tokens():
|
||||
tokens = {}
|
||||
core = _load_core()
|
||||
net = core.get('network_interfaces', {})
|
||||
dns_blk_gen = core.get('dns_blocking', {}).get('general', {})
|
||||
dns = core.get('upstream_dns', {})
|
||||
vlans = core.get('vlans', [])
|
||||
cfg = _load_config()
|
||||
net = cfg.get('network_interfaces', {})
|
||||
dns_blk_gen = cfg.get('dns_blocking', {}).get('general', {})
|
||||
dns = cfg.get('upstream_dns', {})
|
||||
vlans = cfg.get('vlans', [])
|
||||
tokens['GENERAL_WAN_INTERFACE'] = str(net.get('wan_interface', '-'))
|
||||
tokens['GENERAL_LAN_INTERFACE'] = str(net.get('lan_interface', '-'))
|
||||
tokens['GENERAL_WAN_STATUS'] = _iface_status(net.get('wan_interface', ''))
|
||||
|
|
@ -693,10 +693,10 @@ def collect_tokens():
|
|||
tokens['VPN_VLAN_COUNT'] = str(sum(1 for v in vlans if v.get('is_vpn')))
|
||||
tokens['EXISTING_VLAN_IDS_JSON'] = json.dumps([validate.derive_vlan_id(v.get('subnet', ''), v.get('subnet_mask', 24)) for v in vlans])
|
||||
tokens['EXISTING_VLAN_NAMES_JSON'] = json.dumps([v.get('name', '') for v in vlans])
|
||||
tokens['EXISTING_VLAN_INTERFACES_JSON'] = json.dumps([_resolve_iface(v, core) for v in vlans])
|
||||
tokens['STAT_BANNED_IP_COUNT'] = str(sum(1 for b in core.get('banned_ips', []) if b.get('enabled', True)))
|
||||
tokens['STAT_BLOCKLIST_COUNT'] = str(len(core.get('dns_blocking', {}).get('blocklists', [])))
|
||||
tokens['BLOCKLIST_STATS_HTML'] = _blocklist_stats_html(core)
|
||||
tokens['EXISTING_VLAN_INTERFACES_JSON'] = json.dumps([_resolve_iface(v, cfg) for v in vlans])
|
||||
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['BLOCKLIST_STATS_HTML'] = _blocklist_stats_html(cfg)
|
||||
|
||||
ddns = _load_ddns()
|
||||
ddns_gen = ddns.get('general', {})
|
||||
|
|
@ -795,7 +795,7 @@ def collect_tokens():
|
|||
|
||||
tokens['BLOCKLIST_NAME_OPTIONS'] = json.dumps([
|
||||
{'value': bl.get('name', ''), 'label': bl.get('description', bl.get('name', ''))}
|
||||
for bl in core.get('dns_blocking', {}).get('blocklists', [])
|
||||
for bl in cfg.get('dns_blocking', {}).get('blocklists', [])
|
||||
])
|
||||
|
||||
tokens['ACCOUNT_LEVEL_OPTIONS'] = json.dumps([
|
||||
|
|
@ -984,7 +984,7 @@ def _render_item(item, tokens, inherited_req=None):
|
|||
f'<button type="button" class="btn btn-ghost btn-sm stat-card-edit-btn">Edit</button>'
|
||||
f'</div>'
|
||||
f'<form class="stat-card-edit-form" style="display:none" action="{e(edit_action)}" method="post">'
|
||||
f'<input type="hidden" name="config_hash" value="{e(core_hash())}"/>'
|
||||
f'<input type="hidden" name="config_hash" value="{e(config_hash())}"/>'
|
||||
f'{input_wrap}'
|
||||
f'<div style="margin-top:0.5em;display:flex;gap:0.5em">'
|
||||
f'<button type="submit" class="btn btn-primary btn-sm" disabled>Save</button>'
|
||||
|
|
@ -1066,7 +1066,7 @@ def _render_item(item, tokens, inherited_req=None):
|
|||
action = e(apply_tokens(item.get('action', ''), tokens))
|
||||
method = e(item.get('method', 'post'))
|
||||
inner = render_items(item.get('items', []), tokens, req)
|
||||
hash_field = f'<input type="hidden" name="config_hash" value="{e(core_hash())}"/>'
|
||||
hash_field = f'<input type="hidden" name="config_hash" value="{e(config_hash())}"/>'
|
||||
originals = _collect_form_originals(item.get('items', []), tokens)
|
||||
orig_field = (f'<input type="hidden" name="original_values" value="{e(json.dumps(originals))}"/>'
|
||||
if originals else '')
|
||||
|
|
@ -1406,7 +1406,7 @@ def _render_table(item, tokens, inherited_req=None):
|
|||
rows = _load_datasource(item.get('datasource', ''))
|
||||
empty = e(item.get('empty_message', 'No data.'))
|
||||
row_actions = item.get('row_actions', [])
|
||||
hash_val = core_hash()
|
||||
hash_val = config_hash()
|
||||
|
||||
toolbar_html = ''
|
||||
toolbar = item.get('toolbar')
|
||||
|
|
@ -1594,7 +1594,7 @@ def render_layout(view_id, content_html, tokens):
|
|||
navbar_html = _render_navbar(view_id, level, tokens)
|
||||
footer_html = f'<footer class="footer">{WEB_APP_DISPLAY_NAME}</footer>'
|
||||
|
||||
page_hash = core_hash()
|
||||
page_hash = config_hash()
|
||||
lan_iface = e(tokens.get('GENERAL_LAN_INTERFACE', ''))
|
||||
vpn_count = tokens.get('VPN_VLAN_COUNT', '0')
|
||||
existing_ids = tokens.get('EXISTING_VLAN_IDS_JSON', '[]')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue