mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-16 06:53:00 +01:00
Compare commits
2 Commits
feat/downl
...
docs/websi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c582e37dc | ||
|
|
0071259e67 |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -420,30 +420,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
</details>
|
||||
|
||||
## 2026-03-13
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Hotfix: Removed clean install usage from original script. [@nickheyer](https://github.com/nickheyer) ([#12870](https://github.com/community-scripts/ProxmoxVE/pull/12870))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- Discopanel: V2 Support + Script rewrite [@nickheyer](https://github.com/nickheyer) ([#12763](https://github.com/community-scripts/ProxmoxVE/pull/12763))
|
||||
|
||||
### 🧰 Tools
|
||||
|
||||
- update-apps: fix restore path, add PBS support and improve restore messages [@omertahaoztop](https://github.com/omertahaoztop) ([#12528](https://github.com/community-scripts/ProxmoxVE/pull/12528))
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- fix(pve-privilege-converter): handle already stopped container in manage_states [@liuqitoday](https://github.com/liuqitoday) ([#12765](https://github.com/community-scripts/ProxmoxVE/pull/12765))
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Update: Docs/website metadata workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12858](https://github.com/community-scripts/ProxmoxVE/pull/12858))
|
||||
|
||||
## 2026-03-12
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: DragoQC | Co-Author: nickheyer
|
||||
# Author: DragoQC
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://discopanel.app/ | Github: https://github.com/nickheyer/discopanel
|
||||
|
||||
@@ -38,15 +38,34 @@ function update_script() {
|
||||
|
||||
msg_info "Creating Backup"
|
||||
mkdir -p /opt/discopanel_backup_temp
|
||||
cp /opt/discopanel/data/discopanel.db /opt/discopanel_backup_temp/discopanel.db
|
||||
cp -r /opt/discopanel/data/discopanel.db \
|
||||
/opt/discopanel/data/.recovery_key \
|
||||
/opt/discopanel_backup_temp/
|
||||
if [[ -d /opt/discopanel/data/servers ]]; then
|
||||
cp -r /opt/discopanel/data/servers /opt/discopanel_backup_temp/
|
||||
fi
|
||||
msg_ok "Created Backup"
|
||||
|
||||
fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "prebuild" "latest" "/opt/discopanel" "discopanel-linux-amd64.tar.gz"
|
||||
ln -sf /opt/discopanel/discopanel-linux-amd64 /opt/discopanel/discopanel
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "latest" "/opt/discopanel"
|
||||
|
||||
msg_info "Setting up DiscoPanel"
|
||||
cd /opt/discopanel
|
||||
$STD make gen
|
||||
cd /opt/discopanel/web/discopanel
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
msg_ok "Built Web Interface"
|
||||
|
||||
setup_go
|
||||
|
||||
msg_info "Building DiscoPanel"
|
||||
cd /opt/discopanel
|
||||
$STD go build -o discopanel cmd/discopanel/main.go
|
||||
msg_ok "Built DiscoPanel"
|
||||
|
||||
msg_info "Restoring Data"
|
||||
mkdir -p /opt/discopanel/data
|
||||
mv /opt/discopanel_backup_temp/discopanel.db /opt/discopanel/data/discopanel.db
|
||||
cp -a /opt/discopanel_backup_temp/. /opt/discopanel/data/
|
||||
rm -rf /opt/discopanel_backup_temp
|
||||
msg_ok "Restored Data"
|
||||
|
||||
|
||||
15
ct/plex.sh
15
ct/plex.sh
@@ -46,11 +46,11 @@ function update_script() {
|
||||
"main"
|
||||
msg_ok "Migrated to new Plex repository"
|
||||
fi
|
||||
elif compgen -G "/etc/apt/sources.list.d/plex*.list" >/dev/null; then
|
||||
elif [[ -f /etc/apt/sources.list.d/plexmediaserver.list ]]; then
|
||||
msg_info "Migrating to new Plex repository (deb822)"
|
||||
rm -f /etc/apt/sources.list.d/plex*.list
|
||||
rm -f /etc/apt/sources.list.d/plexmediaserver.list
|
||||
rm -f /etc/apt/sources.list.d/plex*
|
||||
rm -f /usr/share/keyrings/PlexSign.asc
|
||||
rm -f /usr/share/keyrings/plexmediaserver.v2.gpg
|
||||
setup_deb822_repo \
|
||||
"plexmediaserver" \
|
||||
"https://downloads.plex.tv/plex-keys/PlexSign.v2.key" \
|
||||
@@ -58,15 +58,6 @@ function update_script() {
|
||||
"public" \
|
||||
"main"
|
||||
msg_ok "Migrated to new Plex repository (deb822)"
|
||||
elif [[ ! -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; then
|
||||
msg_info "Setting up Plex repository"
|
||||
setup_deb822_repo \
|
||||
"plexmediaserver" \
|
||||
"https://downloads.plex.tv/plex-keys/PlexSign.v2.key" \
|
||||
"https://repo.plex.tv/deb/" \
|
||||
"public" \
|
||||
"main"
|
||||
msg_ok "Set up Plex repository"
|
||||
fi
|
||||
if [[ -f /usr/local/bin/plexupdate ]] || [[ -d /opt/plexupdate ]]; then
|
||||
msg_info "Removing legacy plexupdate"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: DragoQC | Co-Author: nickheyer
|
||||
# Author: DragoQC
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://discopanel.app/ | Github: https://github.com/nickheyer/discopanel
|
||||
|
||||
@@ -12,9 +12,25 @@ setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "prebuild" "latest" "/opt/discopanel" "discopanel-linux-amd64.tar.gz"
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y build-essential
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="22" setup_nodejs
|
||||
setup_go
|
||||
fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "latest" "/opt/discopanel"
|
||||
setup_docker
|
||||
|
||||
msg_info "Setting up DiscoPanel"
|
||||
cd /opt/discopanel
|
||||
$STD make gen
|
||||
cd /opt/discopanel/web/discopanel
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
cd /opt/discopanel
|
||||
$STD go build -o discopanel cmd/discopanel/main.go
|
||||
msg_ok "Setup DiscoPanel"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/discopanel.service
|
||||
[Unit]
|
||||
@@ -23,7 +39,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/discopanel
|
||||
ExecStart=/opt/discopanel/discopanel-linux-amd64
|
||||
ExecStart=/opt/discopanel/discopanel
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -105,13 +105,11 @@ curl_with_retry() {
|
||||
fi
|
||||
fi
|
||||
|
||||
debug_log "curl attempt $attempt failed (timeout=${timeout}s), waiting ${backoff}s before retry..."
|
||||
debug_log "curl attempt $attempt failed, waiting ${backoff}s before retry..."
|
||||
sleep "$backoff"
|
||||
# Exponential backoff: 1, 2, 4, 8... capped at 30s
|
||||
backoff=$((backoff * 2))
|
||||
((backoff > 30)) && backoff=30
|
||||
# Double --max-time on each retry so slow connections can finish
|
||||
timeout=$((timeout * 2))
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
@@ -174,10 +172,8 @@ curl_api_with_retry() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
debug_log "curl API attempt $attempt failed (HTTP $http_code, timeout=${timeout}s), waiting ${attempt}s..."
|
||||
debug_log "curl API attempt $attempt failed (HTTP $http_code), waiting ${attempt}s..."
|
||||
sleep "$attempt"
|
||||
# Double --max-time on each retry so slow connections can finish
|
||||
timeout=$((timeout * 2))
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
@@ -2578,30 +2574,6 @@ function ensure_usr_local_bin_persist() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# curl_download - Downloads a file with automatic retry and exponential backoff.
|
||||
#
|
||||
# Usage: curl_download <output_file> <url>
|
||||
#
|
||||
# Retries up to 5 times with increasing --max-time (60/120/240/480/960s).
|
||||
# Returns 0 on success, 1 if all attempts fail.
|
||||
# ------------------------------------------------------------------------------
|
||||
function curl_download() {
|
||||
local output="$1"
|
||||
local url="$2"
|
||||
local timeouts=(60 120 240 480 960)
|
||||
|
||||
for i in "${!timeouts[@]}"; do
|
||||
if curl --connect-timeout 15 --max-time "${timeouts[$i]}" -fsSL -o "$output" "$url"; then
|
||||
return 0
|
||||
fi
|
||||
if ((i < ${#timeouts[@]} - 1)); then
|
||||
msg_warn "Download timed out after ${timeouts[$i]}s, retrying... (attempt $((i + 2))/${#timeouts[@]})"
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Downloads and deploys latest Codeberg release (source, binary, tarball, asset).
|
||||
#
|
||||
@@ -2659,7 +2631,8 @@ function fetch_and_deploy_codeberg_release() {
|
||||
local app_lc=$(echo "${app,,}" | tr -d ' ')
|
||||
local version_file="$HOME/.${app_lc}"
|
||||
|
||||
local api_timeouts=(60 120 240)
|
||||
local api_timeout="--connect-timeout 10 --max-time 60"
|
||||
local download_timeout="--connect-timeout 15 --max-time 900"
|
||||
|
||||
local current_version=""
|
||||
[[ -f "$version_file" ]] && current_version=$(<"$version_file")
|
||||
@@ -2699,7 +2672,7 @@ function fetch_and_deploy_codeberg_release() {
|
||||
|
||||
# Codeberg archive URL format: https://codeberg.org/{owner}/{repo}/archive/{tag}.tar.gz
|
||||
local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz"
|
||||
if curl_download "$tmpdir/$filename" "$archive_url"; then
|
||||
if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$archive_url"; then
|
||||
download_success=true
|
||||
fi
|
||||
|
||||
@@ -2746,18 +2719,16 @@ function fetch_and_deploy_codeberg_release() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
local attempt=0 success=false resp http_code
|
||||
local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code
|
||||
|
||||
while ((attempt < ${#api_timeouts[@]})); do
|
||||
resp=$(curl --connect-timeout 10 --max-time "${api_timeouts[$attempt]}" -fsSL -w "%{http_code}" -o /tmp/codeberg_rel.json "$api_url") && success=true && break
|
||||
while ((attempt <= max_retries)); do
|
||||
resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/codeberg_rel.json "$api_url") && success=true && break
|
||||
sleep "$retry_delay"
|
||||
((attempt++))
|
||||
if ((attempt < ${#api_timeouts[@]})); then
|
||||
msg_warn "API request timed out after ${api_timeouts[$((attempt-1))]}s, retrying... (attempt $((attempt + 1))/${#api_timeouts[@]})"
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $success; then
|
||||
msg_error "Failed to fetch release metadata from $api_url after ${#api_timeouts[@]} attempts"
|
||||
msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -2798,7 +2769,7 @@ function fetch_and_deploy_codeberg_release() {
|
||||
|
||||
# Codeberg archive URL format
|
||||
local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz"
|
||||
if curl_download "$tmpdir/$filename" "$archive_url"; then
|
||||
if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$archive_url"; then
|
||||
download_success=true
|
||||
fi
|
||||
|
||||
@@ -2872,7 +2843,7 @@ function fetch_and_deploy_codeberg_release() {
|
||||
fi
|
||||
|
||||
filename="${url_match##*/}"
|
||||
curl_download "$tmpdir/$filename" "$url_match" || {
|
||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || {
|
||||
msg_error "Download failed: $url_match"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
@@ -2915,7 +2886,7 @@ function fetch_and_deploy_codeberg_release() {
|
||||
}
|
||||
|
||||
filename="${asset_url##*/}"
|
||||
curl_download "$tmpdir/$filename" "$asset_url" || {
|
||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || {
|
||||
msg_error "Download failed: $asset_url"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
@@ -3016,7 +2987,7 @@ function fetch_and_deploy_codeberg_release() {
|
||||
local target_file="$app"
|
||||
[[ "$use_filename" == "true" ]] && target_file="$filename"
|
||||
|
||||
curl_download "$target/$target_file" "$asset_url" || {
|
||||
curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || {
|
||||
msg_error "Download failed: $asset_url"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
@@ -3211,7 +3182,8 @@ function fetch_and_deploy_gh_release() {
|
||||
local app_lc=$(echo "${app,,}" | tr -d ' ')
|
||||
local version_file="$HOME/.${app_lc}"
|
||||
|
||||
local api_timeouts=(60 120 240)
|
||||
local api_timeout="--connect-timeout 10 --max-time 60"
|
||||
local download_timeout="--connect-timeout 15 --max-time 900"
|
||||
|
||||
local current_version=""
|
||||
[[ -f "$version_file" ]] && current_version=$(<"$version_file")
|
||||
@@ -3231,10 +3203,10 @@ function fetch_and_deploy_gh_release() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
local max_retries=${#api_timeouts[@]} retry_delay=2 attempt=1 success=false http_code
|
||||
local max_retries=3 retry_delay=2 attempt=1 success=false http_code
|
||||
|
||||
while ((attempt <= max_retries)); do
|
||||
http_code=$(curl --connect-timeout 10 --max-time "${api_timeouts[$((attempt-1))]:-240}" -sSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url" 2>/dev/null) || true
|
||||
http_code=$(curl $api_timeout -sSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url" 2>/dev/null) || true
|
||||
if [[ "$http_code" == "200" ]]; then
|
||||
success=true
|
||||
break
|
||||
@@ -3308,7 +3280,7 @@ function fetch_and_deploy_gh_release() {
|
||||
local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_name.tar.gz"
|
||||
filename="${app_lc}-${version_safe}.tar.gz"
|
||||
|
||||
curl_download "$tmpdir/$filename" "$direct_tarball_url" || {
|
||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || {
|
||||
msg_error "Download failed: $direct_tarball_url"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
@@ -3411,7 +3383,7 @@ function fetch_and_deploy_gh_release() {
|
||||
fi
|
||||
|
||||
filename="${url_match##*/}"
|
||||
curl_download "$tmpdir/$filename" "$url_match" || {
|
||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || {
|
||||
msg_error "Download failed: $url_match"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
@@ -3478,7 +3450,7 @@ function fetch_and_deploy_gh_release() {
|
||||
}
|
||||
|
||||
filename="${asset_url##*/}"
|
||||
curl_download "$tmpdir/$filename" "$asset_url" || {
|
||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || {
|
||||
msg_error "Download failed: $asset_url"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
@@ -3599,7 +3571,7 @@ function fetch_and_deploy_gh_release() {
|
||||
local target_file="$app"
|
||||
[[ "$use_filename" == "true" ]] && target_file="$filename"
|
||||
|
||||
curl_download "$target/$target_file" "$asset_url" || {
|
||||
curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || {
|
||||
msg_error "Download failed: $asset_url"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
|
||||
@@ -134,20 +134,16 @@ manage_states() {
|
||||
read -rp "Shutdown source and start new container? [Y/n]: " answer
|
||||
answer=${answer:-Y}
|
||||
if [[ $answer =~ ^[Yy] ]]; then
|
||||
pct shutdown "$CONTAINER_ID"
|
||||
for i in {1..36}; do
|
||||
sleep 5
|
||||
! pct status "$CONTAINER_ID" | grep -q running && break
|
||||
done
|
||||
if pct status "$CONTAINER_ID" | grep -q running; then
|
||||
pct shutdown "$CONTAINER_ID"
|
||||
for i in {1..36}; do
|
||||
sleep 5
|
||||
! pct status "$CONTAINER_ID" | grep -q running && break
|
||||
done
|
||||
if pct status "$CONTAINER_ID" | grep -q running; then
|
||||
read -rp "Timeout reached. Force shutdown? [Y/n]: " force
|
||||
if [[ ${force:-Y} =~ ^[Yy] ]]; then
|
||||
pkill -9 -f "lxc-start -F -n $CONTAINER_ID"
|
||||
fi
|
||||
read -rp "Timeout reached. Force shutdown? [Y/n]: " force
|
||||
if [[ ${force:-Y} =~ ^[Yy] ]]; then
|
||||
pkill -9 -f "lxc-start -F -n $CONTAINER_ID"
|
||||
fi
|
||||
else
|
||||
msg_custom "ℹ️" "\e[36m" "Source container $CONTAINER_ID is already stopped"
|
||||
fi
|
||||
pct start "$NEW_CONTAINER_ID"
|
||||
msg_ok "New container started"
|
||||
|
||||
@@ -426,23 +426,16 @@ for container in $CHOICE; do
|
||||
elif [ $exit_code -eq 75 ]; then
|
||||
echo -e "${YW}[WARN]${CL} Container $container skipped (requires interactive mode)"
|
||||
elif [ "$BACKUP_CHOICE" == "yes" ]; then
|
||||
msg_error "Update failed for container $container (exit code: $exit_code) — attempting restore"
|
||||
msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)"
|
||||
msg_info "Restoring LXC from backup"
|
||||
pct stop $container
|
||||
LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}')
|
||||
BACKUP_ENTRY=$(pvesm list "$STORAGE_CHOICE" 2>/dev/null | awk -v ctid="$container" '$1 ~ "vzdump-lxc-"ctid"-" || $1 ~ "/ct/"ctid"/" {print $1}' | sort -r | head -n1)
|
||||
if [ -z "$BACKUP_ENTRY" ]; then
|
||||
msg_error "No backup found in storage $STORAGE_CHOICE for container $container"
|
||||
exit 235
|
||||
fi
|
||||
msg_info "Restoring from: $BACKUP_ENTRY"
|
||||
pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force >/dev/null 2>&1
|
||||
pct restore $container /var/lib/vz/dump/vzdump-lxc-${container}-*.tar.zst --storage $LXC_STORAGE --force >/dev/null 2>&1
|
||||
pct start $container
|
||||
restorestatus=$?
|
||||
if [ $restorestatus -eq 0 ]; then
|
||||
pct start $container
|
||||
msg_ok "Container $container successfully restored from backup"
|
||||
msg_ok "Restored LXC from backup"
|
||||
else
|
||||
msg_error "Restore failed for container $container"
|
||||
msg_error "Restored LXC from backup failed"
|
||||
exit 235
|
||||
fi
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user