diff --git a/update-Nitrov3.sh b/update-Nitrov3.sh index a27316d0..a15e5b2b 100755 --- a/update-Nitrov3.sh +++ b/update-Nitrov3.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -o pipefail +set -Eeo pipefail # ╔══════════════════════════════════════════════════════════════════════════════╗ # ║ ║ @@ -156,6 +156,16 @@ cursor_show() { tput cnorm 2>/dev/null || true; } clear_line() { printf "\r\033[K"; } repeat() { local ch="${1:-=}" n="${2:-1}"; printf '%*s' "$n" '' | tr ' ' "$ch"; } list_sorted() { local d="$1" p="$2" m="${3:-20}"; find "$d" -name "$p" -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -n "$m" | sed 's/^[0-9.]* //'; } +service_active() { + local svc="$1" + if command -v systemctl &>/dev/null; then + systemctl is-active "$svc" &>/dev/null + elif command -v service &>/dev/null; then + service "$svc" status &>/dev/null + else + pgrep -x "$svc" &>/dev/null + fi +} format_size() { local bytes="${1:-0}" if command -v numfmt &>/dev/null; then @@ -202,7 +212,7 @@ confirm() { local msg="${1:-Continue?}" default="${2:-y}" local hint="[Y/n]"; [ "$default" = "n" ] && hint="[y/N]" echo -en " ${C_YELLOW}${E_ARROW}${C_RESET} ${C_BOLD}$msg${C_RESET} $hint " - local reply; read -r reply; reply="${reply:-$default}" + local reply; read -rt 30 reply; reply="${reply:-$default}" [[ "$reply" =~ ^[Yy] ]] } @@ -491,6 +501,16 @@ do_restart() { step 8 8 "Restart Services" if systemctl cat "$EMULATOR_SERVICE" &>/dev/null; then sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted" || warn "Restart failed" + local hc=0 + while [ "$hc" -lt "$HEALTH_RETRIES" ]; do + sleep "$HEALTH_INTERVAL" + if service_active "$EMULATOR_SERVICE"; then + ok "Emulator health check passed" + break + fi + hc=$((hc+1)) + done + [ "$hc" -eq "$HEALTH_RETRIES" ] && warn "Emulator not running after $HEALTH_RETRIES retries" elif command -v pm2 &>/dev/null; then pm2 restart all 2>/dev/null && ok "PM2 restarted" || warn "PM2 restart failed" else @@ -562,8 +582,9 @@ cmd_update() { configs) sync_configs ;; *) update_emulator - update_renderer - update_client + update_renderer & + update_client & + wait sync_configs do_cleanup do_permissions @@ -605,7 +626,7 @@ cmd_restore() { i=$((i+1)) done echo -en "\n Select [1-$((i-1))]: " - local c; read -r c + local c; read -rt 30 c [[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -ge 1 ] && [ "$c" -le ${#backups[@]} ] || die "Invalid" local sel="${backups[$((c-1))]}" if ! confirm "Restore $(basename "$sel")? OVERWRITES database!"; then info "Cancelled"; return; fi @@ -649,7 +670,7 @@ cmd_status() { box_top "SERVICES" 56 for svc in nginx mariadb redis-server "$EMULATOR_SERVICE" php-fpm cron; do local st="${C_DIM}N/A${C_RESET}" - systemctl is-active "$svc" &>/dev/null && st="${C_GREEN}ACTIVE${C_RESET}" + service_active "$svc" && st="${C_GREEN}ACTIVE${C_RESET}" printf " ${C_CYAN}║${C_RESET} %-20s %b${C_CYAN} ║${C_RESET}\n" "$svc" "$st" done; box_bottom 56 echo "" @@ -679,7 +700,7 @@ cmd_health() { echo -e "\n ${C_BOLD}Directories:${C_RESET}" checkw "Emulator" test -d "$EMULATOR_DIR/Emulator"; checkw "Nitro-V3" test -d "$NITRO_CLIENT"; checkw "Renderer" test -d "$NITRO_RENDERER" echo -e "\n ${C_BOLD}Services:${C_RESET}" - checkw "MariaDB" systemctl is-active mariadb; checkw "Nginx" systemctl is-active nginx; checkw "Redis" systemctl is-active redis-server + checkw "MariaDB" service_active mariadb; checkw "Nginx" service_active nginx; checkw "Redis" service_active redis-server echo -e "\n ${C_BOLD}Application:${C_RESET}" checkw "Next.js" test -f "$SCRIPT_DIR/next.config.js" -o -f "$SCRIPT_DIR/next.config.mjs" -o -f "$SCRIPT_DIR/next.config.ts"; checkw ".env" test -f "$SCRIPT_DIR/.env" checkw "Site" timeout 5 curl -sf "${NITRO_SITE_URL:-}" 2>/dev/null @@ -706,7 +727,7 @@ cmd_services() { echo -e " ${C_BOLD}1)${C_RESET} Start Emulator ${C_BOLD}2)${C_RESET} Stop Emulator ${C_BOLD}3)${C_RESET} Restart Emulator" echo -e " ${C_BOLD}4)${C_RESET} Start Nginx ${C_BOLD}5)${C_RESET} Restart Nginx ${C_BOLD}6)${C_RESET} Status All" echo -e " ${C_BOLD}7)${C_RESET} Emulator Logs ${C_BOLD}0)${C_RESET} Return" - echo -en "\n Select: "; local c; read -r c + echo -en "\n Select: "; local c; read -rt 30 c case "$c" in 1) sudo systemctl start "$EMULATOR_SERVICE" && ok "Started" || fail "Failed" ;; 2) sudo systemctl stop "$EMULATOR_SERVICE" && ok "Stopped" || fail "Failed" ;; @@ -714,7 +735,7 @@ cmd_services() { 4) sudo systemctl start nginx && ok "Started" || fail "Failed" ;; 5) sudo systemctl restart nginx && ok "Restarted" || fail "Failed" ;; 6) for s in nginx mariadb redis-server "$EMULATOR_SERVICE" php-fpm cron; do - local st="${C_RED}DOWN${C_RESET}"; systemctl is-active "$s" &>/dev/null && st="${C_GREEN}UP${C_RESET}" + local st="${C_RED}DOWN${C_RESET}"; service_active "$s" && st="${C_GREEN}UP${C_RESET}" printf " %-25s %b\n" "$s" "$st" done ;; 7) sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 30 ;; @@ -730,7 +751,7 @@ cmd_database() { echo -e " ${C_BOLD}1)${C_RESET} DB Info ${C_BOLD}2)${C_RESET} List Tables ${C_BOLD}3)${C_RESET} Table Sizes" echo -e " ${C_BOLD}4)${C_RESET} Optimize ${C_BOLD}5)${C_RESET} Custom Query ${C_BOLD}6)${C_RESET} Active Users" echo -e " ${C_BOLD}0)${C_RESET} Return" - echo -en "\n Select: "; local c; read -r c + echo -en "\n Select: "; local c; read -rt 30 c case "$c" in 1) box_top "DB INFO" 50 local dbs=$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT ROUND(SUM(data_length+index_length)/1024/1024,1) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo "?") @@ -743,7 +764,7 @@ cmd_database() { for tbl in $($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT table_name FROM information_schema.tables WHERE table_schema='$DB_NAME' AND engine='InnoDB'" "$DB_NAME" 2>/dev/null); do $MYSQL_CLIENT $MYSQL_CRED -e "OPTIMIZE TABLE $DB_NAME.\`$tbl\`" "$DB_NAME" &>/dev/null || true done; spinner_stop ok; ok "Optimized" ;; - 5) echo -en " SQL: "; local q; read -r q; [ -n "$q" ] && $MYSQL_CLIENT $MYSQL_CRED "$DB_NAME" -e "$q" 2>/dev/null ;; + 5) echo -en " SQL: "; local q; read -rt 30 q; [ -n "$q" ] && $MYSQL_CLIENT $MYSQL_CRED "$DB_NAME" -e "$q" 2>/dev/null ;; 6) $MYSQL_CLIENT $MYSQL_CRED -e "SELECT name, motto FROM $DB_NAME.users WHERE online='1'" 2>/dev/null || warn "Could not query" ;; esac } @@ -785,7 +806,7 @@ cmd_logs() { printf " ${C_GREEN}${C_BOLD}%2d)${C_RESET} %-35s ${C_DIM}%s${C_RESET}\n" "$i" "$(basename "$l")" "$sz" i=$((i+1)) done - echo -en "\n Select [1-$((i-1))]: "; local c; read -r c + echo -en "\n Select [1-$((i-1))]: "; local c; read -rt 30 c if [[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -ge 1 ] && [ "$c" -le ${#logs[@]} ]; then less -R "${logs[$((c-1))]}" 2>/dev/null || cat "${logs[$((c-1))]}" fi @@ -799,7 +820,7 @@ cmd_clean() { echo "" echo -e " ${C_BOLD}1)${C_RESET} Yarn ${C_BOLD}2)${C_RESET} Laravel ${C_BOLD}3)${C_RESET} Redis" echo -e " ${C_BOLD}4)${C_RESET} Logs ${C_BOLD}5)${C_RESET} Everything ${C_BOLD}0)${C_RESET} Return" - echo -en "\n Select: "; local c; read -r c + echo -en "\n Select: "; local c; read -rt 30 c case "$c" in 1) yarn cache clean 2>/dev/null && ok "Yarn cleaned" ;; 2) cd "$SCRIPT_DIR" && rm -rf .next 2>/dev/null && ok "Next.js cache cleared" ;; @@ -816,7 +837,7 @@ cmd_gitlog() { step 1 1 "Git Commit History" echo "" echo -e " ${C_BOLD}1)${C_RESET} Emulator ${C_BOLD}2)${C_RESET} Nitro-V3 ${C_BOLD}3)${C_RESET} Renderer" - echo -en "\n Select: "; local c; read -r c + echo -en "\n Select: "; local c; read -rt 30 c local repo="" case "$c" in 1) repo="$EMULATOR_DIR/Emulator" ;; 2) repo="$NITRO_CLIENT" ;; 3) repo="$NITRO_RENDERER" ;; *) return ;; esac [ ! -d "$repo/.git" ] && { warn "Not a git repo"; return; } @@ -839,8 +860,8 @@ cmd_compare() { echo -e " ${C_BOLD}Branches:${C_RESET}" local i=1 for b in "${branches[@]}"; do echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $b"; i=$((i+1)); done - echo -en "\n Branch A: "; local ba; read -r ba - echo -en " Branch B: "; local bb; read -r bb + echo -en "\n Branch A: "; local ba; read -rt 30 ba + echo -en " Branch B: "; local bb; read -rt 30 bb [[ "$ba" =~ ^[0-9]+$ ]] && [[ "$bb" =~ ^[0-9]+$ ]] && [ "$ba" -ge 1 ] && [ "$bb" -ge 1 ] && [ "$ba" -le ${#branches[@]} ] && [ "$bb" -le ${#branches[@]} ] || { warn "Invalid"; return; } local bA="${branches[$((ba-1))]}" bB="${branches[$((bb-1))]}" echo "" @@ -867,13 +888,13 @@ cmd_schedule() { echo "" echo -e " ${C_BOLD}1)${C_RESET} Daily 03:00 ${C_BOLD}2)${C_RESET} Daily 04:00 ${C_BOLD}3)${C_RESET} Weekly Sun 03:00" echo -e " ${C_BOLD}4)${C_RESET} Custom time ${C_BOLD}5)${C_RESET} Remove ${C_BOLD}0)${C_RESET} Return" - echo -en "\n Select: "; local c; read -r c + echo -en "\n Select: "; local c; read -rt 30 c case "$c" in 1) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 3 * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled daily 03:00" ;; 2) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 4 * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled daily 04:00" ;; 3) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 3 * * 0 cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled weekly" ;; - 4) echo -en " Minute [0-59]: "; local m; read -r m - echo -en " Hour [0-23]: "; local h; read -r h + 4) echo -en " Minute [0-59]: "; local m; read -rt 30 m + echo -en " Hour [0-23]: "; local h; read -rt 30 h (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "$m $h * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled $m $h * * *" ;; 5) crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME" | crontab - && ok "Removed" ;; esac @@ -901,11 +922,11 @@ pick_branch_and_update() { done echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} Custom" echo -en "\n Branch [1-$i]: " - local bc; read -r bc + local bc; read -rt 30 bc if [[ "$bc" =~ ^[0-9]+$ ]] && [ "$bc" -ge 1 ] && [ "$bc" -lt "$i" ]; then NITRO_BRANCH="${branches[$((bc-1))]}" elif [ "$bc" = "$i" ]; then - echo -en " Name: "; read -r NITRO_BRANCH; [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main" + echo -en " Name: "; read -rt 30 NITRO_BRANCH; [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main" else warn "Invalid, using: $NITRO_BRANCH" fi @@ -915,11 +936,10 @@ pick_branch_and_update() { } cmd_interactive() { + local branches=() + load_branches branches while true; do clear - local branches=() - load_branches branches - echo -e "${C_CYAN}" echo "" echo " ╔═══════════════════════════════════════════════════════════════════════════╗" @@ -966,7 +986,7 @@ cmd_interactive() { echo -e " ${C_RED}${C_BOLD}0)${C_RESET} Exit" echo "" echo -en " ${C_CYAN}${C_BOLD}Select [0-18]${C_RESET}: " - local choice; read -r choice + local choice; read -rt 30 choice case "$choice" in 1) SELECTIVE_UPDATE=""; (cmd_update) || warn "Update had errors" ;; @@ -983,11 +1003,12 @@ cmd_interactive() { echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $b$m"; i=$((i+1)) done echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} Custom" - echo -en "\n Branch [1-$i]: "; local bc; read -r bc + echo -en "\n Branch [1-$i]: "; local bc; read -rt 30 bc if [[ "$bc" =~ ^[0-9]+$ ]] && [ "$bc" -ge 1 ] && [ "$bc" -lt "$i" ]; then NITRO_BRANCH="${branches[$((bc-1))]}" - elif [ "$bc" = "$i" ]; then echo -en " Name: "; read -r NITRO_BRANCH + elif [ "$bc" = "$i" ]; then echo -en " Name: "; read -rt 30 NITRO_BRANCH fi + load_branches branches ok "Branch: $NITRO_BRANCH" ;; 8) (cmd_database) ;; 9) (cmd_backup); echo ""; (cmd_backups) ;; @@ -1006,7 +1027,7 @@ cmd_interactive() { echo "" echo -en " ${C_DIM}Press Enter to continue...${C_RESET}" - read -r + read -rt 30 done }