Development
This commit is contained in:
parent
80390334fb
commit
e98eb85c5a
2 changed files with 39 additions and 24 deletions
|
|
@ -513,8 +513,9 @@ def _parse_time_remaining(text):
|
|||
return None
|
||||
|
||||
def _read_cached_ip():
|
||||
"""Return (ip, mtime) from the most recent .ddns-last-ip-* file, or ('', None)."""
|
||||
try:
|
||||
best_ip, best_mtime = '', 0
|
||||
best_ip, best_mtime = '', 0.0
|
||||
for fname in os.listdir(CONFIGS_DIR):
|
||||
if fname.startswith('.ddns-last-ip-'):
|
||||
path = f'{CONFIGS_DIR}/{fname}'
|
||||
|
|
@ -523,12 +524,12 @@ def _read_cached_ip():
|
|||
ip = open(path).read().strip()
|
||||
if ip:
|
||||
best_ip, best_mtime = ip, mtime
|
||||
return best_ip
|
||||
return best_ip, (best_mtime if best_ip else None)
|
||||
except Exception:
|
||||
return ''
|
||||
return '', None
|
||||
|
||||
def _public_ip_info(ddns_cfg):
|
||||
"""Return (ip_str, domains_sub, next_interval_str) for stat cards."""
|
||||
"""Return (ip_str, domains_sub, next_interval_str, last_obtained_str) for stat cards."""
|
||||
script = f'{CONFIGS_DIR}/ddns.py'
|
||||
enabled_p = [p for p in ddns_cfg.get('providers', []) if p.get('enabled', True)]
|
||||
all_hosts = []
|
||||
|
|
@ -538,6 +539,9 @@ def _public_ip_info(ddns_cfg):
|
|||
interval_secs = _parse_interval_to_seconds(ddns_cfg.get('general', {}).get('timer_interval', ''))
|
||||
next_interval = '-'
|
||||
|
||||
def _last_obtained(mtime):
|
||||
return f'Last obtained: {_relative_time(mtime)}' if mtime else ''
|
||||
|
||||
# Path 1: timer healthy and within interval -> use cached IP
|
||||
if interval_secs and enabled_p:
|
||||
status = _run(f'python3 {script} --status 2>/dev/null')
|
||||
|
|
@ -548,17 +552,18 @@ def _public_ip_info(ddns_cfg):
|
|||
if remaining is not None:
|
||||
next_interval = _fmt_seconds(remaining)
|
||||
if is_enabled and is_active and remaining is not None and remaining < interval_secs:
|
||||
ip = _read_cached_ip()
|
||||
ip, mtime = _read_cached_ip()
|
||||
if ip:
|
||||
return ip, domains_sub, next_interval
|
||||
return ip, domains_sub, next_interval, _last_obtained(mtime)
|
||||
|
||||
# Path 2: live fetch
|
||||
ip = _run(f'python3 {script} --getip 2>/dev/null')
|
||||
if ip and re.match(r'^\d{1,3}(\.\d{1,3}){3}$', ip):
|
||||
return ip, domains_sub, next_interval
|
||||
_, mtime = _read_cached_ip()
|
||||
return ip, domains_sub, next_interval, _last_obtained(mtime)
|
||||
|
||||
# Path 3: offline
|
||||
return 'DDNS Offline', domains_sub, next_interval
|
||||
return 'DDNS Offline', domains_sub, next_interval, ''
|
||||
|
||||
def _vpn_info():
|
||||
for vlan in _load_core().get('vlans', []):
|
||||
|
|
@ -705,10 +710,11 @@ def collect_tokens():
|
|||
except Exception:
|
||||
tokens['VPN_GATEWAY'] = ''
|
||||
|
||||
ip_str, sub_str, next_interval = _public_ip_info(ddns)
|
||||
tokens['STAT_PUBLIC_IP'] = ip_str
|
||||
tokens['STAT_DDNS_HOSTNAME'] = sub_str
|
||||
tokens['STAT_DDNS_NEXT_INTERVAL'] = next_interval
|
||||
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_LAST_OBTAINED'] = last_obtained
|
||||
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'
|
||||
|
|
@ -1490,9 +1496,7 @@ def render_layout(view_id, content_html, tokens):
|
|||
sev = item.get('severity', 'error')
|
||||
text = e(item.get('detail', item.get('name', '')))
|
||||
tip = item.get('suggestion', '')
|
||||
if tip:
|
||||
text += f' <span style="opacity:0.75">{e(tip)}</span>'
|
||||
grouped.setdefault(sev, []).append(text)
|
||||
grouped.setdefault(sev, []).append((text, tip))
|
||||
for item in st.get('services', []):
|
||||
if item.get('status') == 'problem':
|
||||
name = item.get('name', '')
|
||||
|
|
@ -1510,18 +1514,29 @@ def render_layout(view_id, content_html, tokens):
|
|||
f"{' and '.join(exp_parts)} but is {' and '.join(act_parts)}.")
|
||||
tip = f"Run `sudo python3 core.py --apply` to {' and '.join(reversed(fix_parts))} it."
|
||||
sev = item.get('severity', 'error')
|
||||
text = e(detail)
|
||||
if tip:
|
||||
text += f' <span style="opacity:0.75">{e(tip)}</span>'
|
||||
grouped.setdefault(sev, []).append(text)
|
||||
grouped.setdefault(sev, []).append((e(detail), tip))
|
||||
for sev, items in grouped.items():
|
||||
if not items:
|
||||
continue
|
||||
cls = 'info-bar-danger' if sev == 'error' else 'info-bar-warning'
|
||||
if len(items) == 1:
|
||||
content = items[0]
|
||||
else:
|
||||
content = '<ul style="margin:0;padding-left:1.25em">' + ''.join(f'<li>{t}</li>' for t in items) + '</ul>'
|
||||
seen_cmds, fix_cmds = set(), []
|
||||
for _, tip in items:
|
||||
if tip:
|
||||
m = re.search(r'`([^`]+)`', tip)
|
||||
cmd = m.group(1) if m else tip
|
||||
if cmd not in seen_cmds:
|
||||
seen_cmds.add(cmd)
|
||||
fix_cmds.append(cmd)
|
||||
problems_list = ('<ul style="margin:0.25em 0;padding-left:1.25em">'
|
||||
+ ''.join(f'<li>{d}</li>' for d, _ in items)
|
||||
+ '</ul>')
|
||||
fix_html = ''
|
||||
if fix_cmds:
|
||||
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)
|
||||
problem_bars += f'<div class="info-bar {cls}">{content}</div>\n'
|
||||
except Exception:
|
||||
pass
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue