Development
This commit is contained in:
parent
e98eb85c5a
commit
278995958a
6 changed files with 39 additions and 18 deletions
|
|
@ -5,7 +5,7 @@ import sanitize
|
|||
import validation as validate
|
||||
from datetime import datetime, timezone
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
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, CONFIGS_DIR, DATA_DIR
|
||||
from config_utils import core_hash, get_pending_entries, get_dashboard_pending, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime, WEB_APP_DISPLAY_NAME, CONFIGS_DIR, DATA_DIR, DDNS_TIMER_NAME
|
||||
|
||||
bp = Blueprint('view_page', __name__)
|
||||
|
||||
|
|
@ -540,7 +540,7 @@ def _public_ip_info(ddns_cfg):
|
|||
next_interval = '-'
|
||||
|
||||
def _last_obtained(mtime):
|
||||
return f'Last obtained: {_relative_time(mtime)}' if mtime else ''
|
||||
return f'Obtained: {_relative_time(mtime)}' if mtime else ''
|
||||
|
||||
# Path 1: timer healthy and within interval -> use cached IP
|
||||
if interval_secs and enabled_p:
|
||||
|
|
@ -565,6 +565,22 @@ def _public_ip_info(ddns_cfg):
|
|||
# Path 3: offline
|
||||
return 'DDNS Offline', domains_sub, next_interval, ''
|
||||
|
||||
def _ddns_last_checked():
|
||||
"""Return 'Last checked: X ago' based on when the DDNS timer last fired, or ''."""
|
||||
try:
|
||||
out = _run(f'systemctl show {DDNS_TIMER_NAME}.timer --property=LastTriggerUSec --timestamp=utc')
|
||||
val = out.split('=', 1)[1].strip() if '=' in out else ''
|
||||
if not val or val == '0' or val == 'n/a':
|
||||
return ''
|
||||
parts = val.split() # ['Mon', '2026-05-25', '04:28:00', 'UTC']
|
||||
if len(parts) >= 3:
|
||||
dt = datetime.strptime(f'{parts[1]} {parts[2]}', '%Y-%m-%d %H:%M:%S')
|
||||
mtime = dt.replace(tzinfo=timezone.utc).timestamp()
|
||||
return f'Last checked: {_relative_time(mtime)}'
|
||||
except Exception:
|
||||
pass
|
||||
return ''
|
||||
|
||||
def _vpn_info():
|
||||
for vlan in _load_core().get('vlans', []):
|
||||
if 'vpn_information' in vlan:
|
||||
|
|
@ -711,10 +727,11 @@ def collect_tokens():
|
|||
tokens['VPN_GATEWAY'] = ''
|
||||
|
||||
ip_str, sub_str, next_interval, last_obtained = _public_ip_info(ddns)
|
||||
tokens['STAT_PUBLIC_IP'] = ip_str
|
||||
tokens['STAT_DDNS_HOSTNAME'] = sub_str
|
||||
tokens['STAT_DDNS_NEXT_INTERVAL'] = next_interval
|
||||
tokens['STAT_PUBLIC_IP'] = ip_str
|
||||
tokens['STAT_DDNS_HOSTNAME'] = sub_str
|
||||
tokens['STAT_DDNS_NEXT_INTERVAL'] = next_interval
|
||||
tokens['STAT_PUBLIC_IP_LAST_OBTAINED'] = last_obtained
|
||||
tokens['STAT_PUBLIC_IP_LAST_CHECKED'] = _ddns_last_checked()
|
||||
tokens['DDNS_LOG_TAIL'], tokens['DDNS_LOG_SUMMARY'] = _ddns_log_tail()
|
||||
tokens['STAT_UPTIME'] = _run('uptime -p') or '-'
|
||||
tokens['STAT_NFTABLES_STATUS'] = 'Active' if _run('nft list tables 2>/dev/null').strip() else 'Inactive'
|
||||
|
|
@ -1451,9 +1468,9 @@ def _load_datasource(spec):
|
|||
def render_layout(view_id, content_html, tokens):
|
||||
css = _load_css()
|
||||
level = _client_level()
|
||||
titlebar_html = f'<div class="titlebar"><span class="titlebar-brand">{PRODUCT_DISPLAY_NAME}</span></div>'
|
||||
titlebar_html = f'<div class="titlebar"><span class="titlebar-brand">{WEB_APP_DISPLAY_NAME}</span></div>'
|
||||
navbar_html = _render_navbar(view_id, level, tokens)
|
||||
footer_html = f'<footer class="footer">{PRODUCT_DISPLAY_NAME}</footer>'
|
||||
footer_html = f'<footer class="footer">{WEB_APP_DISPLAY_NAME}</footer>'
|
||||
|
||||
page_hash = core_hash()
|
||||
lan_iface = e(tokens.get('GENERAL_LAN_INTERFACE', ''))
|
||||
|
|
@ -1535,8 +1552,10 @@ def render_layout(view_id, content_html, tokens):
|
|||
fix_items = ''.join(f'<li><code>{e(c)}</code></li>' for c in fix_cmds)
|
||||
fix_html = ('<div style="margin-top:0.5em">To fix:</div>'
|
||||
f'<ul style="margin:0.25em 0;padding-left:1.25em">{fix_items}</ul>')
|
||||
content = ('Health check — problems found:'
|
||||
+ problems_list + fix_html)
|
||||
content = ('<div style="width:100%">'
|
||||
'<div style="font-weight:600;margin-bottom:0.25em">Health check — problems found:</div>'
|
||||
+ problems_list + fix_html
|
||||
+ '</div>')
|
||||
problem_bars += f'<div class="info-bar {cls}">{content}</div>\n'
|
||||
except Exception:
|
||||
pass
|
||||
|
|
@ -1544,7 +1563,7 @@ def render_layout(view_id, content_html, tokens):
|
|||
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'
|
||||
f' <title>{PRODUCT_DISPLAY_NAME}</title>\n'
|
||||
f' <title>{WEB_APP_DISPLAY_NAME}</title>\n'
|
||||
f' <style>{css}</style>\n'
|
||||
f'</head>\n<body>\n'
|
||||
f'{titlebar_html}\n'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue