Development

This commit is contained in:
Matthew Grotke 2026-05-22 01:09:23 -04:00
parent cc2f57aa83
commit 74166f03bd
11 changed files with 986 additions and 61 deletions

View file

@ -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, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime, PRODUCT_DISPLAY_NAME
from config_utils import core_hash, get_pending_entries, get_dashboard_pending, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime, PRODUCT_DISPLAY_NAME
bp = Blueprint('view_page', __name__)
@ -529,6 +529,38 @@ def collect_tokens():
tokens['GENERAL_LOG_ERRORS_ONLY'] = 'true' if gen.get('log_errors_only') else 'false'
tokens['GENERAL_DNSMASQ_LOG_QUERIES'] = 'true' if gen.get('dnsmasq_log_queries') else 'false'
tokens['GENERAL_DAILY_EXECUTE_TIME'] = str(gen.get('daily_execute_time_24hr_local', '-'))
tokens['GENERAL_APPLY_ON_SAVE'] = 'true' if gen.get('apply_on_save', True) else 'false'
pending_items = get_dashboard_pending()
if pending_items:
rows = ''
for _uuid, ts, cmd, user, desc in pending_items:
dt_str = datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M')
label = e(desc) if desc else e(cmd)
rows += (f'<tr><td class="table-cell">{e(dt_str)}</td>'
f'<td class="table-cell">{label}</td>'
f'<td class="table-cell">{e(user)}</td></tr>')
pending_html = (
'<hr class="divider">'
'<h3 style="margin:0 0 0.75rem 0;font-size:0.85rem;font-weight:600;'
'text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted)">Pending Changes</h3>'
'<table class="data-table" style="margin-bottom:1rem">'
'<thead><tr>'
'<th class="table-header">Time</th>'
'<th class="table-header">Change</th>'
'<th class="table-header">User</th>'
'</tr></thead>'
f'<tbody>{rows}</tbody>'
'</table>'
'<form method="post" action="/action/apply_pending">'
f'<input type="hidden" name="config_hash" value="{e(core_hash())}">'
'<div class="button-row">'
'<button type="submit" class="btn btn-primary">Apply Now</button>'
'</div></form>'
)
else:
pending_html = ''
tokens['PENDING_CHANGES_HTML'] = pending_html
servers = dns.get('upstream_servers', [])
tokens['DNS_STRICT_ORDER'] = 'true' if dns.get('strict_order') else 'false'
@ -895,6 +927,9 @@ def _render_item(item, tokens, inherited_req=None):
if t == 'table':
return _render_table(item, tokens, req)
if t == 'raw_html':
return Markup(apply_tokens(item.get('html', ''), tokens))
return ''
@ -1364,6 +1399,23 @@ def render_layout(view_id, content_html, tokens):
cls = 'info-bar-warning'
other_bars += f'<div class="info-bar {cls}" data-apply-uuid="{e(o_uuid)}" data-apply-user="{e(o_user)}">{text}</div>\n'
problem_bars = ''
try:
import json as _j
st = _j.load(open(f'{CONFIGS_DIR}/.status'))
for section in ('configurations', 'logs'):
for item in st.get(section, []):
if item.get('status') == 'problem':
sev = item.get('severity', 'error')
cls = 'info-bar-danger' if sev == 'error' else 'info-bar-warning'
text = e(item.get('detail', item.get('name', '')))
tip = item.get('suggestion', '')
if tip:
text += f' <span style="opacity:0.75">— {e(tip)}</span>'
problem_bars += f'<div class="info-bar {cls}">{text}</div>\n'
except Exception:
pass
return (f'<!DOCTYPE html>\n<html lang="en">\n<head>\n'
f' <meta charset="UTF-8">\n'
f' <meta name="viewport" content="width=device-width, initial-scale=1.0">\n'
@ -1372,7 +1424,7 @@ def render_layout(view_id, content_html, tokens):
f'</head>\n<body>\n'
f'{titlebar_html}\n'
f'{navbar_html}\n'
f'<main class="main-content">\n{other_bars}{content_html}\n</main>\n'
f'<main class="main-content">\n{problem_bars}{other_bars}{content_html}\n</main>\n'
f'{footer_html}\n'
f'<script>var CONFIG_HASH="{page_hash}";var LAN_IFACE="{lan_iface}";var VPN_VLAN_COUNT={vpn_count};var EXISTING_VLAN_IDS={existing_ids};var EXISTING_VLAN_NAMES={existing_names};var EXISTING_VLAN_INTERFACES={existing_interfaces};var APPLY_UUID={json.dumps(my_uuid)};</script>\n'
f'<script>{_inline_js()}</script>\n'