mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
feat(x-ui.sh): add PostgreSQL management menu
Add a self-contained 'PostgreSQL Management' submenu (main-menu option 27) so the panel can be set up and migrated without re-running the remote install script: - Install PostgreSQL locally (server + client tools + dedicated xui user/db), ported from install.sh so x-ui.sh stays standalone - Migrate SQLite to PostgreSQL via 'x-ui migrate-db', then write XUI_DB_TYPE/XUI_DB_DSN to the service env file and restart the panel; client tools are ensured first so in-panel backup/restore works for local and external databases - Service control: status (clusters + port 5432), start, stop, restart, enable autostart, view log, with auto-detected cluster version
This commit is contained in:
parent
5b9ed34009
commit
47d9b49666
1 changed files with 408 additions and 2 deletions
410
x-ui.sh
410
x-ui.sh
|
|
@ -2282,6 +2282,407 @@ SSH_port_forwarding() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# PostgreSQL service management (for panels configured with XUI_DB_TYPE=postgres).
|
||||||
|
|
||||||
|
postgresql_installed() {
|
||||||
|
command -v pg_lsclusters > /dev/null 2>&1 || command -v psql > /dev/null 2>&1 || command -v postgres > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prints "VER CLUSTER" of the first configured cluster on Debian-style installs (e.g. "16 main").
|
||||||
|
pg_cluster_info() {
|
||||||
|
if command -v pg_lsclusters > /dev/null 2>&1; then
|
||||||
|
pg_lsclusters 2> /dev/null | awk '$1 ~ /^[0-9]+$/ {print $1, $2; exit}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolves the systemd unit used to manage the PostgreSQL server.
|
||||||
|
pg_systemd_unit() {
|
||||||
|
local info ver cluster
|
||||||
|
info="$(pg_cluster_info)"
|
||||||
|
if [[ -n "$info" ]]; then
|
||||||
|
ver="${info%% *}"
|
||||||
|
cluster="${info##* }"
|
||||||
|
echo "postgresql@${ver}-${cluster}"
|
||||||
|
else
|
||||||
|
echo "postgresql"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_status() {
|
||||||
|
if ! postgresql_installed; then
|
||||||
|
LOGE "PostgreSQL does not appear to be installed on this system."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if command -v pg_lsclusters > /dev/null 2>&1; then
|
||||||
|
pg_lsclusters
|
||||||
|
else
|
||||||
|
systemctl status "$(pg_systemd_unit)" --no-pager
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
if command -v ss > /dev/null 2>&1; then
|
||||||
|
local listening
|
||||||
|
listening=$(ss -ltnp 2> /dev/null | grep ':5432')
|
||||||
|
if [[ -n "$listening" ]]; then
|
||||||
|
echo -e "${green}PostgreSQL is listening on port 5432:${plain}"
|
||||||
|
echo "$listening"
|
||||||
|
else
|
||||||
|
echo -e "${red}Nothing is listening on port 5432 - the database is not running.${plain}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_start() {
|
||||||
|
pg_require_installed || return 1
|
||||||
|
if [[ $release == "alpine" ]]; then
|
||||||
|
rc-service postgresql start
|
||||||
|
else
|
||||||
|
systemctl start "$(pg_systemd_unit)"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
postgresql_status
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_stop() {
|
||||||
|
pg_require_installed || return 1
|
||||||
|
if [[ $release == "alpine" ]]; then
|
||||||
|
rc-service postgresql stop
|
||||||
|
else
|
||||||
|
systemctl stop "$(pg_systemd_unit)"
|
||||||
|
fi
|
||||||
|
LOGI "PostgreSQL stop signal sent."
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_restart() {
|
||||||
|
pg_require_installed || return 1
|
||||||
|
if [[ $release == "alpine" ]]; then
|
||||||
|
rc-service postgresql restart
|
||||||
|
else
|
||||||
|
systemctl restart "$(pg_systemd_unit)"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
postgresql_status
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_enable() {
|
||||||
|
pg_require_installed || return 1
|
||||||
|
if [[ $release == "alpine" ]]; then
|
||||||
|
rc-update add postgresql default
|
||||||
|
else
|
||||||
|
systemctl enable "$(pg_systemd_unit)"
|
||||||
|
fi
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
LOGI "PostgreSQL set to start automatically on boot."
|
||||||
|
else
|
||||||
|
LOGE "Failed to enable PostgreSQL autostart."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_log() {
|
||||||
|
pg_require_installed || return 1
|
||||||
|
local info ver cluster logfile
|
||||||
|
info="$(pg_cluster_info)"
|
||||||
|
if [[ -n "$info" ]]; then
|
||||||
|
ver="${info%% *}"
|
||||||
|
cluster="${info##* }"
|
||||||
|
logfile="/var/log/postgresql/postgresql-${ver}-${cluster}.log"
|
||||||
|
fi
|
||||||
|
if [[ -n "$logfile" && -f "$logfile" ]]; then
|
||||||
|
tail -n 40 "$logfile"
|
||||||
|
elif command -v journalctl > /dev/null 2>&1; then
|
||||||
|
journalctl -u "$(pg_systemd_unit)" -n 40 --no-pager
|
||||||
|
else
|
||||||
|
LOGE "No PostgreSQL log found."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
pg_require_installed() {
|
||||||
|
if ! postgresql_installed; then
|
||||||
|
LOGE "PostgreSQL is not installed. Use option 1 (Install PostgreSQL) in this menu first."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Installs a local PostgreSQL server and creates a dedicated xui user/database.
|
||||||
|
# Progress goes to stderr; on success the connection DSN is printed to stdout so
|
||||||
|
# callers can capture it. Mirrors install_postgres_local() from install.sh, so the
|
||||||
|
# panel can be set up without re-running the remote install script.
|
||||||
|
pg_install_local() {
|
||||||
|
local pg_user pg_pass pg_db pg_host pg_port
|
||||||
|
pg_pass=$(gen_random_string 24)
|
||||||
|
pg_db="xui"
|
||||||
|
pg_host="127.0.0.1"
|
||||||
|
pg_port="5432"
|
||||||
|
|
||||||
|
case "${release}" in
|
||||||
|
ubuntu | debian | armbian)
|
||||||
|
apt-get update >&2 && apt-get install -y -q postgresql >&2 || return 1
|
||||||
|
;;
|
||||||
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
|
dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1
|
||||||
|
[[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1
|
||||||
|
;;
|
||||||
|
centos)
|
||||||
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum install -y postgresql-server postgresql-contrib >&2 || return 1
|
||||||
|
else
|
||||||
|
dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1
|
||||||
|
fi
|
||||||
|
[[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1
|
||||||
|
;;
|
||||||
|
arch | manjaro | parch)
|
||||||
|
pacman -Syu --noconfirm postgresql >&2 || return 1
|
||||||
|
if [[ ! -f /var/lib/postgres/data/PG_VERSION ]]; then
|
||||||
|
sudo -u postgres initdb -D /var/lib/postgres/data >&2 || return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
opensuse-tumbleweed | opensuse-leap)
|
||||||
|
zypper -q install -y postgresql-server postgresql-contrib >&2 || return 1
|
||||||
|
if [[ ! -f /var/lib/pgsql/data/PG_VERSION ]]; then
|
||||||
|
install -d -o postgres -g postgres -m 700 /var/lib/pgsql/data >&2 || return 1
|
||||||
|
su - postgres -c "initdb -D /var/lib/pgsql/data" >&2 || return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
alpine)
|
||||||
|
apk add --no-cache postgresql postgresql-contrib >&2 || return 1
|
||||||
|
if [[ ! -f /var/lib/postgresql/data/PG_VERSION ]]; then
|
||||||
|
/etc/init.d/postgresql setup >&2 || return 1
|
||||||
|
fi
|
||||||
|
rc-update add postgresql default >&2 2> /dev/null || true
|
||||||
|
rc-service postgresql start >&2 || return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Unsupported distro for automatic PostgreSQL install: ${release}${plain}" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "${release}" != "alpine" ]]; then
|
||||||
|
systemctl enable --now postgresql >&2 || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local i
|
||||||
|
for i in 1 2 3 4 5; do
|
||||||
|
sudo -u postgres psql -tAc 'SELECT 1' > /dev/null 2>&1 && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
local existing_owner=""
|
||||||
|
existing_owner=$(sudo -u postgres psql -tAc \
|
||||||
|
"SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_database WHERE datname='${pg_db}'" 2> /dev/null \
|
||||||
|
| tr -d '[:space:]')
|
||||||
|
if [[ -n "${existing_owner}" && "${existing_owner}" != "postgres" ]]; then
|
||||||
|
pg_user="${existing_owner}"
|
||||||
|
else
|
||||||
|
pg_user=$(gen_random_string 8)
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${pg_user}'" 2> /dev/null \
|
||||||
|
| grep -q 1 \
|
||||||
|
|| sudo -u postgres psql -c "CREATE USER \"${pg_user}\" WITH PASSWORD '${pg_pass}';" >&2 || return 1
|
||||||
|
|
||||||
|
sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${pg_db}'" 2> /dev/null \
|
||||||
|
| grep -q 1 \
|
||||||
|
|| sudo -u postgres psql -c "CREATE DATABASE \"${pg_db}\" OWNER \"${pg_user}\";" >&2 || return 1
|
||||||
|
|
||||||
|
sudo -u postgres psql -c "ALTER USER \"${pg_user}\" WITH PASSWORD '${pg_pass}';" >&2 || return 1
|
||||||
|
|
||||||
|
local pg_pass_enc
|
||||||
|
pg_pass_enc=$(printf '%s' "${pg_pass}" | sed -e 's/%/%25/g' -e 's/:/%3A/g' -e 's/@/%40/g' -e 's|/|%2F|g' -e 's/?/%3F/g' -e 's/#/%23/g')
|
||||||
|
|
||||||
|
echo "postgres://${pg_user}:${pg_pass_enc}@${pg_host}:${pg_port}/${pg_db}?sslmode=disable"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Installs the PostgreSQL client tools (pg_dump/pg_restore) used by in-panel backup.
|
||||||
|
pg_ensure_client() {
|
||||||
|
if command -v pg_dump > /dev/null 2>&1 && command -v pg_restore > /dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo -e "${yellow}Installing PostgreSQL client tools (pg_dump/pg_restore)...${plain}" >&2
|
||||||
|
case "${release}" in
|
||||||
|
ubuntu | debian | armbian)
|
||||||
|
apt-get update >&2 && apt-get install -y -q postgresql-client >&2 || return 1
|
||||||
|
;;
|
||||||
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
|
dnf install -y -q postgresql >&2 || return 1
|
||||||
|
;;
|
||||||
|
centos)
|
||||||
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum install -y postgresql >&2 || return 1
|
||||||
|
else
|
||||||
|
dnf install -y -q postgresql >&2 || return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
arch | manjaro | parch)
|
||||||
|
pacman -Sy --noconfirm postgresql >&2 || return 1
|
||||||
|
;;
|
||||||
|
opensuse-tumbleweed | opensuse-leap)
|
||||||
|
zypper -q install -y postgresql >&2 || return 1
|
||||||
|
;;
|
||||||
|
alpine)
|
||||||
|
apk add --no-cache postgresql-client >&2 || return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
command -v pg_dump > /dev/null 2>&1 && command -v pg_restore > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Writes XUI_DB_TYPE/XUI_DB_DSN into the service env file, preserving other entries.
|
||||||
|
pg_write_env() {
|
||||||
|
local dsn="$1" envfile
|
||||||
|
envfile="$(xui_env_file_path)"
|
||||||
|
install -d -m 755 "$(dirname "$envfile")"
|
||||||
|
touch "$envfile"
|
||||||
|
sed -i '/^XUI_DB_TYPE=/d; /^XUI_DB_DSN=/d' "$envfile"
|
||||||
|
{
|
||||||
|
echo "XUI_DB_TYPE=postgres"
|
||||||
|
echo "XUI_DB_DSN=${dsn}"
|
||||||
|
} >> "$envfile"
|
||||||
|
chmod 600 "$envfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
pg_install_server_action() {
|
||||||
|
if postgresql_installed; then
|
||||||
|
LOGI "PostgreSQL already appears to be installed on this system."
|
||||||
|
confirm "Run setup anyway (ensures the xui database/user exist)?" "n" || return 0
|
||||||
|
fi
|
||||||
|
LOGI "Installing PostgreSQL server and creating a dedicated user/database..."
|
||||||
|
local dsn
|
||||||
|
dsn=$(pg_install_local)
|
||||||
|
if [[ $? -ne 0 || -z "$dsn" ]]; then
|
||||||
|
LOGE "PostgreSQL installation failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
PG_LAST_DSN="$dsn"
|
||||||
|
pg_ensure_client || LOGE "Could not install pg_dump/pg_restore (panel DB backup may be unavailable)."
|
||||||
|
echo ""
|
||||||
|
LOGI "PostgreSQL is installed and ready."
|
||||||
|
echo -e "${green}Connection DSN:${plain} ${dsn}"
|
||||||
|
echo -e "${yellow}Use option 2 to migrate your SQLite data and switch the panel to PostgreSQL.${plain}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copies the current SQLite data into PostgreSQL, then switches the panel over.
|
||||||
|
migrate_to_postgres() {
|
||||||
|
if [[ ! -x "${xui_folder}/x-ui" ]]; then
|
||||||
|
LOGE "x-ui is not installed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo -e "${yellow}This copies your current SQLite data into a PostgreSQL database,${plain}"
|
||||||
|
echo -e "${yellow}then switches the panel to PostgreSQL and restarts it.${plain}"
|
||||||
|
echo -e "${yellow}The destination PostgreSQL database must be empty.${plain}"
|
||||||
|
confirm "Continue?" "n" || return 0
|
||||||
|
|
||||||
|
local dsn="" pg_mode
|
||||||
|
if [[ -n "$PG_LAST_DSN" ]]; then
|
||||||
|
echo -e "A PostgreSQL database was created in this session:"
|
||||||
|
echo -e " ${green}${PG_LAST_DSN}${plain}"
|
||||||
|
confirm "Migrate into this database?" "y" && dsn="$PG_LAST_DSN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$dsn" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${green}\t1.${plain} Install PostgreSQL locally and create a dedicated user/db (recommended)"
|
||||||
|
echo -e "${green}\t2.${plain} Use an existing PostgreSQL server (enter DSN)"
|
||||||
|
read -rp "Choose [1]: " pg_mode
|
||||||
|
pg_mode="${pg_mode:-1}"
|
||||||
|
if [[ "$pg_mode" == "2" ]]; then
|
||||||
|
while [[ -z "$dsn" ]]; do
|
||||||
|
read -rp "Enter PostgreSQL DSN (postgres://user:pass@host:port/dbname?sslmode=disable): " dsn
|
||||||
|
dsn="${dsn// /}"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
LOGI "Installing PostgreSQL locally (this may take a moment)..."
|
||||||
|
dsn=$(pg_install_local)
|
||||||
|
if [[ $? -ne 0 || -z "$dsn" ]]; then
|
||||||
|
LOGE "PostgreSQL installation failed. Aborting migration."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
PG_LAST_DSN="$dsn"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
pg_ensure_client || LOGE "Could not install pg_dump/pg_restore (in-panel DB backup/restore may be unavailable)."
|
||||||
|
|
||||||
|
LOGI "Stopping panel to take a consistent snapshot..."
|
||||||
|
stop 0 > /dev/null 2>&1
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
LOGI "Migrating data into PostgreSQL..."
|
||||||
|
if ! ${xui_folder}/x-ui migrate-db --dsn "$dsn"; then
|
||||||
|
LOGE "Migration failed. The panel was NOT switched to PostgreSQL."
|
||||||
|
start 0 > /dev/null 2>&1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pg_write_env "$dsn"
|
||||||
|
LOGI "Wrote database settings to $(xui_env_file_path) (XUI_DB_TYPE=postgres)."
|
||||||
|
LOGI "Restarting panel on PostgreSQL..."
|
||||||
|
restart 0
|
||||||
|
sleep 1
|
||||||
|
if check_status; then
|
||||||
|
LOGI "Migration complete. The panel is now running on PostgreSQL."
|
||||||
|
else
|
||||||
|
LOGE "Panel did not come up. Check logs (option 16). Your SQLite data is left intact."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
postgresql_menu() {
|
||||||
|
echo -e "${green}\t1.${plain} ${green}Install${plain} PostgreSQL (server + client + xui db)"
|
||||||
|
echo -e "${green}\t2.${plain} Migrate SQLite ${green}->${plain} PostgreSQL"
|
||||||
|
echo -e "${green}\t3.${plain} Status (clusters & port 5432)"
|
||||||
|
echo -e "${green}\t4.${plain} ${green}Start${plain} PostgreSQL"
|
||||||
|
echo -e "${green}\t5.${plain} ${red}Stop${plain} PostgreSQL"
|
||||||
|
echo -e "${green}\t6.${plain} Restart PostgreSQL"
|
||||||
|
echo -e "${green}\t7.${plain} ${green}Enable${plain} Autostart on boot"
|
||||||
|
echo -e "${green}\t8.${plain} View PostgreSQL Log"
|
||||||
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
|
read -rp "Choose an option: " choice
|
||||||
|
case "$choice" in
|
||||||
|
0)
|
||||||
|
show_menu
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
pg_install_server_action
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
migrate_to_postgres
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
postgresql_status
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
postgresql_start
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
postgresql_stop
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
postgresql_restart
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
7)
|
||||||
|
postgresql_enable
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
8)
|
||||||
|
postgresql_log
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
show_usage() {
|
show_usage() {
|
||||||
echo -e "┌────────────────────────────────────────────────────────────────┐
|
echo -e "┌────────────────────────────────────────────────────────────────┐
|
||||||
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
||||||
|
|
@ -2342,10 +2743,12 @@ show_menu() {
|
||||||
│ ${green}24.${plain} Enable BBR │
|
│ ${green}24.${plain} Enable BBR │
|
||||||
│ ${green}25.${plain} Update Geo Files │
|
│ ${green}25.${plain} Update Geo Files │
|
||||||
│ ${green}26.${plain} Speedtest by Ookla │
|
│ ${green}26.${plain} Speedtest by Ookla │
|
||||||
|
│────────────────────────────────────────────────│
|
||||||
|
│ ${green}27.${plain} PostgreSQL Management │
|
||||||
╚────────────────────────────────────────────────╝
|
╚────────────────────────────────────────────────╝
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -rp "Please enter your selection [0-26]: " num
|
echo && read -rp "Please enter your selection [0-27]: " num
|
||||||
|
|
||||||
case "${num}" in
|
case "${num}" in
|
||||||
0)
|
0)
|
||||||
|
|
@ -2429,8 +2832,11 @@ show_menu() {
|
||||||
26)
|
26)
|
||||||
run_speedtest
|
run_speedtest
|
||||||
;;
|
;;
|
||||||
|
27)
|
||||||
|
postgresql_menu
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
LOGE "Please enter the correct number [0-26]"
|
LOGE "Please enter the correct number [0-27]"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue