/
DATKAI
/
ITCUP
Обзор
Документация
Войти
/
DATKAI
/
ITCUP
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
deploy/deploy.sh
348 строк
15 KB
DATKAI
feat(tls): виден ход выполнения при включении/обновлении HTTPS
10 июл 2026, 20:58
10 июл 2026, 20:58
7aba5c4
Код
Авторство
О чём код?
#!/usr/bin/env bash # # ITCUP (ipcup) — автоматическое развёртывание на чистом Debian 13 (trixie). # Запускать от root внутри LXC/VM на Proxmox. # # Стек: PHP 8.4 + Laravel 13 + Filament 5 + PostgreSQL + Redis + Nginx # # Использование: # REPO_URL=git@gitverse.ru:DATKAI/ITCUP.git \ # APP_DOMAIN=itcup.local \ # DB_PASSWORD='смени-меня' \ # bash deploy.sh # set -euo pipefail ### ── Параметры (можно переопределить через переменные окружения) ────────────── REPO_URL="${REPO_URL:-git@gitverse.ru:DATKAI/ITCUP.git}" APP_DIR="${APP_DIR:-/var/www/itcup}" APP_DOMAIN="${APP_DOMAIN:-_}" # _ = любой хост (по IP) APP_USER="${APP_USER:-www-data}" BRANCH="${BRANCH:-main}" # HTTPS: internal — свой корневой CA (по умолчанию, для локальной сети), # letsencrypt — публичный сертификат (когда появится белый домен), # off — только HTTP (не рекомендуется) TLS_MODE="${TLS_MODE:-internal}" CERT_NAME="${CERT_NAME:-itcup.local}" # внутреннее имя в SAN-сертификате DETECTED_IP="$(hostname -I 2>/dev/null | awk '{print $1}')" CERT_IP="${CERT_IP:-$DETECTED_IP}" if [ "$TLS_MODE" = "off" ]; then APP_URL="${APP_URL:-http://${APP_DOMAIN}}" else APP_URL="${APP_URL:-https://$([ "$APP_DOMAIN" != "_" ] && echo "$APP_DOMAIN" || echo "$CERT_IP")}" fi DB_NAME="${DB_NAME:-itcup}" DB_USER="${DB_USER:-itcup}" DB_PASSWORD="${DB_PASSWORD:-itcup}" # Язык интерфейса: ru или en APP_LOCALE="${APP_LOCALE:-}" # Учётка администратора (создаётся автоматически) ADMIN_NAME="${ADMIN_NAME:-Администратор}" ADMIN_EMAIL="${ADMIN_EMAIL:-admin@itcup.local}" ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin}" PHP_VER="8.4" log(){ printf '\n\033[1;32m==> %s\033[0m\n' "$*"; } [ "$(id -u)" -eq 0 ] || { echo "Запусти от root"; exit 1; } ### ── Выбор языка ────────────────────────────────────────────────────────────── if [ -z "$APP_LOCALE" ]; then if [ -t 0 ]; then printf '\nВыберите язык интерфейса / Choose language:\n 1) Русский (по умолчанию)\n 2) English\nВвод [1]: ' read -r _lang_choice case "$_lang_choice" in 2|en|EN) APP_LOCALE="en" ;; *) APP_LOCALE="ru" ;; esac else APP_LOCALE="ru" fi fi [ "$APP_LOCALE" = "en" ] || APP_LOCALE="ru" ### ── 1. Системные пакеты ────────────────────────────────────────────────────── log "Обновление системы и установка пакетов" export DEBIAN_FRONTEND=noninteractive apt-get update -y apt-get upgrade -y apt-get install -y \ nginx postgresql redis-server git unzip curl ca-certificates gnupg acl openssl \ php${PHP_VER}-fpm php${PHP_VER}-cli \ php${PHP_VER}-pgsql php${PHP_VER}-redis php${PHP_VER}-ldap \ php${PHP_VER}-gd php${PHP_VER}-intl php${PHP_VER}-zip php${PHP_VER}-bcmath \ php${PHP_VER}-curl php${PHP_VER}-mbstring php${PHP_VER}-xml php${PHP_VER}-gmp ### ── 2. Composer ────────────────────────────────────────────────────────────── if ! command -v composer >/dev/null 2>&1; then log "Установка Composer" curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer fi ### ── 3. Node.js 22 (для сборки фронтенда Vite) ──────────────────────────────── if ! command -v node >/dev/null 2>&1; then log "Установка Node.js 22" curl -fsSL https://deb.nodesource.com/setup_22.x | bash - apt-get install -y nodejs fi ### ── 4. PostgreSQL: база и пользователь ─────────────────────────────────────── log "Настройка PostgreSQL" systemctl enable --now postgresql psql_su(){ runuser -u postgres -- psql "$@"; } psql_su -tc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" | grep -q 1 || \ psql_su -c "CREATE ROLE ${DB_USER} LOGIN PASSWORD '${DB_PASSWORD}';" psql_su -tc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" | grep -q 1 || \ psql_su -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" ### ── 5. Redis ───────────────────────────────────────────────────────────────── systemctl enable --now redis-server ### ── 6. Клонирование / обновление репозитория ───────────────────────────────── log "Получение кода: ${REPO_URL}" git config --global --add safe.directory "$APP_DIR" 2>/dev/null || true mkdir -p "$(dirname "$APP_DIR")" if [ -d "$APP_DIR/.git" ]; then git -C "$APP_DIR" fetch --all git -C "$APP_DIR" checkout "$BRANCH" git -C "$APP_DIR" pull --ff-only origin "$BRANCH" else git clone --branch "$BRANCH" "$REPO_URL" "$APP_DIR" fi cd "$APP_DIR" ### ── 7. .env ────────────────────────────────────────────────────────────────── if [ ! -f .env ]; then log "Создание .env" cp .env.example .env sed -i \ -e "s|^APP_ENV=.*|APP_ENV=production|" \ -e "s|^APP_DEBUG=.*|APP_DEBUG=false|" \ -e "s|^APP_URL=.*|APP_URL=${APP_URL}|" \ -e "s|^APP_LOCALE=.*|APP_LOCALE=${APP_LOCALE}|" \ -e "s|^APP_FALLBACK_LOCALE=.*|APP_FALLBACK_LOCALE=${APP_LOCALE}|" \ -e "s|^DB_CONNECTION=.*|DB_CONNECTION=pgsql|" \ -e "s|^# *DB_HOST=.*|DB_HOST=127.0.0.1|" \ -e "s|^# *DB_PORT=.*|DB_PORT=5432|" \ -e "s|^# *DB_DATABASE=.*|DB_DATABASE=${DB_NAME}|" \ -e "s|^# *DB_USERNAME=.*|DB_USERNAME=${DB_USER}|" \ -e "s|^# *DB_PASSWORD=.*|DB_PASSWORD=${DB_PASSWORD}|" \ -e "s|^CACHE_STORE=.*|CACHE_STORE=redis|" \ -e "s|^SESSION_DRIVER=.*|SESSION_DRIVER=redis|" \ -e "s|^QUEUE_CONNECTION=.*|QUEUE_CONNECTION=redis|" \ .env if [ "$TLS_MODE" != "off" ]; then grep -q '^SESSION_SECURE_COOKIE=' .env || echo 'SESSION_SECURE_COOKIE=true' >> .env fi fi # Язык всегда синхронизируем (в т.ч. при повторном запуске) sed -i \ -e "s|^APP_LOCALE=.*|APP_LOCALE=${APP_LOCALE}|" \ -e "s|^APP_FALLBACK_LOCALE=.*|APP_FALLBACK_LOCALE=${APP_LOCALE}|" \ .env ### ── 8. Зависимости и сборка ────────────────────────────────────────────────── log "composer install" composer install --no-dev --optimize-autoloader --no-interaction grep -q '^APP_KEY=.\+' .env || php artisan key:generate --force log "npm build" npm ci npm run build ### ── 9. Миграции и оптимизация ──────────────────────────────────────────────── log "Миграции БД" php artisan migrate --force --seed ### ── 9a. Автосоздание администратора ────────────────────────────────────────── log "Создание/обновление администратора: ${ADMIN_EMAIL}" php artisan itcup:make-admin "${ADMIN_EMAIL}" --password="${ADMIN_PASSWORD}" --name="${ADMIN_NAME}" php artisan storage:link || true php artisan config:cache php artisan route:cache php artisan view:cache ### ── 10. Права ──────────────────────────────────────────────────────────────── log "Права доступа" chown -R "${APP_USER}:${APP_USER}" "$APP_DIR" chmod -R 775 "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" ### ── 10a. Доступ www-data к git/ssh (для обновления из панели) ───────────────── log "Настройка SSH/git для пользователя приложения (${APP_USER})" APP_HOME="$(getent passwd "$APP_USER" | cut -d: -f6)" APP_HOME="${APP_HOME:-/var/www}" install -d -m 700 -o "$APP_USER" -g "$APP_USER" "$APP_HOME/.ssh" if [ -f /root/.ssh/id_ed25519 ]; then install -m 600 -o "$APP_USER" -g "$APP_USER" /root/.ssh/id_ed25519 "$APP_HOME/.ssh/id_ed25519" fi ssh-keyscan gitverse.ru >> "$APP_HOME/.ssh/known_hosts" 2>/dev/null || true chown -R "$APP_USER:$APP_USER" "$APP_HOME/.ssh" # Кэши composer/npm и git-config под www-data for d in .config .cache .npm; do install -d -o "$APP_USER" -g "$APP_USER" "$APP_HOME/$d"; done runuser -u "$APP_USER" -- git config --global --add safe.directory "$APP_DIR" 2>/dev/null || true chmod +x "$APP_DIR/deploy/update.sh" 2>/dev/null || true ### ── 11. Nginx + HTTPS ───────────────────────────────────────────────────────── log "Настройка Nginx" chmod +x "$APP_DIR/deploy/render-nginx.sh" "$APP_DIR/deploy/setup-tls.sh" if [ "$TLS_MODE" = "off" ]; then log "TLS_MODE=off — сайт только по HTTP (не рекомендуется)" else log "Выпуск/проверка TLS-сертификата (режим: ${TLS_MODE})" CERT_NAME="$CERT_NAME" CERT_IP="$CERT_IP" bash "$APP_DIR/deploy/setup-tls.sh" fi APP_DIR="$APP_DIR" APP_DOMAIN="$APP_DOMAIN" PHP_VER="$PHP_VER" TLS_MODE="$TLS_MODE" \ bash "$APP_DIR/deploy/render-nginx.sh" nginx -t systemctl enable --now nginx php${PHP_VER}-fpm systemctl reload nginx # Текущий режим — читает панель ITCUP для отображения в настройках mkdir -p /etc/ssl/itcup echo "$TLS_MODE" > /etc/ssl/itcup/mode.txt if [ "$TLS_MODE" != "off" ]; then echo echo " Корневой сертификат для установки на компьютеры сотрудников:" echo " http://${CERT_IP}/ca.crt (скачать один раз, затем установить в доверенные)" echo " см. deploy/README-TLS.md — инструкция и GPO-скрипт для домена" fi # Таймер ставится всегда (даже при TLS_MODE=off) — иначе некому будет # подхватить заявку "Включить HTTPS", отправленную позже из панели настроек chmod +x "$APP_DIR/deploy/apply-tls.sh" cat > /etc/systemd/system/itcup-tls-renew.service <<UNIT [Unit] Description=ITCUP: заявки на HTTPS (включение/смена сертификата) и автопродление [Service] Type=oneshot Environment=APP_DIR=${APP_DIR} ExecStart=/bin/bash ${APP_DIR}/deploy/apply-tls.sh UNIT cat > /etc/systemd/system/itcup-tls-renew.timer <<UNIT [Unit] Description=ITCUP: проверка TLS каждую минуту [Timer] OnCalendar=*:0/1 Persistent=true [Install] WantedBy=timers.target UNIT systemctl daemon-reload systemctl enable --now itcup-tls-renew.timer ### ── 12. Systemd: очередь и планировщик ─────────────────────────────────────── log "Systemd-юниты: queue worker + scheduler" cat > /etc/systemd/system/itcup-queue.service <<UNIT [Unit] Description=ITCUP queue worker After=network.target redis-server.service postgresql.service [Service] User=${APP_USER} Restart=always WorkingDirectory=${APP_DIR} ExecStart=/usr/bin/php ${APP_DIR}/artisan queue:work --sleep=3 --tries=3 --max-time=3600 [Install] WantedBy=multi-user.target UNIT cat > /etc/systemd/system/itcup-scheduler.service <<UNIT [Unit] Description=ITCUP scheduler [Service] User=${APP_USER} WorkingDirectory=${APP_DIR} ExecStart=/usr/bin/php ${APP_DIR}/artisan schedule:run UNIT cat > /etc/systemd/system/itcup-scheduler.timer <<UNIT [Unit] Description=Run ITCUP scheduler every minute [Timer] OnCalendar=*:0/1 Persistent=true [Install] WantedBy=timers.target UNIT systemctl daemon-reload systemctl enable --now itcup-queue.service systemctl enable --now itcup-scheduler.timer ### ── 13. Модуль Node.js-приложений ──────────────────────────────────────────── log "Инфраструктура для Node.js-приложений" # Базовый каталог проектов и спул nginx-конфигов (пишет www-data из панели) install -d -m 775 -o "$APP_USER" -g "$APP_USER" /var/www/node-apps install -d -m 775 -o "$APP_USER" -g "$APP_USER" /var/www/node-apps-nginx # Каталог, который подключается в nginx (пишет root-демон синхронизации) install -d -m 755 /etc/nginx/itcup-node grep -q 'include /etc/nginx/itcup-node/\*.conf;' /etc/nginx/nginx.conf || \ sed -i '/http {/a\ include /etc/nginx/itcup-node/*.conf;' /etc/nginx/nginx.conf # Root-демон: переносит конфиги из спула www-data в nginx и перезагружает его cat > /usr/local/sbin/itcup-node-nginx-sync.sh <<'SYNC' #!/usr/bin/env bash set -e SPOOL=/var/www/node-apps-nginx DEST=/etc/nginx/itcup-node mkdir -p "$DEST" changed=0 # Копируем/обновляем актуальные конфиги for f in "$SPOOL"/*.conf; do [ -e "$f" ] || continue base="$(basename "$f")" if ! cmp -s "$f" "$DEST/$base"; then cp "$f" "$DEST/$base"; changed=1 fi done # Удаляем те, которых больше нет в спуле for f in "$DEST"/*.conf; do [ -e "$f" ] || continue base="$(basename "$f")" if [ ! -e "$SPOOL/$base" ]; then rm -f "$f"; changed=1; fi done if [ "$changed" = "1" ]; then nginx -t && systemctl reload nginx fi SYNC chmod 755 /usr/local/sbin/itcup-node-nginx-sync.sh cat > /etc/systemd/system/itcup-node-nginx-sync.service <<UNIT [Unit] Description=ITCUP: синхронизация nginx-конфигов Node.js-приложений [Service] Type=oneshot ExecStart=/usr/local/sbin/itcup-node-nginx-sync.sh UNIT cat > /etc/systemd/system/itcup-node-nginx-sync.timer <<UNIT [Unit] Description=ITCUP: проверка nginx-конфигов Node.js каждую минуту [Timer] OnCalendar=*:0/1 Persistent=true [Install] WantedBy=timers.target UNIT systemctl daemon-reload systemctl enable --now itcup-node-nginx-sync.timer log "Готово. ITCUP развёрнут в ${APP_DIR}, доступен по ${APP_URL}"