Железный Егорин — форк Орбиты: тёплый стиль (amber), логин с odyssey WebGL-молнией справа, энергомониторинг+панель
Ребренд Мониторинг Орбита -> Железный Егорин; accent green->amber #F59E0B; LoginScreen правая половина = Lightning (WebGL, hue 35 тёплый) под стеклянной формой; левая панель — тёплый градиент. Доступ агентам: репо публичный, push german-токеном. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
# ingress-watchdog.sh — алерт обрыва связи ингриса с MQTT-брокером (Milestone B)
|
||||
# stdlib/bash only. Аддитивно. НЕ трогает приёмники/буферизацию/маршруты.
|
||||
#
|
||||
# Проба: TCP-connect к брокеру vm-mts1 10.50.0.48:1883 (таймаут 5с).
|
||||
# Дебаунс: DOWN только после непрерывной недоступности > DOWN_THRESHOLD (60с).
|
||||
# Алерт ТОЛЬКО на переходе состояния (up->down = ОБРЫВ, down->up = ВОССТАНОВЛЕНО).
|
||||
# Каналы: всегда лог + статус-файл; Telegram — если в .env есть TG_TOKEN и TG_CHAT_ID.
|
||||
|
||||
set -u
|
||||
|
||||
# --- Конфиг (можно переопределить через .env) ---
|
||||
BROKER_HOST="10.50.0.48"
|
||||
BROKER_PORT="1883"
|
||||
DOWN_THRESHOLD=60 # сек непрерывной недоступности до алерта ОБРЫВ
|
||||
PROBE_TIMEOUT=5 # сек на TCP-пробу
|
||||
STATE_FILE="/var/run/ingress-watchdog.state"
|
||||
STATUS_FILE="/run/ingress-watchdog.status"
|
||||
LOG_FILE="/var/log/ingress-watchdog.log"
|
||||
ENV_FILE="/opt/ingress-watchdog/.env"
|
||||
NODE_NAME="$(hostname)"
|
||||
SPOOL_FILE="" # путь к queue.jsonl (задаётся в .env на каждой ноде)
|
||||
ALERT_WEBHOOK="https://kpgen.servaki.online/webhook/alert" # единый n8n-хаб (можно переопределить в .env)
|
||||
|
||||
# --- Загрузка .env (TG_TOKEN, TG_CHAT_ID, SPOOL_FILE, NODE_NAME override) ---
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$ENV_FILE"
|
||||
fi
|
||||
|
||||
now="$(date +%s)"
|
||||
ts="$(date '+%Y-%m-%d %H:%M:%S %Z')"
|
||||
|
||||
log() {
|
||||
# НЕ логируем токен. Пишем строку в лог.
|
||||
printf '%s [%s] %s\n' "$ts" "$NODE_NAME" "$1" >> "$LOG_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
spool_count() {
|
||||
if [ -n "${SPOOL_FILE:-}" ] && [ -f "$SPOOL_FILE" ]; then
|
||||
wc -l < "$SPOOL_FILE" 2>/dev/null | tr -d ' '
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
send_telegram() {
|
||||
# $1 = текст. Отправляем только если заданы token+chat_id.
|
||||
local text="$1"
|
||||
if [ -n "${TG_TOKEN:-}" ] && [ -n "${TG_CHAT_ID:-}" ]; then
|
||||
timeout 10 curl -s -o /dev/null \
|
||||
"https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
--data-urlencode "chat_id=${TG_CHAT_ID}" \
|
||||
--data-urlencode "text=${text}" >/dev/null 2>&1 \
|
||||
&& log "telegram: sent" \
|
||||
|| log "telegram: send FAILED"
|
||||
else
|
||||
log "telegram: skipped (no token/chat_id)"
|
||||
fi
|
||||
}
|
||||
|
||||
send_webhook() {
|
||||
# $1=severity (critical|warning|info), $2=title, $3=text. Единый хаб алертов (n8n).
|
||||
local sev="$1" title="$2" text="$3" iso body
|
||||
[ -z "${ALERT_WEBHOOK:-}" ] && { log "webhook: skipped (no ALERT_WEBHOOK)"; return; }
|
||||
iso="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
body="$(printf '{"source":"orbita-ingress","node":"%s","severity":"%s","title":"%s","text":"%s","ts":"%s"}' \
|
||||
"$NODE_NAME" "$sev" "$title" "$text" "$iso")"
|
||||
local code
|
||||
code="$(timeout 12 curl -s -o /dev/null -m 10 --retry 2 -w '%{http_code}' \
|
||||
-X POST "$ALERT_WEBHOOK" -H 'Content-Type: application/json' -d "$body" 2>/dev/null)"
|
||||
if [ "$code" = "200" ]; then log "webhook: sent ($sev) HTTP 200"; else log "webhook: send FAILED (HTTP ${code:-000})"; fi
|
||||
}
|
||||
|
||||
write_status() {
|
||||
# $1=state up|down, $2=human message
|
||||
{
|
||||
printf 'node=%s\n' "$NODE_NAME"
|
||||
printf 'state=%s\n' "$1"
|
||||
printf 'updated=%s\n' "$ts"
|
||||
printf 'updated_epoch=%s\n' "$now"
|
||||
printf 'broker=%s:%s\n' "$BROKER_HOST" "$BROKER_PORT"
|
||||
printf 'spool_lines=%s\n' "$(spool_count)"
|
||||
printf 'last_message=%s\n' "$2"
|
||||
} > "$STATUS_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
# --- Читаем предыдущий стейт ---
|
||||
last_ok=0
|
||||
current_state="up"
|
||||
down_since=0
|
||||
if [ -f "$STATE_FILE" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$STATE_FILE" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# --- TCP-проба ---
|
||||
if timeout "$PROBE_TIMEOUT" bash -c "cat < /dev/null > /dev/tcp/${BROKER_HOST}/${BROKER_PORT}" 2>/dev/null; then
|
||||
probe_ok=1
|
||||
else
|
||||
probe_ok=0
|
||||
fi
|
||||
|
||||
new_state="$current_state"
|
||||
|
||||
if [ "$probe_ok" -eq 1 ]; then
|
||||
last_ok="$now"
|
||||
if [ "$current_state" = "down" ]; then
|
||||
# ВОССТАНОВЛЕНО (переход down->up)
|
||||
new_state="up"
|
||||
down_since=0
|
||||
spc="$(spool_count)"
|
||||
msg="✅ ВОССТАНОВЛЕНО: связь ингриса ${NODE_NAME} с брокером ${BROKER_HOST}:${BROKER_PORT} восстановлена. В спуле сейчас: ${spc} сообщ. (дренаж до-дошлёт). Время: ${ts}"
|
||||
log "STATE up (recovered). spool=${spc}"
|
||||
write_status "up" "recovered"
|
||||
send_webhook "info" "Связь восстановлена ${NODE_NAME}" "брокер ${BROKER_HOST}:${BROKER_PORT} снова доступен; в буфере ${spc} сообщ. (дренаж до-дошлёт)"
|
||||
send_telegram "$msg"
|
||||
else
|
||||
# остаёмся up, без алерта
|
||||
write_status "up" "ok"
|
||||
fi
|
||||
else
|
||||
# недоступно
|
||||
if [ "$down_since" -eq 0 ] 2>/dev/null || [ -z "${down_since:-}" ]; then
|
||||
down_since="$now"
|
||||
fi
|
||||
down_for=$(( now - down_since ))
|
||||
if [ "$current_state" = "up" ] && [ "$down_for" -gt "$DOWN_THRESHOLD" ]; then
|
||||
# ОБРЫВ (переход up->down после дебаунса)
|
||||
new_state="down"
|
||||
spc="$(spool_count)"
|
||||
msg="🔴 ОБРЫВ: ингрис ${NODE_NAME} потерял связь с брокером ${BROKER_HOST}:${BROKER_PORT} (недоступен ${down_for}с). В спуле накоплено: ${spc} сообщ. и буферизуется. Время: ${ts}"
|
||||
log "STATE down (link lost ${down_for}s). spool=${spc}"
|
||||
write_status "down" "link_lost"
|
||||
send_webhook "critical" "Обрыв связи ${NODE_NAME}" "брокер ${BROKER_HOST}:${BROKER_PORT} недоступен ${down_for}с; в буфере ${spc} сообщ., буферизуется"
|
||||
send_telegram "$msg"
|
||||
elif [ "$current_state" = "down" ]; then
|
||||
# уже в down — не спамим
|
||||
write_status "down" "still_down"
|
||||
else
|
||||
# недоступно, но порог ещё не перевален — ждём
|
||||
write_status "up" "probing_down ${down_for}s"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Сохраняем стейт ---
|
||||
{
|
||||
printf 'last_ok=%s\n' "$last_ok"
|
||||
printf 'current_state=%s\n' "$new_state"
|
||||
printf 'down_since=%s\n' "$down_since"
|
||||
} > "$STATE_FILE" 2>/dev/null
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user