release: 1.4.0 — one version for stable/beta/dev
Light/dark theme and compact mobile layout, per-client speed limit, real speed tests, panel domain (Let's Encrypt), client address + cascade relay, RKN scanner guard, WARP, backup/restore, router web terminal, Android VPN client with split tunnelling and Android admin app. Updating from 1.2.x keeps keys, server.env and clients; tested 1.2.8 -> 1.4.0 on live VMs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
162
update.sh
162
update.sh
@@ -25,12 +25,27 @@ PHOBOS_DIR=/opt/Phobos
|
||||
PANEL_DIR=/opt/phobos-panel
|
||||
PUB_REPO=andrey271192/PCA_Phobos
|
||||
DEV_REPO=andrey271192/PCA_Phobos-dev
|
||||
PHOBOSWG_APK_TAG="v1.3.0-beta"
|
||||
PHOBOSWG_APK_SHA256="5399de7c576d56e491a01ab9b52d53c08d30a34d1156c1cb0f4520bf34821e02"
|
||||
BACKUPS="$PHOBOS_DIR/updates"
|
||||
VERFILE="$PANEL_DIR/.version"
|
||||
CUR=$(cat "$VERFILE" 2>/dev/null || echo "unknown")
|
||||
# Токен подписчика для приватных каналов: env > сохранённый файл
|
||||
GH_TOKEN="${GH_TOKEN:-$(cat "$PANEL_DIR/.gh_token" 2>/dev/null || true)}"
|
||||
|
||||
panel_service_name() {
|
||||
for name in phobos-dev-test phobos-panel; do
|
||||
systemctl status "$name" >/dev/null 2>&1 && { echo "$name"; return; }
|
||||
done
|
||||
echo phobos-panel
|
||||
}
|
||||
|
||||
restart_panel_service() {
|
||||
local svc
|
||||
svc="$(panel_service_name)"
|
||||
systemctl restart "$svc" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# repo for a ref/channel: beta/dev/*-beta/*-dev -> private, остальное -> public
|
||||
repo_for() { case "$1" in beta|dev|*-beta|*-dev) echo "$DEV_REPO";; *) echo "$PUB_REPO";; esac; }
|
||||
is_private() { case "$1" in beta|dev|*-beta|*-dev) return 0;; *) return 1;; esac; }
|
||||
@@ -44,6 +59,60 @@ gh_curl() { # $1=ref, rest=curl args
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_repo_file() { # $1=ref $2=repo_path $3=output
|
||||
local ref="$1" rp="$2" out="$3" repo
|
||||
repo=$(repo_for "$ref")
|
||||
if is_private "$ref" && [ -n "$GH_TOKEN" ]; then
|
||||
curl -fsSL -m25 -H "Authorization: token $GH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.raw" \
|
||||
"https://api.github.com/repos/$repo/contents/$rp?ref=$ref" -o "$out"
|
||||
else
|
||||
gh_curl "$ref" -fsSL -m25 \
|
||||
"https://raw.githubusercontent.com/$repo/$ref/$rp?cb=$(date +%s)" -o "$out"
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_repo_text() { # $1=ref $2=repo_path
|
||||
local tmp rc
|
||||
tmp=$(mktemp)
|
||||
fetch_repo_file "$1" "$2" "$tmp"; rc=$?
|
||||
if [ "$rc" = 0 ]; then cat "$tmp"; fi
|
||||
rm -f "$tmp"
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
refresh_phoboswg_apk() { # $1=ref/channel
|
||||
local ref="${1:-}"
|
||||
is_private "$ref" || return 0
|
||||
[ -n "$GH_TOKEN" ] || return 0
|
||||
local apk="$PHOBOS_DIR/www/app/PhobosWG.apk"
|
||||
mkdir -p "$PHOBOS_DIR/www/app"
|
||||
local cur_sha
|
||||
cur_sha="$(sha256sum "$apk" 2>/dev/null | awk '{print $1}')"
|
||||
if [ "$cur_sha" = "$PHOBOSWG_APK_SHA256" ]; then
|
||||
echo "[APK] PhobosWG.apk current"
|
||||
return 0
|
||||
fi
|
||||
echo "[APK] Refreshing PhobosWG.apk from release $PHOBOSWG_APK_TAG..."
|
||||
local apk_api_url
|
||||
apk_api_url="$(curl -fsSL -m15 -H "Authorization: token $GH_TOKEN" \
|
||||
"https://api.github.com/repos/$DEV_REPO/releases/tags/$PHOBOSWG_APK_TAG" 2>/dev/null \
|
||||
| grep -B8 '"name": "PhobosWG.apk"' | grep '"url"' | head -1 | cut -d'"' -f4)"
|
||||
if [ -n "$apk_api_url" ] && curl -fsSL -m120 -H "Authorization: token $GH_TOKEN" \
|
||||
-H "Accept: application/octet-stream" "$apk_api_url" -o "$apk"; then
|
||||
local new_sha
|
||||
new_sha="$(sha256sum "$apk" 2>/dev/null | awk '{print $1}')"
|
||||
if [ "$new_sha" = "$PHOBOSWG_APK_SHA256" ]; then
|
||||
echo "[APK] Downloaded PhobosWG.apk: $(ls -lh "$apk" | awk '{print $5}')"
|
||||
else
|
||||
rm -f "$apk"
|
||||
echo "[APK] SHA mismatch after download (non-fatal)"
|
||||
fi
|
||||
else
|
||||
echo "[APK] Download failed (non-fatal)"
|
||||
fi
|
||||
}
|
||||
|
||||
managed_files() {
|
||||
cat <<LIST
|
||||
$PANEL_DIR/app.py|app.py
|
||||
@@ -56,12 +125,22 @@ $PHOBOS_DIR/repo/client/templates/install-wireguard.sh|overlay/install-wireguard
|
||||
$PHOBOS_DIR/repo/client/templates/router-configure-wireguard.sh|overlay/router-configure-wireguard.sh
|
||||
$PHOBOS_DIR/repo/client/templates/router-configure-wireguard-openwrt.sh|overlay/router-configure-wireguard-openwrt.sh
|
||||
$PHOBOS_DIR/repo/client/templates/phobos-pull.sh|overlay/phobos-pull.sh
|
||||
$PHOBOS_DIR/repo/client/templates/phobos-ttyd.sh|overlay/phobos-ttyd.sh
|
||||
$PHOBOS_DIR/repo/client/templates/phobos-uninstall.sh|overlay/phobos-uninstall.sh
|
||||
$PHOBOS_DIR/repo/client/templates/3xui.sh|overlay/3xui.sh
|
||||
$PHOBOS_DIR/server/phobos-warp.sh|server/phobos-warp.sh
|
||||
$PHOBOS_DIR/server/phobos-health.sh|server/phobos-health.sh
|
||||
$PHOBOS_DIR/server/phobos-self-check.sh|server/phobos-self-check.sh
|
||||
$PHOBOS_DIR/server/phobos-pull.sh|server/phobos-pull.sh
|
||||
$PHOBOS_DIR/server/phobos-router-watchdog.py|server/phobos-router-watchdog.py
|
||||
$PHOBOS_DIR/server/api.py|server/api.py
|
||||
$PHOBOS_DIR/server/secondary-setup.sh|server/secondary-setup.sh
|
||||
$PHOBOS_DIR/server/phobos-backup.sh|server/phobos-backup.sh
|
||||
$PHOBOS_DIR/server/phobos-guard.sh|server/phobos-guard.sh
|
||||
$PHOBOS_DIR/server/phobos-cascade.sh|server/phobos-cascade.sh
|
||||
$PHOBOS_DIR/server/phobos-domain.sh|server/phobos-domain.sh
|
||||
$PHOBOS_DIR/server/phobos-limit.sh|server/phobos-limit.sh
|
||||
$PHOBOS_DIR/server/phobos-speedtest.sh|server/phobos-speedtest.sh
|
||||
$PHOBOS_DIR/server/update.sh|update.sh
|
||||
LIST
|
||||
}
|
||||
@@ -73,13 +152,33 @@ run_self_check() {
|
||||
else
|
||||
echo " WARN: phobos-self-check.sh не найден"
|
||||
fi
|
||||
[ -x "$PHOBOS_DIR/server/phobos-backup.sh" ] && ln -sf "$PHOBOS_DIR/server/phobos-backup.sh" /usr/local/bin/phobos-backup 2>/dev/null || true
|
||||
[ -x "$PHOBOS_DIR/server/phobos-warp.sh" ] && ln -sf "$PHOBOS_DIR/server/phobos-warp.sh" /usr/local/bin/phobos-warp 2>/dev/null || true
|
||||
for _n in phobos-guard phobos-cascade phobos-domain; do [ -x "$PHOBOS_DIR/server/$_n.sh" ] && ln -sf "$PHOBOS_DIR/server/$_n.sh" /usr/local/bin/$_n 2>/dev/null || true; done
|
||||
}
|
||||
|
||||
refresh_router_packages() {
|
||||
local script="$PHOBOS_DIR/repo/server/scripts/phobos-client.sh"
|
||||
[ -x "$script" ] || return 0
|
||||
[ -d "$PHOBOS_DIR/clients" ] || return 0
|
||||
local rebuilt=0 failed=0 id
|
||||
for dir in "$PHOBOS_DIR/clients"/*; do
|
||||
[ -d "$dir" ] || continue
|
||||
id=$(basename "$dir")
|
||||
if "$script" package "$id" >/dev/null 2>&1; then
|
||||
rebuilt=$((rebuilt + 1))
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
echo " WARN: router package rebuild failed for $id"
|
||||
fi
|
||||
done
|
||||
[ "$rebuilt" = 0 ] && [ "$failed" = 0 ] || echo "Router packages rebuilt: $rebuilt, failed: $failed"
|
||||
}
|
||||
|
||||
# fetch VERSION of a channel (public repos / or private if token present)
|
||||
chan_version() { # $1 = ref(channel)
|
||||
local repo; repo=$(repo_for "$1"); local repo_ref="$1"
|
||||
if is_private "$1" && [ -z "$GH_TOKEN" ]; then echo ""; return; fi
|
||||
gh_curl "$1" -fsSL -m8 "https://raw.githubusercontent.com/$repo/$1/VERSION" 2>/dev/null | tr -d ' \r\n'
|
||||
fetch_repo_text "$1" VERSION 2>/dev/null | tr -d ' \r\n'
|
||||
}
|
||||
latest_version() { curl -fsSL -m10 "https://raw.githubusercontent.com/$PUB_REPO/main/VERSION" 2>/dev/null | tr -d ' \r\n'; }
|
||||
|
||||
@@ -94,12 +193,10 @@ do_backup() {
|
||||
}
|
||||
|
||||
apply_ref() {
|
||||
local ref="$1"; local repo; repo=$(repo_for "$ref")
|
||||
local repo_ref="$ref"
|
||||
local RAW="https://raw.githubusercontent.com/$repo/$ref"
|
||||
local ref="$1"
|
||||
managed_files | while IFS='|' read -r lp rp; do
|
||||
local tmp; tmp=$(mktemp)
|
||||
if gh_curl "$ref" -fsSL -m25 "$RAW/$rp" -o "$tmp" && [ -s "$tmp" ]; then
|
||||
if fetch_repo_file "$ref" "$rp" "$tmp" && [ -s "$tmp" ]; then
|
||||
mkdir -p "$(dirname "$lp")"; mv "$tmp" "$lp"
|
||||
case "$lp" in *.sh|*.py) chmod +x "$lp" 2>/dev/null || true;; esac
|
||||
else
|
||||
@@ -108,9 +205,39 @@ apply_ref() {
|
||||
done
|
||||
}
|
||||
|
||||
ensure_runtime_deps() {
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq sshpass python3-pip >/dev/null 2>&1 || true
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-flask-sock >/dev/null 2>&1 || true
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-asyncssh >/dev/null 2>&1 || true
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-websocket >/dev/null 2>&1 || true
|
||||
fi
|
||||
python3 - <<'PY' >/dev/null 2>&1 || python3 -m pip install flask-sock asyncssh websocket-client >/dev/null 2>&1 || python3 -m pip install --break-system-packages flask-sock asyncssh websocket-client >/dev/null 2>&1 || true
|
||||
import flask_sock, asyncssh, websocket
|
||||
PY
|
||||
mkdir -p "$PANEL_DIR/static"
|
||||
curl -fsSL -m20 https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css -o "$PANEL_DIR/static/xterm.min.css" 2>/dev/null || true
|
||||
curl -fsSL -m20 https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js -o "$PANEL_DIR/static/xterm.min.js" 2>/dev/null || true
|
||||
curl -fsSL -m20 https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js -o "$PANEL_DIR/static/xterm-addon-fit.min.js" 2>/dev/null || true
|
||||
if [ -f /etc/systemd/system/phobos-panel.service ]; then
|
||||
if grep -q 'ExecStart=/usr/bin/gunicorn -w 1 -b ' /etc/systemd/system/phobos-panel.service; then
|
||||
sed -i 's#ExecStart=/usr/bin/gunicorn -w 1 -b #ExecStart=/usr/bin/gunicorn -w 1 --threads 20 -b #' /etc/systemd/system/phobos-panel.service
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
fi
|
||||
if grep -q '0.0.0.0:8443 app:app' /etc/systemd/system/phobos-panel.service && [ -s "$PANEL_DIR/.port" ]; then
|
||||
port=$(cat "$PANEL_DIR/.port" 2>/dev/null || echo 8443)
|
||||
sed -i "s#0.0.0.0:8443 app:app#0.0.0.0:${port} app:app#" /etc/systemd/system/phobos-panel.service
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
ref_exists() {
|
||||
local repo; repo=$(repo_for "$1"); local repo_ref="$1"
|
||||
gh_curl "$1" -fsSL -m12 "https://raw.githubusercontent.com/$repo/$1/app.py" -o /dev/null 2>/dev/null
|
||||
local tmp rc
|
||||
tmp=$(mktemp)
|
||||
fetch_repo_file "$1" app.py "$tmp" >/dev/null 2>&1; rc=$?
|
||||
rm -f "$tmp"
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
@@ -150,7 +277,8 @@ case "${1:-}" in
|
||||
done
|
||||
echo "$fromver" > "$VERFILE"
|
||||
run_self_check
|
||||
systemctl restart phobos-panel 2>/dev/null || true
|
||||
restart_panel_service
|
||||
refresh_router_packages
|
||||
echo "Откат выполнен -> $fromver"
|
||||
;;
|
||||
*)
|
||||
@@ -174,13 +302,25 @@ case "${1:-}" in
|
||||
( umask 077; printf %s "$GH_TOKEN" > "$PANEL_DIR/.gh_token" )
|
||||
fi
|
||||
repo_ref="$ref"
|
||||
target=$(gh_curl "$ref" -fsSL -m10 "https://raw.githubusercontent.com/$(repo_for "$ref")/$ref/VERSION" 2>/dev/null | tr -d ' \r\n'); [ -z "$target" ] && target="$ref"
|
||||
# run the TARGET version's updater: its file list knows the new files
|
||||
if [ -z "${PHOBOS_UPDATE_REEXEC:-}" ]; then
|
||||
_new=$(mktemp)
|
||||
if fetch_repo_file "$ref" update.sh "$_new" && head -1 "$_new" | grep -q '^#!' && ! cmp -s "$_new" "$0"; then
|
||||
chmod +x "$_new"
|
||||
PHOBOS_UPDATE_REEXEC=1 GH_TOKEN="$GH_TOKEN" exec bash "$_new" "$@"
|
||||
fi
|
||||
rm -f "$_new"
|
||||
fi
|
||||
target=$(fetch_repo_text "$ref" VERSION 2>/dev/null | tr -d ' \r\n'); [ -z "$target" ] && target="$ref"
|
||||
echo "Текущая: $CUR -> целевая: $target ($ref)"
|
||||
b=$(do_backup); echo "Бэкап: $b"
|
||||
apply_ref "$ref"
|
||||
ensure_runtime_deps
|
||||
refresh_phoboswg_apk "$ref"
|
||||
run_self_check
|
||||
refresh_router_packages
|
||||
echo "$target" > "$VERFILE"
|
||||
systemctl restart phobos-panel 2>/dev/null || true
|
||||
restart_panel_service
|
||||
echo "Обновлено до $target. Откат: phobos-update --rollback"
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user