#!/usr/bin/env bash # # Установка панели игрового хостинга в один клик. # Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом. # # Что делает скрипт: # 1. Ставит nginx, PHP 8.x (FPM), MariaDB # 2. Разворачивает код панели в /var/www/hostinpl # 3. Создаёт базу, пользователя и загружает схему # 4. Прописывает конфиг (пароль БД, адрес, токен планировщика) # 5. Настраивает планировщик (9 заданий) и сторож автозапуска # 6. Создаёт администратора панели # # Запуск: sudo bash install-panel.sh # set -euo pipefail C_OK=$'\033[1;32m'; C_ERR=$'\033[1;31m'; C_INF=$'\033[1;36m'; C_W=$'\033[1;33m'; C_0=$'\033[0m' step() { printf '%s»%s %s\n' "$C_INF" "$C_0" "$1"; } ok() { printf ' %s✓%s %s\n' "$C_OK" "$C_0" "$1"; } warn() { printf ' %s!%s %s\n' "$C_W" "$C_0" "$1"; } die() { printf '%s✗ %s%s\n' "$C_ERR" "$1" "$C_0" >&2; exit 1; } [[ $EUID -eq 0 ]] || die "Запустите скрипт от root: sudo bash $0" SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" [[ -d "$SRC/panel" ]] || die "Рядом со скриптом нет каталога panel/ — распакуйте репозиторий целиком" WEBROOT=/var/www/hostinpl DBNAME=hostin DBUSER=hostinpl PORT="${PORT:-80}" printf '\n%s=== Панель игрового хостинга · установка ===%s\n' "$C_INF" "$C_0" printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n' # ── 0. вопросы ─────────────────────────────────────────────────────────────── read -rp "Домен или IP панели (например panel.example.com): " DOMAIN [[ -n "$DOMAIN" ]] || die "Адрес не может быть пустым" read -rp "E-Mail администратора (он же логин в панель): " ADMMAIL [[ "$ADMMAIL" == *@*.* ]] || die "Похоже, это не e-mail" read -rsp "Пароль администратора (минимум 6 символов): " ADMPASS; echo [[ ${#ADMPASS} -ge 6 ]] || die "Пароль короче 6 символов" if [[ -e "$WEBROOT" ]]; then read -rp "Каталог $WEBROOT уже существует. Перезаписать код панели? (yes/no): " OW [[ "$OW" == "yes" ]] || die "Отменено пользователем" fi . /etc/os-release 2>/dev/null || true step "Система: ${PRETTY_NAME:-неизвестна}" # ── 1. пакеты ──────────────────────────────────────────────────────────────── step "Устанавливаю пакеты (nginx, PHP-FPM, MariaDB)…" export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq nginx mariadb-server unzip curl cron pwgen \ php-fpm php-mysql php-mbstring php-curl php-gd php-xml php-zip php-ssh2 >/dev/null PHPV="$(ls -1 /etc/php/ 2>/dev/null | sort -V | tail -1)" [[ -n "$PHPV" ]] || die "PHP не установился" ok "PHP $PHPV, $(nginx -v 2>&1 | sed 's/.*nginx\///')" # ── 2. службы (в контейнерах systemd может отсутствовать) ──────────────────── step "Запускаю MariaDB…" mkdir -p /run/mysqld && chown mysql:mysql /run/mysqld if ! mysqladmin ping >/dev/null 2>&1; then service mariadb start >/dev/null 2>&1 || /etc/init.d/mariadb start >/dev/null 2>&1 || true for _ in $(seq 1 45); do mysqladmin ping >/dev/null 2>&1 && break; sleep 2; done fi mysqladmin ping >/dev/null 2>&1 || die "MariaDB не поднялась — проверьте /var/log/mysql/" ok "MariaDB отвечает" # ── 3. база ────────────────────────────────────────────────────────────────── step "Создаю базу данных…" DBPASS="$(pwgen -cns -1 20)" mysql -e "CREATE DATABASE IF NOT EXISTS \`$DBNAME\` DEFAULT CHARSET utf8;" mysql -e "CREATE USER IF NOT EXISTS '$DBUSER'@'localhost' IDENTIFIED BY '$DBPASS'; ALTER USER '$DBUSER'@'localhost' IDENTIFIED BY '$DBPASS'; GRANT ALL PRIVILEGES ON \`$DBNAME\`.* TO '$DBUSER'@'localhost'; FLUSH PRIVILEGES;" ok "База $DBNAME, пользователь $DBUSER (права только на свою базу)" # ── 4. файлы ───────────────────────────────────────────────────────────────── step "Копирую код панели в $WEBROOT…" mkdir -p "$WEBROOT" cp -a "$SRC/panel/." "$WEBROOT/" if [[ -f "$WEBROOT/hostinpl.sql" ]]; then TABLES="$(mysql -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DBNAME'")" if [[ "$TABLES" -lt 5 ]]; then mysql "$DBNAME" < "$WEBROOT/hostinpl.sql" ok "Схема загружена: $(mysql -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DBNAME'") таблиц" else warn "В базе уже $TABLES таблиц — схему не перезаписываю" fi rm -f "$WEBROOT/hostinpl.sql" fi # ── 5. конфиг ──────────────────────────────────────────────────────────────── step "Настраиваю конфигурацию…" CRONTOKEN="$(pwgen -cns -1 24)" CFG="$WEBROOT/application/config.php" PROTO=http; [[ "$PORT" == "443" ]] && PROTO=https URL="$PROTO://$DOMAIN/"; [[ "$PORT" != "80" && "$PORT" != "443" ]] && URL="http://$DOMAIN:$PORT/" # Значения подставляются через callback, а не строкой замены: иначе символы вида $1 # в сгенерированном пароле PHP принял бы за обратную ссылку и записал бы мусор. php -r ' $f=$argv[1];$s=file_get_contents($f); $set=function($s,$key,$val,$anchor=false){ $re="~".($anchor?"^":"")."\x27".preg_quote($key,"~")."\x27(\s*)=>(\s*)\x27[^\x27]*\x27~u".($anchor?"m":""); return preg_replace_callback($re,function($m)use($key,$val){ return "\x27".$key."\x27".$m[1]."=>".$m[2]."\x27".$val."\x27"; },$s,1); }; $s=$set($s,"db_username",$argv[2]); $s=$set($s,"db_password",$argv[3]); $s=$set($s,"url",$argv[4]); $s=$set($s,"token",$argv[5],true); file_put_contents($f,$s);' "$CFG" "$DBUSER" "$DBPASS" "$URL" "$CRONTOKEN" php -l "$CFG" >/dev/null || die "Конфиг повреждён — установка прервана" grep -q "'db_password' => *'$DBPASS'" "$CFG" || grep -q "$DBPASS" "$CFG" || die "Пароль БД не попал в конфиг" umask 077; printf 'db_user=%s\ndb_pass=%s\ncron_token=%s\n' "$DBUSER" "$DBPASS" "$CRONTOKEN" > /root/.redl-panel-credentials ok "Конфиг записан, креды сохранены в /root/.redl-panel-credentials" # ── 6. права ───────────────────────────────────────────────────────────────── step "Выставляю права…" chown -R www-data:www-data "$WEBROOT" find "$WEBROOT" -type d -exec chmod 750 {} + find "$WEBROOT" -type f -exec chmod 640 {} + chmod -R 770 "$WEBROOT/tmp" ok "Владелец www-data, конфиг недоступен извне" # ── 7. PHP ─────────────────────────────────────────────────────────────────── step "Настраиваю PHP $PHPV…" INI="/etc/php/$PHPV/fpm/php.ini" sed -i 's/^short_open_tag = Off/short_open_tag = On/' "$INI" sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 90M/' "$INI" sed -i 's/^post_max_size = .*/post_max_size = 360M/' "$INI" sed -i 's|^;\?date.timezone =.*|date.timezone = Europe/Moscow|' "$INI" mkdir -p /run/php service "php$PHPV-fpm" restart >/dev/null 2>&1 || /etc/init.d/"php$PHPV-fpm" restart >/dev/null 2>&1 || true sleep 2 [[ -S "/run/php/php$PHPV-fpm.sock" ]] || die "PHP-FPM сокет не создан" ok "short_open_tag включён (панель использует ), загрузка до 90 МБ" # ── 8. nginx ───────────────────────────────────────────────────────────────── step "Настраиваю nginx (порт $PORT)…" cat > /etc/nginx/sites-available/hostinpl </dev/null 2>&1 || { nginx -t; die "Конфиг nginx не прошёл проверку"; } service nginx restart >/dev/null 2>&1 || /etc/init.d/nginx restart >/dev/null 2>&1 || nginx sleep 1 ok "Сайт слушает 0.0.0.0:$PORT" # ── 9. планировщик ─────────────────────────────────────────────────────────── step "Настраиваю планировщик…" B="http://127.0.0.1:$PORT/main/cron" { echo 'SHELL=/bin/sh' echo 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin' echo "0 0 * * * root curl -s -m 300 '$B/index?token=$CRONTOKEN' >/dev/null 2>&1" echo "* * * * * root curl -s -m 55 '$B/gameServers?token=$CRONTOKEN' >/dev/null 2>&1" echo "* * * * * root curl -s -m 55 '$B/tasks?token=$CRONTOKEN' >/dev/null 2>&1" echo "*/10 * * * * root curl -s -m 300 '$B/serverReloader?token=$CRONTOKEN' >/dev/null 2>&1" echo "*/30 * * * * root curl -s -m 300 '$B/stopServers?token=$CRONTOKEN' >/dev/null 2>&1" echo "*/30 * * * * root curl -s -m 300 '$B/stopServersQuery?token=$CRONTOKEN' >/dev/null 2>&1" echo "0 * * * * root curl -s -m 300 '$B/updateStats?token=$CRONTOKEN' >/dev/null 2>&1" echo "0 * * * * root curl -s -m 300 '$B/updateStatsLocations?token=$CRONTOKEN' >/dev/null 2>&1" echo "0 3 * * 0 root curl -s -m 300 '$B/clearLogs?token=$CRONTOKEN' >/dev/null 2>&1" } > /etc/cron.d/hostinpl chmod 644 /etc/cron.d/hostinpl service cron start >/dev/null 2>&1 || /etc/init.d/cron start >/dev/null 2>&1 || true ok "9 заданий панели (статистика, задачи, автоотключение просрочки)" # ── 10. сторож автозапуска (нужен там, где нет systemd) ────────────────────── step "Ставлю сторож автозапуска…" cat > /usr/local/bin/hostinpl-guard </dev/null 2>&1 || pgrep -x mysqld >/dev/null 2>&1 || { mkdir -p /run/mysqld; chown mysql:mysql /run/mysqld service mariadb start >/dev/null 2>&1 || /etc/init.d/mariadb start >/dev/null 2>&1 } pgrep -f "^php-fpm$PHPV: master" >/dev/null 2>&1 || service php$PHPV-fpm start >/dev/null 2>&1 curl -sf -o /dev/null http://127.0.0.1:$PORT/account/login || service nginx start >/dev/null 2>&1 pgrep -x cron >/dev/null 2>&1 || service cron start >/dev/null 2>&1 GUARD chmod +x /usr/local/bin/hostinpl-guard cat > /etc/cron.d/hostinpl-guard <<'G' SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin * * * * * root /usr/local/bin/hostinpl-guard >/dev/null 2>&1 @reboot root sleep 20; /usr/local/bin/hostinpl-guard >/dev/null 2>&1 G chmod 644 /etc/cron.d/hostinpl-guard ok "Проверка раз в минуту и подъём после перезагрузки" # ── 11. администратор ──────────────────────────────────────────────────────── step "Создаю администратора панели…" HASH="$(php -r 'echo md5($argv[1]);' "$ADMPASS")" mysql "$DBNAME" <