From 1a38cab9c9b8291024f89e93babec7ad16ba2faf Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 13 Jun 2026 00:53:02 +0000 Subject: [PATCH] fix: prune stale HWLAB public Caddy blocks --- scripts/src/hwlab-node.ts | 45 +++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/scripts/src/hwlab-node.ts b/scripts/src/hwlab-node.ts index b987cfdf..332a9e55 100644 --- a/scripts/src/hwlab-node.ts +++ b/scripts/src/hwlab-node.ts @@ -3272,6 +3272,8 @@ function publicExposureCaddyStatus(fields: Record, result: Comma configPath: fields.configPath || null, beforeBlockPresent: fields.beforeBlockPresent === "yes", afterBlockPresent: fields.afterBlockPresent === "yes", + staleBlocksRemoved: numericField(fields.staleBlocksRemoved), + pythonExitCode: numericField(fields.pythonExitCode), validateMode: fields.validateMode || null, validateExitCode: numericField(fields.validateExitCode), reloadExitCode: numericField(fields.reloadExitCode), @@ -3486,7 +3488,9 @@ function publicExposureCaddyScript(options: NodePublicExposureOptions, exposure: `marker=${shellQuote(marker)}`, `block_b64=${shellQuote(blockB64)}`, `expected_a=${shellQuote(exposure.expectedA)}`, - "rm -f /tmp/hwlab-public-caddy-validate.out /tmp/hwlab-public-caddy-validate.err /tmp/hwlab-public-caddy-web.err /tmp/hwlab-public-caddy-api.err", + `web_port=${shellQuote(String(exposure.webProxy.remotePort))}`, + `api_port=${shellQuote(String(exposure.apiProxy.remotePort))}`, + "rm -f /tmp/hwlab-public-caddy-validate.out /tmp/hwlab-public-caddy-validate.err /tmp/hwlab-public-caddy-python.err /tmp/hwlab-public-caddy-web.err /tmp/hwlab-public-caddy-api.err", "tmp=$(mktemp -d)", "block=\"$tmp/block\"", "next=\"$tmp/Caddyfile\"", @@ -3494,15 +3498,28 @@ function publicExposureCaddyScript(options: NodePublicExposureOptions, exposure: "before_present=no", "if [ -f \"$config_path\" ] && grep -Fq \"# BEGIN $marker\" \"$config_path\"; then before_present=yes; fi", "if [ -f \"$config_path\" ]; then cp \"$config_path\" \"$next\"; else : >\"$next\"; fi", - "python3 - \"$next\" \"$block\" \"$marker\" <<'PY'", - "import pathlib, sys", + "stale_blocks_removed=$(python3 - \"$next\" \"$block\" \"$marker\" \"$web_port\" \"$api_port\" 2>/tmp/hwlab-public-caddy-python.err <<'PY'", + "import pathlib, re, sys", "config = pathlib.Path(sys.argv[1])", "block = pathlib.Path(sys.argv[2]).read_text(encoding='utf-8')", "marker = sys.argv[3]", + "web_port = sys.argv[4]", + "api_port = sys.argv[5]", + "current_name = marker[len('unidesk managed '):] if marker.startswith('unidesk managed ') else marker", "text = config.read_text(encoding='utf-8') if config.exists() else ''", "begin = f'# BEGIN {marker}'", "end = f'# END {marker}'", "managed = f'{begin}\\n{block.rstrip()}\\n{end}\\n'", + "stale_removed = [0]", + "pattern = re.compile(r'(?ms)^# BEGIN unidesk managed (?P[^\\n]+)\\n(?P.*?)\\n# END unidesk managed (?P=name)\\n*')", + "def keep(match):", + " name = match.group('name')", + " body = match.group('body')", + " if name != current_name and (f'127.0.0.1:{web_port}' in body or f'127.0.0.1:{api_port}' in body):", + " stale_removed[0] += 1", + " return ''", + " return match.group(0)", + "text = pattern.sub(keep, text)", "if begin in text and end in text:", " start = text.index(begin)", " stop = text.index(end, start) + len(end)", @@ -3510,16 +3527,24 @@ function publicExposureCaddyScript(options: NodePublicExposureOptions, exposure: "else:", " text = text.rstrip() + '\\n\\n' + managed", "config.write_text(text, encoding='utf-8')", + "print(stale_removed[0])", "PY", + ")", + "python_exit=$?", "validate_exit=1", "validate_mode=validate", - "if [ \"$dry_run\" = true ]; then", - " validate_mode=adapt", - " caddy adapt --config \"$next\" --adapter caddyfile >/tmp/hwlab-public-caddy-validate.out 2>/tmp/hwlab-public-caddy-validate.err", + "if [ \"$python_exit\" != 0 ]; then", + " validate_mode=python", + " validate_exit=$python_exit", "else", - " sudo caddy validate --config \"$next\" --adapter caddyfile >/tmp/hwlab-public-caddy-validate.out 2>/tmp/hwlab-public-caddy-validate.err", + " if [ \"$dry_run\" = true ]; then", + " validate_mode=adapt", + " caddy adapt --config \"$next\" --adapter caddyfile >/tmp/hwlab-public-caddy-validate.out 2>/tmp/hwlab-public-caddy-validate.err", + " else", + " sudo caddy validate --config \"$next\" --adapter caddyfile >/tmp/hwlab-public-caddy-validate.out 2>/tmp/hwlab-public-caddy-validate.err", + " fi", + " validate_exit=$?", "fi", - "validate_exit=$?", "reload_exit=", "mutation=false", "if [ \"$dry_run\" = false ] && [ \"$validate_exit\" = 0 ]; then", @@ -3542,7 +3567,7 @@ function publicExposureCaddyScript(options: NodePublicExposureOptions, exposure: " local_web_status=$(curl -kfsS --max-time 15 --resolve \"$hostname:443:127.0.0.1\" \"https://$hostname/\" -o /dev/null -w '%{http_code}' 2>/tmp/hwlab-public-caddy-web.err || true)", " local_api_status=$(curl -kfsS --max-time 15 --resolve \"$hostname:443:127.0.0.1\" \"https://$hostname/health/live\" -o /dev/null -w '%{http_code}' 2>/tmp/hwlab-public-caddy-api.err || true)", "fi", - "validate_err_preview=$([ -f /tmp/hwlab-public-caddy-validate.err ] && tr '\\n' ';' /dev/null | cut -c1-1000 || true)", + "validate_err_preview=$(cat /tmp/hwlab-public-caddy-python.err /tmp/hwlab-public-caddy-validate.err 2>/dev/null | tr '\\n' ';' | cut -c1-1000 || true)", "local_web_err_preview=$([ -f /tmp/hwlab-public-caddy-web.err ] && tr '\\n' ';' /dev/null | cut -c1-1000 || true)", "local_api_err_preview=$([ -f /tmp/hwlab-public-caddy-api.err ] && tr '\\n' ';' /dev/null | cut -c1-1000 || true)", "printf 'hostname\\t%s\\n' \"$hostname\"", @@ -3552,6 +3577,8 @@ function publicExposureCaddyScript(options: NodePublicExposureOptions, exposure: "printf 'mutation\\t%s\\n' \"$mutation\"", "printf 'beforeBlockPresent\\t%s\\n' \"$before_present\"", "printf 'afterBlockPresent\\t%s\\n' \"$after_present\"", + "printf 'staleBlocksRemoved\\t%s\\n' \"$stale_blocks_removed\"", + "printf 'pythonExitCode\\t%s\\n' \"$python_exit\"", "printf 'validateMode\\t%s\\n' \"$validate_mode\"", "printf 'validateExitCode\\t%s\\n' \"$validate_exit\"", "printf 'reloadExitCode\\t%s\\n' \"$reload_exit\"",