diff --git a/routlin/core.py b/routlin/core.py index 705c695..bfda7cf 100644 --- a/routlin/core.py +++ b/routlin/core.py @@ -105,6 +105,7 @@ PRODUCT_NAME = "routlin" SCRIPT_DIR = Path(__file__).parent CONFIG_FILE = SCRIPT_DIR / "config.json" +DASHBOARD_PENDING = SCRIPT_DIR / ".dashboard-pending" BLOCKLIST_DIR = SCRIPT_DIR / "blocklists" METRICS_FILE = SCRIPT_DIR / ".dns-metrics" DNSMASQ_CONF_DIR = Path(f"/etc/dnsmasq-{PRODUCT_NAME}") @@ -2133,9 +2134,21 @@ def disable_avahi(): print("avahi-daemon: not running, skipping.") +def _remove_pending_cmd(cmd): + try: + if not DASHBOARD_PENDING.exists(): + return + lines = DASHBOARD_PENDING.read_text().splitlines() + kept = [l for l in lines if f'[{cmd}]' not in l] + DASHBOARD_PENDING.write_text('\n'.join(kept) + ('\n' if kept else '')) + except Exception: + pass + + def show_status(data): import health as _health - _health.print_table(_health.run_and_write(data)) + _, _status = _health.run_and_write(data) + _health.print_table(_status) def show_configs(data): for vlan in data["vlans"]: @@ -3093,7 +3106,12 @@ def cmd_apply(data, dry_run=False): print("Done.") import health as _health - _health.print_table(_health.run_and_write(data)) + _healthy, _status = _health.run_and_write(data) + _health.print_table(_status) + + _remove_pending_cmd('core apply') + if _healthy: + _remove_pending_cmd('fix problems') def main(): diff --git a/routlin/health.py b/routlin/health.py index 2430385..1965e48 100644 --- a/routlin/health.py +++ b/routlin/health.py @@ -5,7 +5,7 @@ Reads config.json, checks services, configuration files, and logs, then writes .health JSON. Imported by core.py; also runnable standalone. Public API: - run_and_write(data) -> dict run all checks, write .health, return dict + run_and_write(data) -> (bool, dict) run all checks, write .health, return (all_healthy, status) print_table(status: dict) render the CLI service table from status dict """ import hashlib @@ -711,7 +711,7 @@ def _next_blocklist_update(): # =================================================================== def run_and_write(data): - """Run all checks, write .health atomically, return the status dict.""" + """Run all checks, write .health atomically, return (all_healthy, status_dict).""" status = { "checked_at": datetime.now().strftime("%Y-%m-%dT%H:%M:%S"), "services": check_services(data), @@ -722,7 +722,12 @@ def run_and_write(data): tmp = HEALTH_FILE.with_suffix(".tmp") tmp.write_text(json.dumps(status, indent=2)) tmp.replace(HEALTH_FILE) - return status + healthy = all( + item.get('status') != 'problem' + for section in ('services', 'configurations', 'logs') + for item in status.get(section, []) + ) + return healthy, status def print_table(status): @@ -797,5 +802,5 @@ if __name__ == "__main__": except Exception as ex: print(f"Error loading {CONFIG_FILE}: {ex}", file=sys.stderr) sys.exit(1) - status = run_and_write(data) + _, status = run_and_write(data) print_table(status)