ade8e76a68
- replace color palette with Cyberpunk 2077 neon variants in lib/common.sh - add DIM, BLINK, REV attributes and show_title ui helper - make show_separator terminal-width-aware via tput cols - restyle menus with ◢ numbered items and ▸_ prompt indicator - support two-digit input aliases (01-05, 00) alongside single-digit - update status tokens: [✓]/[✗] → [OK]/[KO], PURPLE → MAGENTA - apply consistent ui changes to lxs.sh and tools/index.sh
187 lines
7.2 KiB
Bash
187 lines
7.2 KiB
Bash
#!/bin/bash
|
|
|
|
# LXS - Common library
|
|
# Sourced by lxs.sh and all sub-scripts. Provides colors, UI helpers, loggers,
|
|
# spinner, OS guards, and shared utilities (public IP, password generation,
|
|
# apt lock wait, root re-exec, apt non-interactive setup).
|
|
# Repo: https://git.hyko.cx/hykocx/lxs
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# Colors — Cyberpunk 2077 neon palette
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
RED='\033[1;91m' # ICE alert
|
|
GREEN='\033[1;92m' # phosphor green
|
|
YELLOW='\033[1;93m' # signature electric yellow
|
|
MAGENTA='\033[1;95m' # hot pink neon
|
|
CYAN='\033[1;96m' # neon cyan
|
|
WHITE='\033[1;97m' # bold white
|
|
GRAY='\033[0;90m' # dark gray
|
|
PURPLE='\033[1;95m' # alias on MAGENTA for legacy sub-scripts
|
|
NC='\033[0m'
|
|
BOLD='\033[1m'
|
|
DIM='\033[2m'
|
|
BLINK='\033[5m'
|
|
REV='\033[7m'
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# UI helpers
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
show_separator() {
|
|
local cols
|
|
cols=$(tput cols 2>/dev/null || echo 64)
|
|
printf "${YELLOW}"
|
|
printf '▀%.0s' $(seq 1 "$cols")
|
|
printf "${NC}\n"
|
|
}
|
|
|
|
show_title() {
|
|
local title="$1"
|
|
local subtitle="${2:-SYS_MODULE}"
|
|
echo ""
|
|
echo -e "${YELLOW}${REV} ${title} ${NC} ${MAGENTA}// ${subtitle}${NC}"
|
|
show_separator
|
|
}
|
|
|
|
info() { echo -e "${MAGENTA}[··]${NC} $*"; }
|
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[!!]${NC} $*"; }
|
|
err() { echo -e "${RED}[KO]${NC} $*" >&2; }
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# Spinner — runs a shell command, redirects output to a log file, shows
|
|
# a spinner with a label, prints ✓/✗ on completion. Returns the command's
|
|
# exit code.
|
|
#
|
|
# Usage:
|
|
# run_spinner "Installing foo..." "apt-get install -y foo"
|
|
#
|
|
# Log file: ${LXS_LOG_FILE:-/tmp/lxs.log}
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
run_spinner() {
|
|
local message=$1
|
|
shift
|
|
local command="$*"
|
|
local spinstr='|/-\'
|
|
local log="${LXS_LOG_FILE:-/tmp/lxs.log}"
|
|
|
|
echo -e "${MAGENTA}[··] ${message}${NC}"
|
|
eval "$command" > "$log" 2>&1 &
|
|
local pid=$!
|
|
|
|
while kill -0 "$pid" 2>/dev/null; do
|
|
local temp=${spinstr#?}
|
|
printf "\r${MAGENTA}[%c·]${NC} ${message}" "$spinstr"
|
|
spinstr=$temp${spinstr%"$temp"}
|
|
sleep 0.15
|
|
done
|
|
|
|
wait "$pid"
|
|
local exit_code=$?
|
|
if [ $exit_code -eq 0 ]; then
|
|
printf "\r${GREEN}[OK]${NC} ${message}\n"
|
|
else
|
|
printf "\r${RED}[KO]${NC} ${message}\n"
|
|
fi
|
|
return $exit_code
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# Guards
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
require_debian_ubuntu() {
|
|
if ! grep -qiE 'debian|ubuntu' /etc/os-release 2>/dev/null; then
|
|
err "This script supports Debian/Ubuntu only."
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Re-exec the current script via sudo if not already root. Preserves env and
|
|
# arguments. Call near the top of a sub-script:
|
|
# require_root "$0" "$@"
|
|
require_root() {
|
|
[ "$EUID" -eq 0 ] && return 0
|
|
local self=$1
|
|
shift
|
|
exec sudo -E "$self" "$@"
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# Network / random / apt helpers
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
get_public_ip() {
|
|
local ip=""
|
|
ip=$(curl -s --max-time 5 ifconfig.me 2>/dev/null) || \
|
|
ip=$(curl -s --max-time 5 icanhazip.com 2>/dev/null) || \
|
|
ip=$(curl -s --max-time 5 ipinfo.io/ip 2>/dev/null) || \
|
|
ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
|
echo "$ip"
|
|
}
|
|
|
|
# Generate a random alphanumeric string. Default length: 32.
|
|
generate_password() {
|
|
local length=${1:-32}
|
|
LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | head -c "$length"
|
|
}
|
|
|
|
# Configure apt/debconf for fully non-interactive runs. Safe to call multiple
|
|
# times. No-op when apt is absent.
|
|
apt_noninteractive() {
|
|
command -v apt >/dev/null 2>&1 || return 0
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export NEEDRESTART_MODE=a
|
|
export NEEDRESTART_SUSPEND=1
|
|
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections 2>/dev/null || true
|
|
}
|
|
|
|
# Add a UFW allow rule, but only if UFW is installed AND active. No-op
|
|
# otherwise — so app installers can declare the ports they need without
|
|
# forcing UFW on hosts that don't use it.
|
|
#
|
|
# Usage:
|
|
# ufw_allow 8000/tcp "Coolify dashboard"
|
|
# ufw_allow 80/tcp
|
|
ufw_allow() {
|
|
command -v ufw >/dev/null 2>&1 || return 0
|
|
ufw status 2>/dev/null | grep -q "Status: active" || return 0
|
|
local rule=$1 comment=${2:-}
|
|
if [ -n "$comment" ]; then
|
|
ufw allow "$rule" comment "$comment" >/dev/null
|
|
else
|
|
ufw allow "$rule" >/dev/null
|
|
fi
|
|
ok "UFW :: allowed ${rule}${comment:+ (${comment})}"
|
|
}
|
|
|
|
# Wait for other apt/dpkg processes to release their locks. Up to 120s.
|
|
wait_for_apt() {
|
|
command -v apt >/dev/null 2>&1 || return 0
|
|
|
|
local max_wait=120
|
|
local wait_count=0
|
|
local lock_detected=false
|
|
|
|
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \
|
|
fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \
|
|
fuser /var/lib/apt/lists/lock >/dev/null 2>&1; do
|
|
|
|
[ "$lock_detected" = false ] && lock_detected=true && \
|
|
echo -e "${YELLOW}[!!] Waiting for other package manager to finish...${NC}"
|
|
|
|
wait_count=$((wait_count + 1))
|
|
[ $wait_count -ge $max_wait ] && \
|
|
echo -e "${RED}[KO] Timeout waiting for package manager${NC}" && return 1
|
|
|
|
printf "\r${GRAY}[··] Waiting... (%ds/%ds)${NC}" $wait_count $max_wait
|
|
sleep 1
|
|
done
|
|
|
|
[ "$lock_detected" = true ] && \
|
|
echo -e "\n${GREEN}[OK] Package manager is now available${NC}"
|
|
return 0
|
|
}
|