162 lines
6.1 KiB
Bash
162 lines
6.1 KiB
Bash
#!/usr/bin/env bash
|
||
set -Eeuo pipefail
|
||
|
||
readonly REPO_RAW_BASE="https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main"
|
||
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||
|
||
declare -a STEP_FILES=(
|
||
"ochenstarik-server-1.sh"
|
||
"ochenstarik-server-2.sh"
|
||
"ochenstarik-server-user-3.sh"
|
||
"ochenstarik-server-tg-4.sh"
|
||
"ochenstarik-server-vpn-5.sh"
|
||
"ochenstarik-server-panel-warp-6.sh"
|
||
"ochenstarik-server-backup-7.sh"
|
||
)
|
||
|
||
declare -a STEP_TITLES=(
|
||
"Часовой пояс, язык терминала, набор программ и swap"
|
||
"Базовые пакеты, будущий SSH-порт, IPv4/IPv6 и UFW"
|
||
"Администратор, перенос SSH и fail2ban"
|
||
"Telegram-уведомления о входах по SSH"
|
||
"Системный VPN через Xray"
|
||
"Панель 3x-ui и Cloudflare WARP"
|
||
"Первичный снимок и расписания резервного копирования"
|
||
)
|
||
|
||
TEMP_FILE=""
|
||
|
||
log() { printf '[+] %s\n' "$*"; }
|
||
warn() { printf '[!] %s\n' "$*" >&2; }
|
||
die() { printf '[x] %s\n' "$*" >&2; exit 1; }
|
||
|
||
cleanup() {
|
||
if [[ -n "$TEMP_FILE" && "$TEMP_FILE" == "$SCRIPT_DIR"/.ochenstarik-server-* \
|
||
&& -f "$TEMP_FILE" && ! -L "$TEMP_FILE" ]]; then
|
||
rm -f -- "$TEMP_FILE"
|
||
fi
|
||
}
|
||
trap cleanup EXIT
|
||
|
||
ensure_step_script() {
|
||
local filename="$1" result_variable="$2" target
|
||
target="${SCRIPT_DIR}/${filename}"
|
||
|
||
if [[ -e "$target" ]]; then
|
||
[[ -f "$target" && ! -L "$target" ]] \
|
||
|| die "Отказ от запуска: $target должен быть обычным файлом"
|
||
else
|
||
command -v curl >/dev/null 2>&1 \
|
||
|| die "Не найден curl: установите его командой apt-get install -y curl ca-certificates"
|
||
[[ -d "$SCRIPT_DIR" && ! -L "$SCRIPT_DIR" && -w "$SCRIPT_DIR" ]] \
|
||
|| die "Каталог $SCRIPT_DIR недоступен для безопасной загрузки"
|
||
|
||
log "Файл ${filename} отсутствует; загружаю его из основного репозитория"
|
||
TEMP_FILE="$(mktemp "${SCRIPT_DIR}/.ochenstarik-server-download.XXXXXX")"
|
||
chmod 600 "$TEMP_FILE"
|
||
curl -fL --retry 5 --retry-delay 5 --connect-timeout 30 \
|
||
--proto '=https' --tlsv1.2 "${REPO_RAW_BASE}/${filename}" -o "$TEMP_FILE"
|
||
bash -n "$TEMP_FILE" || die "Загруженный файл ${filename} не прошёл проверку Bash"
|
||
chmod 700 "$TEMP_FILE"
|
||
mv -- "$TEMP_FILE" "$target"
|
||
TEMP_FILE=""
|
||
fi
|
||
|
||
chmod 700 "$target"
|
||
bash -n "$target" || die "Синтаксическая ошибка в $target"
|
||
printf -v "$result_variable" '%s' "$target"
|
||
}
|
||
|
||
choose_step_action() {
|
||
local step_number="$1" title="$2" result_variable="$3" answer
|
||
while :; do
|
||
printf '\n============================================================\n'
|
||
printf 'Этап %s из %s: %s\n' "$step_number" "${#STEP_FILES[@]}" "$title"
|
||
printf ' 1) Установить / запустить этот этап\n'
|
||
printf ' 2) Пропустить и перейти к следующему\n'
|
||
printf ' 3) Завершить мастер\n'
|
||
read -rp 'Выберите действие: ' answer || die "Ввод прерван"
|
||
case "$answer" in
|
||
1|2|3) printf -v "$result_variable" '%s' "$answer"; return 0 ;;
|
||
*) warn "Введите 1, 2 или 3" ;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
choose_after_failure() {
|
||
local result_variable="$1" answer
|
||
while :; do
|
||
printf '\nЭтап завершился с ошибкой.\n'
|
||
printf ' 1) Запустить этот этап повторно\n'
|
||
printf ' 2) Пропустить и перейти к следующему\n'
|
||
printf ' 3) Завершить мастер\n'
|
||
read -rp 'Выберите действие: ' answer || die "Ввод прерван"
|
||
case "$answer" in
|
||
1|2|3) printf -v "$result_variable" '%s' "$answer"; return 0 ;;
|
||
*) warn "Введите 1, 2 или 3" ;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
print_summary() {
|
||
local completed="$1" skipped="$2"
|
||
printf '\n============================================================\n'
|
||
printf 'Мастер установки завершён.\n'
|
||
printf 'Успешно выполнено этапов: %s\n' "$completed"
|
||
printf 'Пропущено этапов: %s\n' "$skipped"
|
||
printf '\nМастер можно запустить повторно: каждый этап поддерживает повторную настройку.\n'
|
||
printf 'Для полного сброса используйте ochenstarik-server-uninstall.sh.\n'
|
||
}
|
||
|
||
[[ "$EUID" -eq 0 ]] || die "Запустите мастер от имени root: sudo ./ochenstarik-server-install.sh"
|
||
((${#STEP_FILES[@]} == ${#STEP_TITLES[@]})) || die "Некорректное описание этапов"
|
||
|
||
cat <<'INTRO'
|
||
|
||
Единый мастер установки lightweight-server
|
||
|
||
Этапы будут показаны по порядку. Каждый из них можно выполнить, пропустить
|
||
или завершить весь мастер. Не закрывайте текущую SSH-сессию во время изменения
|
||
порта SSH и проверяйте новый вход во втором терминале.
|
||
INTRO
|
||
|
||
completed=0
|
||
skipped=0
|
||
|
||
for index in "${!STEP_FILES[@]}"; do
|
||
step_number="$((index + 1))"
|
||
action=""
|
||
choose_step_action "$step_number" "${STEP_TITLES[index]}" action
|
||
case "$action" in
|
||
2)
|
||
skipped="$((skipped + 1))"
|
||
continue
|
||
;;
|
||
3)
|
||
print_summary "$completed" "$skipped"
|
||
exit 0
|
||
;;
|
||
esac
|
||
|
||
script_path=""
|
||
ensure_step_script "${STEP_FILES[index]}" script_path
|
||
while :; do
|
||
log "Запуск этапа ${step_number}: ${STEP_TITLES[index]}"
|
||
if bash -- "$script_path"; then
|
||
completed="$((completed + 1))"
|
||
log "Этап ${step_number} успешно завершён"
|
||
break
|
||
fi
|
||
|
||
warn "Этап ${step_number} вернул ошибку"
|
||
failure_action=""
|
||
choose_after_failure failure_action
|
||
case "$failure_action" in
|
||
1) continue ;;
|
||
2) skipped="$((skipped + 1))"; break ;;
|
||
3) print_summary "$completed" "$skipped"; exit 1 ;;
|
||
esac
|
||
done
|
||
done
|
||
|
||
print_summary "$completed" "$skipped"
|