@@ -2493,126 +2493,159 @@ auto_recover() {
24932493
24942494# ── Section 13: Auto-Update ─────────────────────────────────
24952495
2496- check_for_updates () {
2497- local latest
2498- latest=$( curl -sL --max-time 10 \
2499- " https://api.github.com/repos/${GITHUB_REPO} /releases/latest" \
2500- 2> /dev/null | grep ' "tag_name"' | head -1 | grep -oE ' [0-9]+\.[0-9]+\.[0-9]+' )
2496+ _UPDATE_SHA_FILE= " ${INSTALL_DIR} /.update_sha"
2497+ _UPDATE_BADGE= " /tmp/.mtproxymax_update_available"
2498+
2499+ # Background SHA check — non-blocking, ~40 bytes over the wire
2500+ check_update_sha_bg () {
2501+ {
2502+ local _remote_sha
2503+ _remote_sha=$( curl -fsSL --connect-timeout 5 --max-time 10 \
2504+ " https://api.github.com/repos/${GITHUB_REPO} /commits/main" \
2505+ -H " Accept: application/vnd.github.sha" 2> /dev/null) || true
2506+
2507+ # Must be 40 lowercase hex chars
2508+ if [ -n " $_remote_sha " ] && [ ${# _remote_sha} -ge 40 ]; then
2509+ _remote_sha=" ${_remote_sha: 0: 40} "
2510+ case " $_remote_sha " in * [!a-f0-9]* ) exit 0 ;; esac
2511+
2512+ local _stored=" "
2513+ [ -f " $_UPDATE_SHA_FILE " ] && _stored=$( < " $_UPDATE_SHA_FILE " )
2514+
2515+ if [ -z " $_stored " ]; then
2516+ # First run — save baseline, no badge
2517+ echo " $_remote_sha " > " $_UPDATE_SHA_FILE " 2> /dev/null || true
2518+ rm -f " $_UPDATE_BADGE " 2> /dev/null
2519+ elif [ " $_remote_sha " != " $_stored " ]; then
2520+ echo " new" > " $_UPDATE_BADGE " 2> /dev/null
2521+ else
2522+ rm -f " $_UPDATE_BADGE " 2> /dev/null
2523+ fi
2524+ fi
2525+ # API unreachable — do nothing; badge stays as-is (no false positives)
2526+ } &
2527+ }
2528+
2529+ self_update () {
2530+ # Prevent concurrent updates
2531+ if command -v flock & > /dev/null; then
2532+ local _lfd
2533+ exec {_lfd}> /tmp/.mtproxymax_update.lock
2534+ if ! flock -n " $_lfd " 2> /dev/null; then
2535+ log_warn " Another update is already running."
2536+ return 1
2537+ fi
2538+ fi
2539+
2540+ local _script_updated=false
2541+ local _url=" https://raw.githubusercontent.com/${GITHUB_REPO} /main/mtproxymax.sh"
2542+
2543+ echo " "
2544+ log_info " Checking for script updates..."
2545+
2546+ local _tmp
2547+ _tmp=$( _mktemp) || return 1
2548+
2549+ if curl -fsSL --max-time 60 --max-filesize 5242880 -o " $_tmp " " $_url " 2> /dev/null; then
2550+ # Validate: bash syntax + sanity check
2551+ if ! bash -n " $_tmp " 2> /dev/null; then
2552+ log_error " Downloaded script has syntax errors — aborting"
2553+ rm -f " $_tmp " ; return 1
2554+ fi
2555+ if ! grep -q " GITHUB_REPO=\" SamNet-dev/MTProxyMax\" " " $_tmp " 2> /dev/null; then
2556+ log_error " Downloaded file doesn't look like MTProxyMax — aborting"
2557+ rm -f " $_tmp " ; return 1
2558+ fi
2559+ local _dl_size
2560+ _dl_size=$( wc -c < " $_tmp " )
2561+ if [ " $_dl_size " -lt 10000 ]; then
2562+ log_error " Downloaded file too small (${_dl_size} bytes) — possible truncated download"
2563+ rm -f " $_tmp " ; return 1
2564+ fi
2565+
2566+ local _new_ver
2567+ _new_ver=$( grep -m1 ' ^VERSION="' " $_tmp " | cut -d' "' -f2)
25012568
2502- if [ -z " $latest " ]; then
2569+ # Compare SHA256 — if identical, already up to date
2570+ local _local_hash _remote_hash
2571+ _local_hash=$( sha256sum " ${INSTALL_DIR} /mtproxymax" 2> /dev/null | cut -d' ' -f1)
2572+ _remote_hash=$( sha256sum " $_tmp " | cut -d' ' -f1)
2573+
2574+ if [ " $_local_hash " = " $_remote_hash " ]; then
2575+ log_success " Script is already up to date (v${_new_ver:- ${VERSION} } )"
2576+ rm -f " $_tmp " " $_UPDATE_BADGE "
2577+ else
2578+ log_info " Update found: v${_new_ver:- ?} (installed: v${VERSION} )"
2579+ echo -en " ${BOLD} Update now? [y/N]:${NC} "
2580+ local _confirm; read -r _confirm
2581+ if [ " $_confirm " != " y" ] && [ " $_confirm " != " Y" ]; then
2582+ log_info " Skipped"
2583+ rm -f " $_tmp "
2584+ else
2585+ mkdir -p " $BACKUP_DIR "
2586+ cp " ${INSTALL_DIR} /mtproxymax" \
2587+ " ${BACKUP_DIR} /mtproxymax.v${VERSION} .$( date +%s) " 2> /dev/null || true
2588+ chmod +x " $_tmp "
2589+ mv " $_tmp " " ${INSTALL_DIR} /mtproxymax"
2590+ log_success " Script updated to v${_new_ver:- ?} "
2591+ _script_updated=true
2592+ rm -f " $_UPDATE_BADGE "
2593+
2594+ # Save new commit SHA as baseline
2595+ local _new_sha
2596+ _new_sha=$( curl -fsSL --connect-timeout 5 --max-time 10 \
2597+ " https://api.github.com/repos/${GITHUB_REPO} /commits/main" \
2598+ -H " Accept: application/vnd.github.sha" 2> /dev/null) || true
2599+ if [ -n " $_new_sha " ] && [ ${# _new_sha} -ge 40 ]; then
2600+ _new_sha=" ${_new_sha: 0: 40} "
2601+ case " $_new_sha " in
2602+ * [!a-f0-9]* ) : ;;
2603+ * ) echo " $_new_sha " > " $_UPDATE_SHA_FILE " 2> /dev/null || true ;;
2604+ esac
2605+ fi
2606+ fi
2607+ fi
2608+ else
2609+ log_error " Download failed — check your internet connection"
2610+ rm -f " $_tmp "
25032611 return 1
25042612 fi
25052613
2506- # Update available if current version is older than latest
2507- if ! version_gte " $VERSION " " $latest " ; then
2508- echo " $latest "
2509- return 0
2614+ # Regenerate + restart Telegram bot service if script was updated
2615+ if [ " $_script_updated " = true ] && [ " ${TELEGRAM_ENABLED:- } " = " true" ]; then
2616+ log_info " Regenerating Telegram bot service..."
2617+ telegram_generate_service_script
2618+ if command -v systemctl & > /dev/null; then
2619+ systemctl restart mtproxymax-telegram.service 2> /dev/null \
2620+ && log_success " Telegram bot service restarted" \
2621+ || log_warn " Telegram restart failed — run: systemctl restart mtproxymax-telegram.service"
2622+ fi
25102623 fi
2511- return 1
2512- }
25132624
2514- self_update () {
2515- log_info " Checking for updates..."
2516-
2517- # Check telemt engine update
2518- local telemt_latest
2519- telemt_latest=$( check_telemt_update 2> /dev/null) && {
2520- local telemt_current
2521- telemt_current=$( get_telemt_version)
2522- log_info " Telemt engine update: v${telemt_current} -> v${telemt_latest} "
2625+ # Telemt engine update check
2626+ echo " "
2627+ local _telemt_latest
2628+ _telemt_latest=$( check_telemt_update 2> /dev/null) && {
2629+ local _telemt_cur
2630+ _telemt_cur=$( get_telemt_version)
2631+ log_info " Telemt engine update available: v${_telemt_cur} -> v${_telemt_latest} "
25232632 echo -en " ${BOLD} Update telemt engine? [y/N]:${NC} "
2524- local tconfirm
2525- read -r tconfirm
2526- if [ " $tconfirm " = " y" ] || [ " $tconfirm " = " Y" ]; then
2633+ local _tconfirm; read -r _tconfirm
2634+ if [ " $_tconfirm " = " y" ] || [ " $_tconfirm " = " Y" ]; then
25272635 build_telemt_image true
25282636 if is_proxy_running; then
25292637 load_secrets
25302638 restart_proxy_container
25312639 fi
25322640 fi
25332641 } || {
2534- local tver
2535- tver=$( get_telemt_version)
2536- if [ " $tver " = " unknown" ]; then
2642+ local _tver; _tver=$( get_telemt_version)
2643+ if [ " $_tver " = " unknown" ]; then
25372644 log_warn " Telemt version unknown — try rebuilding with: mtproxymax rebuild"
25382645 else
2539- log_success " Telemt engine is up to date (v${tver } )"
2646+ log_success " Telemt engine is up to date (v${_tver } )"
25402647 fi
25412648 }
2542-
2543- # Check script update
2544- local latest
2545- latest=$( check_for_updates 2> /dev/null)
2546- local rc=$?
2547- if [ $rc -ne 0 ] && [ -z " $latest " ]; then
2548- log_info " Script update check unavailable (no releases found)"
2549- return 0
2550- fi
2551- if [ -z " $latest " ]; then
2552- log_success " Script is up to date (v${VERSION} )"
2553- return 0
2554- fi
2555-
2556- log_info " New version available: v${latest} (current: v${VERSION} )"
2557- echo -en " ${BOLD} Update now? [y/N]:${NC} "
2558- local confirm
2559- read -r confirm
2560- [ " $confirm " != " y" ] && [ " $confirm " != " Y" ] && { log_info " Skipped" ; return 0; }
2561-
2562- # Backup current script
2563- mkdir -p " $BACKUP_DIR "
2564- cp " ${INSTALL_DIR} /mtproxymax" " ${BACKUP_DIR} /mtproxymax.v${VERSION} .$( date +%s) " 2> /dev/null
2565-
2566- # Download new version
2567- local tmp
2568- tmp=$( _mktemp) || return 1
2569- if curl -sL --max-time 60 \
2570- " https://raw.githubusercontent.com/${GITHUB_REPO} /main/mtproxymax.sh" \
2571- -o " $tmp " 2> /dev/null; then
2572-
2573- # Validate: must be a bash script, > 1KB, and contain the expected version
2574- local dl_size
2575- dl_size=$( wc -c < " $tmp " )
2576- if ! head -1 " $tmp " | grep -q ' ^#!/bin/bash' ; then
2577- log_error " Downloaded file is not a valid script"
2578- return 1
2579- fi
2580- if [ " $dl_size " -lt 1000 ]; then
2581- log_error " Downloaded file too small (${dl_size} bytes) — possible truncated download"
2582- return 1
2583- fi
2584- local dl_version
2585- dl_version=$( grep -oE ' ^VERSION="[0-9]+\.[0-9]+\.[0-9]+"' " $tmp " | head -1 | grep -oE ' [0-9]+\.[0-9]+\.[0-9]+' )
2586- if [ -n " $dl_version " ] && [ " $dl_version " != " $latest " ]; then
2587- log_error " Version mismatch: expected v${latest} , got v${dl_version} "
2588- return 1
2589- fi
2590- # Verify SHA256 integrity (download hash from repo if available)
2591- local sha_expect
2592- sha_expect=$( curl -sL --max-time 10 \
2593- " https://raw.githubusercontent.com/${GITHUB_REPO} /main/mtproxymax.sh.sha256" 2> /dev/null \
2594- | grep -oE ' ^[0-9a-f]{64}' | head -1)
2595- if [ -n " $sha_expect " ]; then
2596- local sha_actual
2597- sha_actual=$( sha256sum " $tmp " | cut -d' ' -f1)
2598- if [ " $sha_actual " != " $sha_expect " ]; then
2599- log_error " SHA256 mismatch — download may be corrupted or tampered"
2600- return 1
2601- fi
2602- log_info " SHA256 verified"
2603- fi
2604- chmod +x " $tmp "
2605- mv " $tmp " " ${INSTALL_DIR} /mtproxymax"
2606- log_success " Updated to v${latest} "
2607-
2608- # Regenerate telegram service if needed
2609- if [ " $TELEGRAM_ENABLED " = " true" ]; then
2610- telegram_generate_service_script
2611- fi
2612- else
2613- log_error " Download failed"
2614- return 1
2615- fi
26162649}
26172650
26182651# ── Section 14: Telegram Integration ────────────────────────
@@ -3986,6 +4019,7 @@ cli_main() {
39864019 if [ -f " $SETTINGS_FILE " ]; then
39874020 load_settings
39884021 load_secrets
4022+ check_update_sha_bg # non-blocking background SHA check
39894023 show_main_menu
39904024 else
39914025 run_installer
@@ -4633,6 +4667,10 @@ show_main_menu() {
46334667 draw_box_line " ${BOLD} Secrets:${NC} ${active} active / ${disabled} disabled" " $w "
46344668
46354669 draw_box_sep " $w "
4670+ if [ -f " $_UPDATE_BADGE " ]; then
4671+ draw_box_line " ${YELLOW}${BOLD} ⬆ Update available — select [9] to update${NC} " " $w "
4672+ draw_box_sep " $w "
4673+ fi
46364674 draw_box_empty " $w "
46374675 draw_box_line " ${BRIGHT_CYAN} [1]${NC} Proxy Management" " $w "
46384676 draw_box_line " ${BRIGHT_CYAN} [2]${NC} Secret Management" " $w "
0 commit comments