fix: stats crash (set -u + CSV header), header box, QR spacing

This commit is contained in:
anten-ka
2026-04-08 22:21:17 +03:00
parent 0dae922d1b
commit 52912e0ead
2 changed files with 55 additions and 72 deletions

View File

@@ -38,11 +38,11 @@ show_main_menu() {
local line; line=$(printf '━%.0s' $(seq 1 $W))
local line2; line2=$(printf '─%.0s' $(seq 1 $W))
# ── Заголовок ──
# ── Заголовок (без правого бордера — ANSI ломает выравнивание) ──
echo ""
echo -e " ${BOLD}${CYAN}${line}${NC}"
echo -e " ${BOLD}${CYAN}${NC} ${BOLD}${WHITE}GoTelegram v${GOTELEGRAM_VERSION}${NC} Панель управления ${BOLD}${CYAN}${NC}"
echo -e " ${BOLD}${CYAN}${line}${NC}"
echo -e " ${BOLD}${CYAN}${line}${NC}"
echo -e " ${BOLD}${WHITE} GoTelegram v${GOTELEGRAM_VERSION}${NC} ${DIM} Панель управления${NC}"
echo -e " ${BOLD}${CYAN}${line}${NC}"
# ── Здоровье сервисов ──
echo ""
@@ -109,9 +109,10 @@ show_main_menu() {
echo -e " ${BOLD}${WHITE}Ссылка для Telegram:${NC}"
echo -e " ${GREEN}${link}${NC}"
echo ""
if command -v qrencode &>/dev/null; then
echo ""
echo ""
qrencode -t UTF8 -m 1 "$link" 2>/dev/null | while IFS= read -r qr_line; do
echo " ${qr_line}"
done

View File

@@ -85,16 +85,19 @@ EOF
fi
# Save snapshot for rate calculation (one per minute)
local minute_key=$(date +%s -d "1 minute ago" +%Y%m%d%H%M 2>/dev/null || date +%Y%m%d%H%M)
local minute_key
minute_key=$(date +%Y%m%d%H%M 2>/dev/null)
local snapshot_file="$SNAPSHOTS_DIR/snap_${minute_key}.json"
cp "$CURRENT_SNAPSHOT" "$snapshot_file" 2>/dev/null
# Append to history CSV (once per minute, check if last entry is fresh)
if [[ -f "$HISTORY_FILE" ]]; then
local last_ts=$(tail -1 "$HISTORY_FILE" 2>/dev/null | cut -d, -f1)
local last_ts
last_ts=$(grep -E '^[0-9]' "$HISTORY_FILE" 2>/dev/null | tail -1 | cut -d, -f1)
last_ts="${last_ts:-0}"
local current_minute=$((ts - (ts % 60)))
if [[ -z "$last_ts" ]] || [[ $((current_minute - last_ts)) -ge 60 ]]; then
if [[ "$last_ts" -eq 0 ]] || [[ $((current_minute - last_ts)) -ge 60 ]]; then
echo "$current_minute,$proxy_bytes,$site_bytes" >> "$HISTORY_FILE" 2>/dev/null
# Cleanup old entries (keep only 365 days)
@@ -158,82 +161,61 @@ format_rate() {
fi
}
# Safely convert value to integer (returns 0 for empty/non-numeric)
_to_int() {
local val="${1:-0}"
# Strip non-numeric chars, default to 0
val="${val//[^0-9]/}"
echo "${val:-0}"
}
# Calculate diff safely (never negative, never crashes on empty)
_safe_diff() {
local a=$(_to_int "$1")
local b=$(_to_int "$2")
local d=$((a - b))
(( d < 0 )) && d=0
echo "$d"
}
# Calculate traffic rates and totals from history
stats_calculate_rates() {
local traffic_type="$1" # "proxy" or "site"
local col_idx=2 # proxy_bytes is column 2
[[ "$traffic_type" == "site" ]] && col_idx=3
[[ "$traffic_type" == "site" ]] && col_idx=3 # site_bytes is column 3
local now
now=$(date +%s)
local now=$(date +%s)
local result=""
# Get latest data line (skip header with grep -E '^[0-9]')
local bytes_now
bytes_now=$(_to_int "$(grep -E '^[0-9]' "$HISTORY_FILE" 2>/dev/null | tail -1 | cut -d, -f"$col_idx")")
# 1 minute rate
local ts_1m=$((now - 60))
local bytes_now=$(tail -1 "$HISTORY_FILE" 2>/dev/null | cut -d, -f"$col_idx")
local bytes_1m=$(awk -F, -v ts="$ts_1m" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_1m=$((bytes_now - (bytes_1m > 0 ? bytes_1m : bytes_now)))
[[ $diff_1m -lt 0 ]] && diff_1m=0
local rate_1m=$((diff_1m / 60))
local bytes_1m_fmt=$(format_bytes "$diff_1m")
local rate_1m_fmt=$(format_rate "$rate_1m")
local periods="60 300 3600 86400 604800 2592000 31536000"
local results=""
# 5 minute rate
local ts_5m=$((now - 300))
local bytes_5m=$(awk -F, -v ts="$ts_5m" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_5m=$((bytes_now - (bytes_5m > 0 ? bytes_5m : bytes_now)))
[[ $diff_5m -lt 0 ]] && diff_5m=0
local rate_5m=$((diff_5m / 300))
local bytes_5m_fmt=$(format_bytes "$diff_5m")
local rate_5m_fmt=$(format_rate "$rate_5m")
for secs in $periods; do
local target_ts=$((now - secs))
# Find closest entry at or after target timestamp (skip header)
local old_val
old_val=$(_to_int "$(awk -F, -v ts="$target_ts" '$1 ~ /^[0-9]/ && $1 <= ts' "$HISTORY_FILE" 2>/dev/null | tail -1 | cut -d, -f"$col_idx")")
# 60 minute rate
local ts_60m=$((now - 3600))
local bytes_60m=$(awk -F, -v ts="$ts_60m" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_60m=$((bytes_now - (bytes_60m > 0 ? bytes_60m : bytes_now)))
[[ $diff_60m -lt 0 ]] && diff_60m=0
local rate_60m=$((diff_60m / 3600))
local bytes_60m_fmt=$(format_bytes "$diff_60m")
local rate_60m_fmt=$(format_rate "$rate_60m")
local diff
diff=$(_safe_diff "$bytes_now" "$old_val")
local rate=$(( secs > 0 ? diff / secs : 0 ))
# 1 day total
local ts_1d=$((now - 86400))
local bytes_1d=$(awk -F, -v ts="$ts_1d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_1d=$((bytes_now - (bytes_1d > 0 ? bytes_1d : bytes_now)))
[[ $diff_1d -lt 0 ]] && diff_1d=0
local rate_1d=$((diff_1d > 0 ? diff_1d / 86400 : 0))
local bytes_1d_fmt=$(format_bytes "$diff_1d")
local rate_1d_fmt=$(format_rate "$rate_1d")
local bytes_fmt rate_fmt
bytes_fmt=$(format_bytes "$diff")
rate_fmt=$(format_rate "$rate")
# 7 days total
local ts_7d=$((now - 604800))
local bytes_7d=$(awk -F, -v ts="$ts_7d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_7d=$((bytes_now - (bytes_7d > 0 ? bytes_7d : bytes_now)))
[[ $diff_7d -lt 0 ]] && diff_7d=0
local rate_7d=$((diff_7d > 0 ? diff_7d / 604800 : 0))
local bytes_7d_fmt=$(format_bytes "$diff_7d")
local rate_7d_fmt=$(format_rate "$rate_7d")
if [ -z "$results" ]; then
results="${bytes_fmt}|${rate_fmt}"
else
results="${results}|${bytes_fmt}|${rate_fmt}"
fi
done
# 30 days total
local ts_30d=$((now - 2592000))
local bytes_30d=$(awk -F, -v ts="$ts_30d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_30d=$((bytes_now - (bytes_30d > 0 ? bytes_30d : bytes_now)))
[[ $diff_30d -lt 0 ]] && diff_30d=0
local rate_30d=$((diff_30d > 0 ? diff_30d / 2592000 : 0))
local bytes_30d_fmt=$(format_bytes "$diff_30d")
local rate_30d_fmt=$(format_rate "$rate_30d")
# 365 days total
local ts_365d=$((now - 31536000))
local bytes_365d=$(awk -F, -v ts="$ts_365d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
local diff_365d=$((bytes_now - (bytes_365d > 0 ? bytes_365d : bytes_now)))
[[ $diff_365d -lt 0 ]] && diff_365d=0
local rate_365d=$((diff_365d > 0 ? diff_365d / 31536000 : 0))
local bytes_365d_fmt=$(format_bytes "$diff_365d")
local rate_365d_fmt=$(format_rate "$rate_365d")
# Return as pipe-delimited format for table display
echo "$bytes_1m_fmt|$rate_1m_fmt|$bytes_5m_fmt|$rate_5m_fmt|$bytes_60m_fmt|$rate_60m_fmt|$bytes_1d_fmt|$rate_1d_fmt|$bytes_7d_fmt|$rate_7d_fmt|$bytes_30d_fmt|$rate_30d_fmt|$bytes_365d_fmt|$rate_365d_fmt"
echo "$results"
}
# Main display function for traffic statistics