From 86a45612c81f9b62e1e988760c418ddc2c83e741 Mon Sep 17 00:00:00 2001 From: Nexus Agent Date: Sun, 14 Jun 2026 04:23:20 +0800 Subject: [PATCH] fix(ops): writable Layer3 state dir for cron user Fall back to ~/.nexus/layer3-health when deploy/var is root-owned. --- deploy/health_monitor.sh | 28 ++++++++++++++++--- ...26-06-14-layer3-health-monitor-p2-p3-ui.md | 2 +- tests/test_ops_patrol_sync.py | 4 +-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/deploy/health_monitor.sh b/deploy/health_monitor.sh index e01a1fa8..d1a8569c 100644 --- a/deploy/health_monitor.sh +++ b/deploy/health_monitor.sh @@ -9,9 +9,9 @@ set -euo pipefail DEPLOY_DIR="${NEXUS_DEPLOY_DIR:-/opt/nexus}" NEXUS_SERVICE="${NEXUS_SERVICE:-nexus}" -STATE_DIR="${DEPLOY_DIR}/var/layer3-health" -FAIL_FILE="${STATE_DIR}/fail_count" -LAST_RESTART_FILE="${STATE_DIR}/last_restart" +STATE_DIR="" +FAIL_FILE="" +LAST_RESTART_FILE="" # Legacy paths (cron user may have created root-owned files under /tmp) LEGACY_FAIL_FILE="/tmp/nexus_health_fail_count" LEGACY_LAST_RESTART_FILE="/tmp/nexus_health_last_restart" @@ -19,6 +19,26 @@ MAX_FAIL=3 RESTART_COOLDOWN_SEC="${NEXUS_HEALTH_RESTART_COOLDOWN:-300}" ENV_PROD="${DEPLOY_DIR}/docker/.env.prod" +ensure_state_paths() { + if [[ -n "$STATE_DIR" ]]; then + return 0 + fi + local preferred="${DEPLOY_DIR}/var/layer3-health" + local fallback="${HOME:-/tmp}/.nexus/layer3-health" + if mkdir -p "$preferred" 2>/dev/null && [[ -w "$preferred" ]]; then + STATE_DIR="$preferred" + elif command -v sudo >/dev/null 2>&1 && sudo mkdir -p "$preferred" 2>/dev/null \ + && sudo chown -R "$(id -un):$(id -gn)" "$preferred" 2>/dev/null \ + && [[ -w "$preferred" ]]; then + STATE_DIR="$preferred" + else + mkdir -p "$fallback" + STATE_DIR="$fallback" + fi + FAIL_FILE="${STATE_DIR}/fail_count" + LAST_RESTART_FILE="${STATE_DIR}/last_restart" +} + docker_cmd() { if docker "$@" 2>/dev/null; then return 0 @@ -31,7 +51,7 @@ docker_cmd() { } init_layer3_state() { - mkdir -p "$STATE_DIR" + ensure_state_paths chmod 755 "$STATE_DIR" 2>/dev/null || true if [[ ! -f "$FAIL_FILE" && -f "$LEGACY_FAIL_FILE" ]]; then cp "$LEGACY_FAIL_FILE" "$FAIL_FILE" 2>/dev/null || true diff --git a/docs/changelog/2026-06-14-layer3-health-monitor-p2-p3-ui.md b/docs/changelog/2026-06-14-layer3-health-monitor-p2-p3-ui.md index a57632af..166ba30e 100644 --- a/docs/changelog/2026-06-14-layer3-health-monitor-p2-p3-ui.md +++ b/docs/changelog/2026-06-14-layer3-health-monitor-p2-p3-ui.md @@ -10,7 +10,7 @@ ## 变更 -- `deploy/health_monitor.sh`:状态迁至 `${DEPLOY_DIR}/var/layer3-health`;`docker_cmd` 回退 sudo;Telegram 文案改 heredoc +- `deploy/health_monitor.sh`:状态迁至 `${DEPLOY_DIR}/var/layer3-health`;不可写时回退 `~/.nexus/layer3-health` 或 sudo chown;`docker_cmd` 回退 sudo;Telegram 文案改 heredoc - `frontend/src/utils/echartsColor.ts` + `WatchSparkline.vue`:`colorWithAlpha` - `WatchProbeRecordsTable.vue`:`total>0` 才显示分页 - `WatchSlotCard.vue` / `WatchMetricRing.vue`:监测槽 UI(菜单、暂停态,同批) diff --git a/tests/test_ops_patrol_sync.py b/tests/test_ops_patrol_sync.py index ac8be2ff..4aef5c77 100644 --- a/tests/test_ops_patrol_sync.py +++ b/tests/test_ops_patrol_sync.py @@ -18,8 +18,8 @@ def test_health_monitor_shell_syntax(): def test_health_monitor_uses_deploy_var_state_dir(): text = HEALTH_MONITOR.read_text(encoding="utf-8") - assert 'STATE_DIR="${DEPLOY_DIR}/var/layer3-health"' in text - assert 'FAIL_FILE="${STATE_DIR}/fail_count"' in text + assert "var/layer3-health" in text + assert "ensure_state_paths" in text def test_escape_env_value_quotes_special_chars():