mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-06 21:04:20 +00:00
feat(db): add pgclient command to install or upgrade PostgreSQL client tools
Restoring a panel backup made by a newer pg_dump fails when the host's pg_restore is older, and the existing pg_ensure_client only installs the distribution package when the tools are missing - it can never upgrade, and distribution repositories often cap below the required major. Add pg_upgrade_client to x-ui.sh, exposed as 'x-ui pgclient [major]' and as a PostgreSQL menu entry: it checks the installed pg_restore major, tries the distribution package for the exact requested major first, and falls back to the official PostgreSQL repository (apt on Debian/Ubuntu, yum/dnf on Enterprise Linux, with a /usr/pgsql PATH symlink fallback); Arch, Alpine and openSUSE install their current package. The panel's dump-version mismatch error now names the ready-to-copy command with the exact major parsed from the dump header.
This commit is contained in:
@@ -1640,7 +1640,7 @@ func pgRestoreReadFailureError(probeOutput, localVersion string) error {
|
||||
localVersion = "unknown"
|
||||
}
|
||||
if major, known := pgArchiveVersionIntroducedIn[m[1]]; known {
|
||||
return common.NewErrorf("This backup was created by pg_dump from PostgreSQL %d or newer, but the server's pg_restore is version %s and cannot read it; upgrade the postgresql-client package to version %d or newer and retry the import", major, localVersion, major)
|
||||
return common.NewErrorf("This backup was created by pg_dump from PostgreSQL %d or newer, but the server's pg_restore is version %s and cannot read it; run 'x-ui pgclient %d' on the server (or upgrade the postgresql-client package to version %d or newer), then retry the import", major, localVersion, major, major)
|
||||
}
|
||||
return common.NewErrorf("This backup was created by a newer pg_dump than the server's pg_restore (version %s) can read; upgrade the postgresql-client package and retry the import", localVersion)
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ func TestPgRestoreReadFailureError(t *testing.T) {
|
||||
name: "dump from postgres 17 on older client",
|
||||
probeOutput: "pg_restore: error: unsupported version (1.16) in file header",
|
||||
localVersion: "16.4",
|
||||
want: "This backup was created by pg_dump from PostgreSQL 17 or newer, but the server's pg_restore is version 16.4 and cannot read it; upgrade the postgresql-client package to version 17 or newer and retry the import",
|
||||
want: "This backup was created by pg_dump from PostgreSQL 17 or newer, but the server's pg_restore is version 16.4 and cannot read it; run 'x-ui pgclient 17' on the server (or upgrade the postgresql-client package to version 17 or newer), then retry the import",
|
||||
},
|
||||
{
|
||||
name: "dump from postgres 16 on older client",
|
||||
probeOutput: "pg_restore: error: unsupported version (1.15) in file header",
|
||||
localVersion: "15.8",
|
||||
want: "This backup was created by pg_dump from PostgreSQL 16 or newer, but the server's pg_restore is version 15.8 and cannot read it; upgrade the postgresql-client package to version 16 or newer and retry the import",
|
||||
want: "This backup was created by pg_dump from PostgreSQL 16 or newer, but the server's pg_restore is version 15.8 and cannot read it; run 'x-ui pgclient 16' on the server (or upgrade the postgresql-client package to version 16 or newer), then retry the import",
|
||||
},
|
||||
{
|
||||
name: "archive version newer than any known mapping",
|
||||
@@ -31,7 +31,7 @@ func TestPgRestoreReadFailureError(t *testing.T) {
|
||||
name: "local version could not be determined",
|
||||
probeOutput: "pg_restore: error: unsupported version (1.16) in file header",
|
||||
localVersion: "",
|
||||
want: "This backup was created by pg_dump from PostgreSQL 17 or newer, but the server's pg_restore is version unknown and cannot read it; upgrade the postgresql-client package to version 17 or newer and retry the import",
|
||||
want: "This backup was created by pg_dump from PostgreSQL 17 or newer, but the server's pg_restore is version unknown and cannot read it; run 'x-ui pgclient 17' on the server (or upgrade the postgresql-client package to version 17 or newer), then retry the import",
|
||||
},
|
||||
{
|
||||
name: "unrelated read failure passes through",
|
||||
|
||||
@@ -3012,6 +3012,104 @@ pg_ensure_client() {
|
||||
command -v pg_dump > /dev/null 2>&1 && command -v pg_restore > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# Prints the major version of the installed pg_restore, or nothing when absent.
|
||||
pg_client_major() {
|
||||
command -v pg_restore > /dev/null 2>&1 || return 1
|
||||
pg_restore --version 2> /dev/null | grep -oE '[0-9]+' | head -n 1
|
||||
}
|
||||
|
||||
# Installs or upgrades the PostgreSQL client tools (pg_dump/pg_restore) so their
|
||||
# major version is at least $1 (e.g. 17); with no argument any installed version
|
||||
# is accepted. Falls back to the official PostgreSQL package repository when the
|
||||
# distribution one is too old. Restoring a panel backup made by a newer pg_dump
|
||||
# needs this: x-ui pgclient <major>
|
||||
pg_upgrade_client() {
|
||||
local want="$1" have
|
||||
if [[ -n "$want" && ! "$want" =~ ^[0-9]+$ ]]; then
|
||||
LOGE "Invalid PostgreSQL major version '${want}' (expected a number like 17)."
|
||||
return 1
|
||||
fi
|
||||
have=$(pg_client_major)
|
||||
if [[ -n "$have" ]]; then
|
||||
if [[ -z "$want" || "$have" -ge "$want" ]]; then
|
||||
LOGI "PostgreSQL client tools are already installed (version ${have})."
|
||||
return 0
|
||||
fi
|
||||
LOGI "Installed PostgreSQL client tools are version ${have}; version ${want} or newer is required."
|
||||
fi
|
||||
if [[ "${running_in_docker}" == "true" ]]; then
|
||||
LOGI "Note: packages installed inside the container are lost when the container is recreated."
|
||||
fi
|
||||
case "${release}" in
|
||||
ubuntu | debian | armbian)
|
||||
apt-get update >&2 || return 1
|
||||
if [[ -z "$want" ]]; then
|
||||
apt-get install -y -q postgresql-client >&2 || return 1
|
||||
elif ! apt-get install -y -q "postgresql-client-${want}" >&2; then
|
||||
LOGI "postgresql-client-${want} is not in the distribution repositories; adding the official PostgreSQL apt repository..."
|
||||
apt-get install -y -q postgresql-common ca-certificates >&2 || return 1
|
||||
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y >&2 || return 1
|
||||
apt-get install -y -q "postgresql-client-${want}" >&2 || return 1
|
||||
fi
|
||||
;;
|
||||
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol | centos)
|
||||
local pkg_mgr="dnf"
|
||||
command -v dnf > /dev/null 2>&1 || pkg_mgr="yum"
|
||||
if [[ -z "$want" ]]; then
|
||||
"$pkg_mgr" install -y -q postgresql >&2 || return 1
|
||||
elif ! "$pkg_mgr" install -y -q "postgresql${want}" >&2; then
|
||||
local elver
|
||||
elver=$(rpm -E %rhel 2> /dev/null)
|
||||
if [[ ! "$elver" =~ ^[0-9]+$ ]]; then
|
||||
LOGE "Could not determine the Enterprise Linux release; install the PostgreSQL ${want} client tools manually."
|
||||
return 1
|
||||
fi
|
||||
LOGI "postgresql${want} is not in the enabled repositories; adding the official PostgreSQL yum repository..."
|
||||
"$pkg_mgr" install -y "https://download.postgresql.org/pub/repos/yum/reporpms/EL-${elver}-$(uname -m)/pgdg-redhat-repo-latest.noarch.rpm" >&2 || return 1
|
||||
if [[ "$pkg_mgr" == "dnf" ]]; then
|
||||
dnf -qy module disable postgresql >&2 || true
|
||||
fi
|
||||
"$pkg_mgr" install -y -q "postgresql${want}" >&2 || return 1
|
||||
fi
|
||||
;;
|
||||
arch | manjaro | parch)
|
||||
pacman -Sy --noconfirm postgresql >&2 || return 1
|
||||
;;
|
||||
opensuse-tumbleweed | opensuse-leap)
|
||||
if [[ -z "$want" ]] || ! zypper -q install -y "postgresql${want}" >&2; then
|
||||
zypper -q install -y postgresql >&2 || return 1
|
||||
fi
|
||||
;;
|
||||
alpine)
|
||||
if [[ -z "$want" ]] || ! apk add --no-cache "postgresql${want}-client" >&2; then
|
||||
apk add --no-cache postgresql-client >&2 || return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
LOGE "Unsupported OS '${release}'; install the PostgreSQL client tools manually."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
hash -r 2> /dev/null
|
||||
have=$(pg_client_major)
|
||||
if [[ -n "$want" && ( -z "$have" || "$have" -lt "$want" ) && -x "/usr/pgsql-${want}/bin/pg_restore" ]]; then
|
||||
ln -sf "/usr/pgsql-${want}/bin/pg_dump" /usr/local/bin/pg_dump
|
||||
ln -sf "/usr/pgsql-${want}/bin/pg_restore" /usr/local/bin/pg_restore
|
||||
hash -r 2> /dev/null
|
||||
have=$(pg_client_major)
|
||||
fi
|
||||
if [[ -z "$have" ]]; then
|
||||
LOGE "pg_dump/pg_restore are still unavailable after installation."
|
||||
return 1
|
||||
fi
|
||||
if [[ -n "$want" && "$have" -lt "$want" ]]; then
|
||||
LOGE "PostgreSQL client tools are version ${have} after installation but ${want} or newer is required; install them manually."
|
||||
return 1
|
||||
fi
|
||||
LOGI "PostgreSQL client tools are ready (version ${have})."
|
||||
return 0
|
||||
}
|
||||
|
||||
# Writes XUI_DB_TYPE/XUI_DB_DSN into the service env file, preserving other entries.
|
||||
pg_write_env() {
|
||||
local dsn="$1" envfile
|
||||
@@ -3122,6 +3220,7 @@ postgresql_menu() {
|
||||
echo -e "${green}\t7.${plain} ${green}Enable${plain} Autostart on boot"
|
||||
echo -e "${green}\t8.${plain} View PostgreSQL Log"
|
||||
echo -e "${green}\t9.${plain} Convert SQLite ${green}.db <-> .dump${plain}"
|
||||
echo -e "${green}\t10.${plain} Install/Upgrade client tools (pg_dump/pg_restore)"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -rp "Choose an option: " choice
|
||||
case "$choice" in
|
||||
@@ -3164,6 +3263,11 @@ postgresql_menu() {
|
||||
migrate_db_prompt
|
||||
postgresql_menu
|
||||
;;
|
||||
10)
|
||||
read -rp "Required PostgreSQL major version (empty = any): " pg_client_ver
|
||||
pg_upgrade_client "$pg_client_ver"
|
||||
postgresql_menu
|
||||
;;
|
||||
*)
|
||||
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||
postgresql_menu
|
||||
@@ -3283,6 +3387,7 @@ show_usage() {
|
||||
│ ${blue}x-ui update-dev${plain} - Update to Dev channel (latest) │
|
||||
│ ${blue}x-ui update-all-geofiles${plain} - Update all geo files │
|
||||
│ ${blue}x-ui migrateDB [file]${plain} - Convert .db <-> .dump (SQLite) │
|
||||
│ ${blue}x-ui pgclient [ver]${plain} - Upgrade pg_dump/pg_restore tools │
|
||||
│ ${blue}x-ui legacy${plain} - Legacy version │
|
||||
│ ${blue}x-ui install${plain} - Install │
|
||||
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||
@@ -3486,6 +3591,9 @@ if [[ $# > 0 ]]; then
|
||||
"migrateDB")
|
||||
migrate_db "$2" "$3"
|
||||
;;
|
||||
"pgclient")
|
||||
pg_upgrade_client "$2"
|
||||
;;
|
||||
*) show_usage ;;
|
||||
esac
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user