mirror of
https://github.com/samjage/lab.git
synced 2026-06-06 01:20:43 +00:00
151 lines
8.1 KiB
Bash
151 lines
8.1 KiB
Bash
#!/bin/bash
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
PURPLE='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
GRAY='\033[0;90m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
COLS=${COLUMNS:-$(tput cols 2>/dev/null)}
|
|
[[ "$COLS" =~ ^[0-9]+$ ]] || COLS=80
|
|
SEP="${GRAY}$(printf '━%.0s' $(seq 1 $COLS))${NC}"
|
|
|
|
# ── Logo ─────────────────────────────────────────────────────────────────────
|
|
echo -e "${CYAN}${BOLD}"
|
|
echo '██ ██ ███ ██ ██ ██'
|
|
echo '██ ██ ████ ██ ██ ██ '
|
|
echo '██ ██ ██ ██ ██ █████ '
|
|
echo '██ ██ ██ ██ ██ ██ ██ '
|
|
echo '███████ ██ ██ ████ ██ ██'
|
|
echo -e "${NC}"
|
|
echo -e "$SEP"
|
|
|
|
# ── Date & Time ───────────────────────────────────────────────────────────────
|
|
echo -e " ${PURPLE}${BOLD}$(date '+%A, %B %d %Y')${NC} ${GRAY}·${NC} ${PURPLE}$(date '+%I:%M %p')${NC}"
|
|
echo -e "$SEP"
|
|
|
|
# ── Weather ───────────────────────────────────────────────────────────────────
|
|
WEATHER=$(curl -s --max-time 4 "https://api.open-meteo.com/v1/forecast?latitude=39.84&longitude=-82.81¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code&temperature_unit=fahrenheit&timezone=America/New_York" 2>/dev/null)
|
|
echo -e " ${YELLOW}${BOLD}☁ Canal Winchester${NC}"
|
|
if echo "$WEATHER" | python3 -c "import sys,json; json.load(sys.stdin)" &>/dev/null; then
|
|
TEMP=$(echo "$WEATHER" | python3 -c "import sys,json; d=json.load(sys.stdin)['current']; print(round(d['temperature_2m']))")
|
|
FEELS=$(echo "$WEATHER" | python3 -c "import sys,json; d=json.load(sys.stdin)['current']; print(round(d['apparent_temperature']))")
|
|
HUMID=$(echo "$WEATHER" | python3 -c "import sys,json; d=json.load(sys.stdin)['current']; print(round(d['relative_humidity_2m']))")
|
|
WIND=$(echo "$WEATHER" | python3 -c "import sys,json; d=json.load(sys.stdin)['current']; print(round(d['wind_speed_10m']))")
|
|
CODE=$(echo "$WEATHER" | python3 -c "import sys,json; d=json.load(sys.stdin)['current']; print(d['weather_code'])")
|
|
case $CODE in
|
|
0) ICON="☀️ Clear";;
|
|
1|2) ICON="⛅ Partly Cloudy";;
|
|
3) ICON="☁️ Overcast";;
|
|
45|48) ICON="🌫️ Fog";;
|
|
51|53|55|61|63|65|80|81|82) ICON="🌧️ Rain";;
|
|
71|73|75|85|86) ICON="❄️ Snow";;
|
|
95|96|99) ICON="⛈️ Storms";;
|
|
*) ICON="🌥️ Cloudy";;
|
|
esac
|
|
echo -e " ${ICON} ${BOLD}${TEMP}°F${NC} ${GRAY}feels ${FEELS}°F · 💧${HUMID}% · 💨 ${WIND} km/h${NC}"
|
|
else
|
|
echo -e " ${GRAY}weather unavailable${NC}"
|
|
fi
|
|
echo -e "$SEP"
|
|
|
|
# ── Docker ────────────────────────────────────────────────────────────────────
|
|
echo -e " ${GREEN}${BOLD}🐳 Docker${NC}"
|
|
if command -v docker &> /dev/null; then
|
|
RUNNING=$(docker ps -q 2>/dev/null | wc -l)
|
|
TOTAL=$(docker ps -a -q 2>/dev/null | wc -l)
|
|
DOWN=$((TOTAL - RUNNING))
|
|
RESTART=$(docker ps --filter "status=restarting" -q 2>/dev/null | wc -l)
|
|
echo ""
|
|
echo -e " ${GREEN}●${NC} ${RUNNING} running ${GRAY}·${NC} ${RED}●${NC} ${DOWN} stopped ${GRAY}·${NC} 🔄 ${RESTART} restarting ${GRAY}·${NC} 📦 ${TOTAL} total"
|
|
echo ""
|
|
|
|
# Outdated images — parsed live from Watchtower logs
|
|
echo -e " ${CYAN}image updates${NC}"
|
|
if docker inspect watchtower &>/dev/null 2>&1; then
|
|
LAST_SCAN=$(docker logs watchtower --since 72h 2>&1 | grep "Session done" | tail -1 | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}' || true)
|
|
OUTDATED_LIST=$(docker logs watchtower --since 36h 2>&1 | grep "Found new" | grep -oP '(?<=Found new )\S+(?= image)' || true)
|
|
if [ -n "$OUTDATED_LIST" ]; then
|
|
COUNT=$(echo "$OUTDATED_LIST" | wc -l)
|
|
echo -e " ${YELLOW}↑ ${COUNT} update(s) available${NC}"
|
|
while IFS= read -r img; do
|
|
CT=$(docker ps -a --format '{{.Names}}|{{.Image}}' | awk -F'|' -v i="$img" '$2==i {printf "%s ", $1}')
|
|
echo -e " ${GRAY} · ${img}${NC} ${CT}"
|
|
done <<< "$OUTDATED_LIST"
|
|
else
|
|
echo -e " ${GREEN}●${NC} all images current"
|
|
fi
|
|
[ -n "$LAST_SCAN" ] && echo -e " ${GRAY}last scan: ${LAST_SCAN}${NC}"
|
|
echo -e " ${GRAY}details: bash /opt/stacks/scripts/update-check.sh${NC}"
|
|
else
|
|
echo -e " ${GRAY}watchtower not running${NC}"
|
|
fi
|
|
else
|
|
echo -e " ${RED}docker not available${NC}"
|
|
fi
|
|
echo -e "$SEP"
|
|
|
|
# ── System ────────────────────────────────────────────────────────────────────
|
|
echo -e " ${BLUE}${BOLD}💻 System${NC}"
|
|
echo ""
|
|
|
|
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
|
EXT_IP=$(curl -s --max-time 3 ifconfig.me 2>/dev/null || echo "unavailable")
|
|
|
|
CPU_CORES=$(nproc 2>/dev/null || echo 1)
|
|
CPU_PCT=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{printf "%.0f", 100 - $1}')
|
|
CPU_FILLED=$((CPU_PCT * 10 / 100))
|
|
CPU_EMPTY=$((10 - CPU_FILLED))
|
|
CPU_ON="$(printf '█%.0s' $(seq 1 $CPU_FILLED) 2>/dev/null)"
|
|
CPU_OFF="$(printf '░%.0s' $(seq 1 $CPU_EMPTY) 2>/dev/null)"
|
|
|
|
RAM_USED=$(free -m | awk 'NR==2 {printf "%.1f", $3/1024}')
|
|
RAM_TOTAL=$(free -m | awk 'NR==2 {printf "%.1f", $2/1024}')
|
|
RAM_PCT=$(free | awk 'NR==2 {printf "%.0f", $3/$2*100}')
|
|
RAM_FILLED=$((RAM_PCT * 10 / 100))
|
|
RAM_EMPTY=$((10 - RAM_FILLED))
|
|
BAR_ON="$(printf '█%.0s' $(seq 1 $RAM_FILLED) 2>/dev/null)"
|
|
BAR_OFF="$(printf '░%.0s' $(seq 1 $RAM_EMPTY) 2>/dev/null)"
|
|
|
|
LOAD=$(awk '{print $1" "$2" "$3}' /proc/loadavg)
|
|
LOAD1=$(awk '{print $1}' /proc/loadavg)
|
|
LOAD_PCT=$(awk -v l="$LOAD1" -v c="$CPU_CORES" 'BEGIN {v=int((l/c)*100); if(v>100) v=100; print v}')
|
|
LOAD_FILLED=$((LOAD_PCT * 10 / 100))
|
|
LOAD_EMPTY=$((10 - LOAD_FILLED))
|
|
LOAD_ON="$(printf '█%.0s' $(seq 1 $LOAD_FILLED) 2>/dev/null)"
|
|
LOAD_OFF="$(printf '░%.0s' $(seq 1 $LOAD_EMPTY) 2>/dev/null)"
|
|
|
|
TOP_NAME=$(ps aux --sort=-%cpu | awk 'NR==2 {split($11,a,"/"); print a[length(a)]}')
|
|
TOP_CPU=$(ps aux --sort=-%cpu | awk 'NR==2 {printf "%.1f%%", $3}')
|
|
|
|
printf " ${GRAY}%-10s${NC} %s\n" "uptime" "$(uptime -p | sed 's/up //')"
|
|
printf " ${GRAY}%-10s${NC} %s\n" "local ip" "$LOCAL_IP"
|
|
printf " ${GRAY}%-10s${NC} %s\n" "ext ip" "$EXT_IP"
|
|
echo ""
|
|
printf " ${GRAY}%-10s${NC} ${GREEN}${CPU_ON}${GRAY}${CPU_OFF}${NC} ${CPU_PCT}%%\n" "cpu"
|
|
printf " ${GRAY}%-10s${NC} ${GREEN}${BAR_ON}${GRAY}${BAR_OFF}${NC} ${RAM_USED}/${RAM_TOTAL} GB ${RAM_PCT}%%\n" "ram"
|
|
printf " ${GRAY}%-10s${NC} ${GREEN}${LOAD_ON}${GRAY}${LOAD_OFF}${NC} ${LOAD} ${GRAY}/ ${CPU_CORES} cores${NC}\n" "load"
|
|
echo ""
|
|
printf " ${GRAY}%-10s${NC} ${YELLOW}%s${NC} ${GRAY}%s${NC}\n" "top proc" "$TOP_NAME" "$TOP_CPU"
|
|
printf " ${GRAY}%-10s${NC} %s\n" "users" "$(who | wc -l) logged in"
|
|
echo -e "$SEP"
|
|
|
|
# ── Quote ─────────────────────────────────────────────────────────────────────
|
|
echo -e " ${PURPLE}${BOLD}✦ Quote${NC}"
|
|
echo ""
|
|
QDATA=$(curl -s --max-time 4 "https://zenquotes.io/api/today" 2>/dev/null)
|
|
if echo "$QDATA" | python3 -c "import sys,json; json.load(sys.stdin)" &>/dev/null; then
|
|
QUOTE=$(echo "$QDATA" | python3 -c "import sys,json; d=json.load(sys.stdin)[0]; print(d['q'])")
|
|
AUTHOR=$(echo "$QDATA" | python3 -c "import sys,json; d=json.load(sys.stdin)[0]; print(d['a'])")
|
|
echo -e " ${GRAY}\"${QUOTE}\"${NC}" | fold -s -w $((COLS - 4))
|
|
echo -e " ${PURPLE}— ${AUTHOR}${NC}"
|
|
else
|
|
echo -e " ${GRAY}unavailable${NC}"
|
|
fi
|
|
echo -e "$SEP"
|
|
echo ""
|