tmp_full
Различия
Показаны различия между двумя версиями страницы.
| tmp_full [2026/05/17 12:18] – создано VladPolskiy | tmp_full [2026/05/18 11:28] (текущий) – внешнее изменение 127.0.0.1 | ||
|---|---|---|---|
| Строка 1: | Строка 1: | ||
| - | ====== index.html | + | < |
| - | <code html index.html> | + | === index.html |
| < | < | ||
| <html lang=" | <html lang=" | ||
| Строка 182: | Строка 182: | ||
| </ | </ | ||
| - | | + | < |
| - | <div id=" | + | <div id=" |
| - | <div class=" | + | <h4 class=" |
| - | <p id=" | + | <p class=" |
| - | <div class=" | + | |
| - | <div id=" | + | <div class=" |
| - | </ | + | < |
| - | <div class=" | + | <!-- Текстовая область, |
| - | </ | + | < |
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | </ | ||
| + | |||
| + | <div class=" | ||
| + | < | ||
| + | ⚠️ < | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | <!-- ШАГ 10: ПРОЦЕСС УСТАНОВКИ С ВНУТРЕННЕЙ КОНСОЛЬЮ | ||
| + | <div id=" | ||
| + | <div class=" | ||
| + | <p id=" | ||
| + | |||
| + | <div class=" | ||
| + | <div id=" | ||
| + | </ | ||
| + | |||
| + | <div class=" | ||
| + | |||
| + | <!-- Консоль теперь живет строго ЗДЕСЬ и будет появляться только на этом шаге --> | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | <div id=" | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | <!-- Теперь это ШАГ 11: Завершение и перезагрузка --> | ||
| + | <div id=" | ||
| + | <div class=" | ||
| + | < | ||
| + | |||
| + | <!-- Сюда JS выведет надпись об обновлении страницы --> | ||
| + | <div id=" | ||
| + | |||
| + | <div class=" | ||
| + | < | ||
| + | <h1 id=" | ||
| + | <p class=" | ||
| + | </ | ||
| + | </ | ||
| - | <!-- Теперь это ШАГ 10: Завершение и перезагрузка --> | ||
| - | <div id=" | ||
| - | <div class=" | ||
| - | < | ||
| - | <div class=" | ||
| - | < | ||
| - | <h1 id=" | ||
| - | <p class=" | ||
| - | </ | ||
| - | </ | ||
| Строка 216: | Строка 256: | ||
| < | < | ||
| let currentStep = 1; | let currentStep = 1; | ||
| - | const totalSteps = 10; | + | const totalSteps = 11; |
| // Флаги валидации для учетной записи | // Флаги валидации для учетной записи | ||
| Строка 241: | Строка 281: | ||
| const errorBlock = document.getElementById(' | const errorBlock = document.getElementById(' | ||
| if (errorBlock) errorBlock.style.display = ' | if (errorBlock) errorBlock.style.display = ' | ||
| + | |||
| + | // АВТО-ОЧИСТКА СЕРВЕРА: | ||
| + | if (direction === -1 && currentStep === 9) { | ||
| + | // Блокируем интерфейс на долю секунды для выполнения зачистки | ||
| + | document.getElementById(' | ||
| + | document.getElementById(' | ||
| + | |||
| + | fetch(' | ||
| + | .then(res => res.json()) | ||
| + | .then(result => { | ||
| + | // Разблокируем кнопки после успешной очистки сервера | ||
| + | document.getElementById(' | ||
| + | document.getElementById(' | ||
| + | }) | ||
| + | .catch(err => { | ||
| + | console.log(" | ||
| + | document.getElementById(' | ||
| + | document.getElementById(' | ||
| + | }); | ||
| + | } | ||
| // Подгрузка доступных дисков при переходе с 5 на 6 шаг | // Подгрузка доступных дисков при переходе с 5 на 6 шаг | ||
| Строка 274: | Строка 334: | ||
| } | } | ||
| - | // Действия при нажатии " | + | // ТРИГГЕР: |
| if (direction === 1 && currentStep === 8) { | if (direction === 1 && currentStep === 8) { | ||
| let selectedDisks = []; | let selectedDisks = []; | ||
| const diskMode = document.querySelector(' | const diskMode = document.querySelector(' | ||
| + | | ||
| + | if (diskMode === ' | ||
| + | document.querySelectorAll(' | ||
| + | selectedDisks.push(cb.value); | ||
| + | }); | ||
| + | } else { | ||
| + | const firstDiskInput = document.querySelector(' | ||
| + | // ИСПРАВЛЕНО: | ||
| + | selectedDisks.push(firstDiskInput ? firstDiskInput.value : '' | ||
| + | } | ||
| + | // Формируем точный текст будущего конфигурационного файла для вывода пользователю | ||
| + | let configPreview = `SYS_LANG=${document.getElementById(' | ||
| + | configPreview += `SYS_LAYOUT=${document.getElementById(' | ||
| + | configPreview += `SYS_TIMEZONE=${document.getElementById(' | ||
| + | configPreview += `SYS_HOSTNAME=${document.getElementById(' | ||
| + | configPreview += `DISK_MODE=${diskMode}\n`; | ||
| + | configPreview += `SELECTED_DISKS=${selectedDisks.join(' | ||
| + | configPreview += `SYS_USER=${document.getElementById(' | ||
| + | configPreview += `SYS_PASS=${document.getElementById(' | ||
| + | |||
| + | // Безопасно выводим сформированный текст в зону контроля нового Шага 9 | ||
| + | const previewZone = document.getElementById(' | ||
| + | if (previewZone) { | ||
| + | previewZone.value = configPreview; | ||
| + | } | ||
| + | |||
| + | // Переключаем интерфейс со старого Шага 8 на новый Шаг 9 | ||
| + | document.getElementById(`step-${currentStep}`).classList.remove(' | ||
| + | currentStep = 9; | ||
| + | document.getElementById(`step-9`).classList.add(' | ||
| + | | ||
| + | // Кнопки навигации остаются активными, | ||
| + | document.getElementById(' | ||
| + | document.getElementById(' | ||
| + | | ||
| + | return; // Полностью прерываем выполнение, | ||
| + | } | ||
| + | |||
| + | // ТРИГГЕР: | ||
| + | if (direction === 1 && currentStep === 9) { | ||
| + | let selectedDisks = []; | ||
| + | const diskMode = document.querySelector(' | ||
| + | | ||
| if (diskMode === ' | if (diskMode === ' | ||
| - | document.querySelectorAll(' | + | document.querySelectorAll(' |
| - | selectedDisks.push(cb.value); | + | selectedDisks.push(cb.value); |
| }); | }); | ||
| } else { | } else { | ||
| Строка 288: | Строка 391: | ||
| } | } | ||
| - | // Сбор Payload напрямую из полей (СВЕЖИЕ ДАННЫЕ) | ||
| const payload = { | const payload = { | ||
| lang: document.getElementById(' | lang: document.getElementById(' | ||
| Строка 299: | Строка 401: | ||
| password: document.getElementById(' | password: document.getElementById(' | ||
| }; | }; | ||
| + | |||
| + | // Блокируем кнопку на время отправки, | ||
| + | document.getElementById(' | ||
| // ПРИНУДИТЕЛЬНЫЙ ЗАПУСК ОТПРАВКИ НА СЕРВЕР | // ПРИНУДИТЕЛЬНЫЙ ЗАПУСК ОТПРАВКИ НА СЕРВЕР | ||
| Строка 309: | Строка 414: | ||
| .then(result => { | .then(result => { | ||
| if (result && result.success) { | if (result && result.success) { | ||
| - | document.getElementById(' | + | |
| - | currentStep = 9; // <--- Корректный переход | + | |
| - | document.getElementById(' | + | currentStep = 10; |
| + | document.getElementById(' | ||
| | | ||
| + | // Намертво скрываем нижние кнопки навигации на время установки | ||
| document.getElementById(' | document.getElementById(' | ||
| document.getElementById(' | document.getElementById(' | ||
| | | ||
| + | // Запускаем AJAX-опрос системного журнала установки | ||
| startInstallationSimulation(); | startInstallationSimulation(); | ||
| } else { | } else { | ||
| + | document.getElementById(' | ||
| showInlineError(' | showInlineError(' | ||
| } | } | ||
| }) | }) | ||
| .catch(err => { | .catch(err => { | ||
| + | document.getElementById(' | ||
| showInlineError(' | showInlineError(' | ||
| }); | }); | ||
| - | return; // Ждем ответа fetch, блокируем линейный переход | + | |
| + | | ||
| } | } | ||
| + | |||
| // Линейный переход для остальных шагов | // Линейный переход для остальных шагов | ||
| Строка 332: | Строка 444: | ||
| document.getElementById(`step-${currentStep}`).classList.add(' | document.getElementById(`step-${currentStep}`).classList.add(' | ||
| - | document.getElementById(' | + | document.getElementById(' |
| - | document.getElementById(' | + | document.getElementById(' |
| if (currentStep === 6) { | if (currentStep === 6) { | ||
| Строка 434: | Строка 546: | ||
| } | } | ||
| - | // Мониторинг лога бэкенда | + | // Мониторинг лога бэкенда |
| function startInstallationSimulation() { | function startInstallationSimulation() { | ||
| const progressBar = document.getElementById(' | const progressBar = document.getElementById(' | ||
| Строка 441: | Строка 553: | ||
| let elapsedSeconds = 0; | let elapsedSeconds = 0; | ||
| + | // Счётчик времени выполнения операции | ||
| const durationTimer = setInterval(() => { | const durationTimer = setInterval(() => { | ||
| elapsedSeconds++; | elapsedSeconds++; | ||
| Строка 446: | Строка 559: | ||
| }, 1000); | }, 1000); | ||
| + | // Ежесекундный AJAX-опрос системного журнала установки | ||
| const logWatcher = setInterval(() => { | const logWatcher = setInterval(() => { | ||
| fetch(' | fetch(' | ||
| Строка 451: | Строка 565: | ||
| .then(data => { | .then(data => { | ||
| if (data && data.success) { | if (data && data.success) { | ||
| + | // Обновляем визуальный прогресс-бар | ||
| if (progressBar) { | if (progressBar) { | ||
| progressBar.style.width = data.progress + ' | progressBar.style.width = data.progress + ' | ||
| progressBar.innerText = data.progress + ' | progressBar.innerText = data.progress + ' | ||
| } | } | ||
| + | // Обновляем текущий статус над шкалой | ||
| if (statusText) statusText.innerText = data.status; | if (statusText) statusText.innerText = data.status; | ||
| - | // Переход на финал при | + | // ВЫВОД |
| + | const consoleZone = document.getElementById(' | ||
| + | const consoleWrapper = document.getElementById(' | ||
| + | |||
| + | if (consoleZone && data.console && data.console.length > 0) { | ||
| + | // Объединяем полученные строки через перенос строки | ||
| + | consoleZone.innerHTML = data.console.join('< | ||
| + | |||
| + | // Безопасная автоматическая прокрутка терминала вниз | ||
| + | if (consoleWrapper) { | ||
| + | consoleWrapper.scrollTop = consoleWrapper.scrollHeight; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Переход с Шага 10 на Финальный Шаг 11 при достижении 90% (С ЗАДЕРЖКОЙ ДЛЯ ИНСПЕКЦИИ) | ||
| if (data.progress >= 90) { | if (data.progress >= 90) { | ||
| + | // 1. Мгновенно останавливаем опрос логов, чтобы зафиксировать финальный текст на экране | ||
| clearInterval(logWatcher); | clearInterval(logWatcher); | ||
| clearInterval(durationTimer); | clearInterval(durationTimer); | ||
| | | ||
| - | document.getElementById(' | + | |
| - | | + | const progressBar = document.getElementById(' |
| - | document.getElementById('step-10').classList.add('active'); | + | if (progressBar) { |
| - | + | progressBar.style.width = '100%'; | |
| - | | + | |
| - | document.getElementById(' | + | progressBar.innerText = '100%'; |
| - | document.getElementById(' | + | |
| - | + | ||
| - | startRebootCountdown(); | + | |
| + | |||
| + | // 2. Включаем | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | // Запускаем финальный обратный отсчет самой перезагрузки | ||
| + | | ||
| + | }, 5000); // 5000 миллисекунд = 5 секунд | ||
| } | } | ||
| + | |||
| } | } | ||
| }) | }) | ||
| .catch(err => { | .catch(err => { | ||
| - | | + | |
| }); | }); | ||
| }, 1500); | }, 1500); | ||
| } | } | ||
| - | // Финальный экран и мягкий таймер без alert | ||
| - | function startRebootCountdown() { | ||
| - | let timeLeft = 5; | ||
| - | const countdownText = document.getElementById(' | ||
| - | const statusText = document.getElementById(' | ||
| - | | ||
| - | if (statusText) { | ||
| - | statusText.innerHTML = '< | ||
| - | } | ||
| - | const countdown = setInterval(() => { | ||
| - | timeLeft--; | ||
| - | if (countdownText) countdownText.innerText = timeLeft; | ||
| - | | ||
| - | if (timeLeft <= 0) { | ||
| - | clearInterval(countdown); | ||
| - | fetch(' | ||
| - | } | ||
| - | }, 1000); | ||
| - | } | ||
| // Вспомогательная функция вывода ошибок на экран (Привязка к контейнеру .buttons) | // Вспомогательная функция вывода ошибок на экран (Привязка к контейнеру .buttons) | ||
| Строка 535: | Строка 658: | ||
| const diskDiv = document.createElement(' | const diskDiv = document.createElement(' | ||
| diskDiv.className = ' | diskDiv.className = ' | ||
| + | // Обновленная верстка: | ||
| diskDiv.innerHTML = ` | diskDiv.innerHTML = ` | ||
| <input class=" | <input class=" | ||
| <label class=" | <label class=" | ||
| - | 💾 < | + | 💾 < |
| + | | ||
| + | Модель: | ||
| </ | </ | ||
| `; | `; | ||
| Строка 544: | Строка 670: | ||
| }); | }); | ||
| } else { | } else { | ||
| - | disksList.innerHTML = '< | + | disksList.innerHTML = '< |
| } | } | ||
| }) | }) | ||
| .catch(error => { | .catch(error => { | ||
| - | disksList.innerHTML = '< | + | disksList.innerHTML = '< |
| }); | }); | ||
| } | } | ||
| + | |||
| // Логика переключения режимов разметки дисков | // Логика переключения режимов разметки дисков | ||
| Строка 577: | Строка 704: | ||
| </ | </ | ||
| </ | </ | ||
| + | |||
| </ | </ | ||
| - | ====== 2fa.php ====== | + | < |
| - | <code php 2fa.php> | + | === 2fa.php === |
| <?php | <?php | ||
| // 1. Полностью отключаем дисковое кэширование библиотеки QR | // 1. Полностью отключаем дисковое кэширование библиотеки QR | ||
| Строка 621: | Строка 749: | ||
| QRcode:: | QRcode:: | ||
| exit; | exit; | ||
| + | |||
| </ | </ | ||
| + | < | ||
| - | ====== disk_prepare.sh ====== | + | === cancel_install.php |
| - | < | + | <?php |
| + | // Скрипт принудительной очистки при возврате назад (api/ | ||
| + | header(' | ||
| + | ini_set(' | ||
| + | |||
| + | $config_file = __DIR__ . '/ | ||
| + | $log_file = '/ | ||
| + | |||
| + | // 1. Физически удаляем файл конфигурации, | ||
| + | if (file_exists($config_file)) { | ||
| + | unlink($config_file); | ||
| + | } | ||
| + | |||
| + | // 2. Стираем лог, чтобы get_log.php обнулился | ||
| + | if (file_exists($log_file)) { | ||
| + | unlink($log_file); | ||
| + | } | ||
| + | |||
| + | // 3. Мягко завершаем фоновые процессы, | ||
| + | exec(' | ||
| + | |||
| + | // 4. Размонтируем диски, если они остались заблокированы в системе | ||
| + | exec(' | ||
| + | |||
| + | echo json_encode([' | ||
| + | exit; | ||
| + | |||
| + | </code> | ||
| + | |||
| + | < | ||
| + | |||
| + | === chroot_configure.sh === | ||
| #!/bin/bash | #!/bin/bash | ||
| - | # Финальный эталонный скрипт разметки (api/ | + | # Итоговый эталонный скрипт настройки под UEFI (api/ |
| - | cd " | + | |
| - | >> | + | # Импортируем переданные переменные из временного файла конфигурации |
| - | # 1. Сбрасываем лог дляget_log.php | + | if [ -f / |
| - | echo " | + | source / |
| - | " | + | fi |
| - | log_msg " | + | |
| - | # 2. Чтение | + | # 1. Сетевое имя хоста |
| - | set -a source " | + | if [ -z " |
| + | echo " | ||
| + | |||
| + | cat <<EOF > / | ||
| + | 127.0.0.1 | ||
| + | ::1 | ||
| + | 127.1.1.1 | ||
| + | EOF | ||
| + | |||
| + | # 2. Часовой пояс | ||
| + | /usr/bin/ln -sf / | ||
| + | / | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 3. Настройка учетной записи суперпользователя root (ЗАЩИЩЕННАЯ) | ||
| + | # ===================================================================== | ||
| + | if [ -z " | ||
| + | |||
| + | # Используем стандартный потоковый passwd, который никогда не роняет скрипт | ||
| + | echo -e " | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 4. Пользователь и права администратора (ЗАЩИЩЕННАЯ) | ||
| + | # ===================================================================== | ||
| + | if [ -z " | ||
| + | if ! id " | ||
| + | / | ||
| + | fi | ||
| + | |||
| + | # Назначаем пароль пользователю через надежный механизм | ||
| + | echo -e " | ||
| + | |||
| + | echo " | ||
| + | chmod 440 / | ||
| + | |||
| + | </ | ||
| + | < | ||
| + | |||
| + | === disk_prepare.sh === | ||
| + | # | ||
| + | # Финальный эталонный скрипт разметки | ||
| + | cd " | ||
| + | CONFIG_FILE=" | ||
| + | LOG_FILE="/ | ||
| + | |||
| + | # Безопасная функция записи, | ||
| + | log_msg() { | ||
| + | | ||
| + | | ||
| + | } | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 1. Принудительное удаление логов и сброс монтирований (Авто-очистка буфера) | ||
| + | # ===================================================================== | ||
| + | |||
| + | # полностью освобождая виртуальный dev-буфер для нового прогона | ||
| + | sudo / | ||
| + | sudo / | ||
| + | sudo / | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | # чтобы mkfs.vfat никогда не спотыкался о занятые дескрипторы | ||
| + | sudo / | ||
| + | |||
| + | # предотвращая аппаратную блокировку " | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | # полностью освобождая диски sdb и sdc от фоновой блокировки ядра | ||
| + | sudo / | ||
| + | |||
| + | # Если файл лога существовал, | ||
| + | sudo /usr/bin/rm -f " | ||
| + | |||
| + | # Создаем абсолютно новый, девственно чистый файл лога | ||
| + | echo " | ||
| + | |||
| + | # Сразу даем ему права 666, чтобы в него могли дописывать строки и http, и root (через sudo) | ||
| + | / | ||
| + | |||
| + | # Пишем первый синий маркер старта | ||
| + | echo " | ||
| + | # ===================================================================== | ||
| + | |||
| + | |||
| + | if [ ! -f " | ||
| + | log_msg " | ||
| + | | ||
| + | fi | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 2. Чтение | ||
| + | # ===================================================================== | ||
| + | set -a | ||
| + | # Команда tr -d ' | ||
| + | source | ||
| + | set +a | ||
| # 3. Безопасное уничтожение файла конфигурации на диске | # 3. Безопасное уничтожение файла конфигурации на диске | ||
| + | # Сохраняем бэкап конфига для system_install.sh перед удалением оригинала | ||
| + | cp " | ||
| / | / | ||
| - | # 4. Начало разметки дисков | + | log_msg " |
| - | echo " | + | |
| - | read -r -a DISKS_ARRAY <<< | + | # ===================================================================== |
| - | # 5. Стираем старые сигнатуры ФС | + | # 4. Сбор |
| - | for disk in " | + | # ===================================================================== |
| - | " | + | log_msg |
| - | # 6. Форматирование | + | if [ -z "$SELECTED_DISKS" ]; then |
| - | if [ " | + | SELECTED_DISKS=" |
| - | "$LOG_FILE" | + | fi |
| - | else /usr/bin/mkfs.btrfs -f $TARGET_DISKS | + | IFS=' ' read -r -a DISKS_ARRAY <<< |
| - | " | + | |
| - | # 7. Монтирование | + | # ===================================================================== |
| - | FIRST_DISK="/ | + | # НОВОЕ: Динамический сброс таблиц разделов |
| - | /mnt/@ / | + | # ===================================================================== |
| - | # 8. Передача эстафеты скрипту установки | + | # Этот цикл заставит ядро Linux принудительно перечитать структуру именно |
| - | echo " | + | # тех накопителей, |
| - | " | + | for raw_disk in "${DISKS_ARRAY[@]}"; do |
| + | disk=$(echo " | ||
| + | sudo / | ||
| + | done | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 5. Принудительная зачистка (Wipe Out) сигнатур прошлых ФС через sudo | ||
| + | # ===================================================================== | ||
| + | log_msg " | ||
| + | for disk in " | ||
| + | sudo / | ||
| + | done | ||
| + | |||
| + | log_msg | ||
| + | log_msg | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 6. Автоматическая нарезка таблиц GPT через fdisk (Чистый синтаксис) | ||
| + | # ===================================================================== | ||
| + | log_msg | ||
| + | for raw_disk in " | ||
| + | | ||
| + | disk=$(echo | ||
| + | |||
| + | sudo /usr/bin/fdisk "/dev/$disk" << | ||
| + | g | ||
| + | n | ||
| + | 1 | ||
| + | |||
| + | +512M | ||
| + | t | ||
| + | 1 | ||
| + | n | ||
| + | 2 | ||
| + | |||
| + | |||
| + | w | ||
| + | EOF | ||
| + | done | ||
| + | |||
| + | # ИСПРАВЛЕНО: | ||
| + | CLEAN_FIRST_DISK=$(echo "${DISKS_ARRAY[0]}" | ||
| + | |||
| + | # Форматируем созданный первый раздел в FAT32 (UEFI) через | ||
| + | log_msg " | ||
| + | sudo / | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 7. Форматирование | ||
| + | # ===================================================================== | ||
| + | log_msg " | ||
| + | # Используем разделы (sdb2, sdc2), | ||
| + | TARGET_PARTITIONS=$(printf | ||
| + | |||
| + | if [ " | ||
| + | sudo /usr/bin/mkfs.btrfs -f -d raid1 -m raid1 $TARGET_PARTITIONS | /usr/bin/tee -a " | ||
| + | else | ||
| + | sudo /usr/bin/mkfs.btrfs -f $TARGET_PARTITIONS | /usr/bin/tee -a " | ||
| + | fi | ||
| + | |||
| + | # 8. Передача эстафеты следующему этапу | ||
| + | log_msg " | ||
| + | echo " | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 🔍 ИНСПЕКЦИЯ ЭТАПА РАЗМЕТКИ И ФОРМАТИРОВАНИЯ (ФИЗИЧЕСКИЙ КОНТРОЛЬ) | ||
| + | # ===================================================================== | ||
| + | log_msg | ||
| + | |||
| + | # 1. Выводим реальный статус физических разделов sdb и sdc из ядра Linux | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | # 2. Временно монтируем чистый корень sdb2 наружу для проверки структуры | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | log_msg " | ||
| + | sudo /usr/ | ||
| + | |||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | log_msg " | ||
| + | # ===================================================================== | ||
| + | |||
| + | |||
| + | sudo -E / | ||
| + | |||
| </ | </ | ||
| + | < | ||
| - | ====== get_disks.php ====== | + | === get_disks.php === |
| - | <code php get_disks.php> | + | |
| <?php | <?php | ||
| + | // Автоматический опрос дисков с выводом модели устройства (api/ | ||
| header(' | header(' | ||
| + | ini_set(' | ||
| - | // Передаем параметры lsblk напрямую в бинарник | + | // 1. Автоматически определяем имя установочной флешки |
| - | $command | + | $usb_drive = ''; |
| - | exec($command, | + | $boot_mount = shell_exec(" |
| + | if (empty($boot_mount)) { | ||
| + | $boot_mount = shell_exec(" | ||
| + | } | ||
| + | if (!empty($boot_mount)) { | ||
| + | $usb_drive = preg_replace('/ | ||
| + | } | ||
| + | |||
| + | // 2. Автоматически определяем диск текущей запущенной ОС (Ubuntu / Донор) | ||
| + | $current_os_drive | ||
| + | $root_mount = shell_exec(" | ||
| + | if (!empty($root_mount)) { | ||
| + | $current_os_drive = preg_replace('/ | ||
| + | } | ||
| + | |||
| + | // 3. Получаем список физических дисков | ||
| + | $sys_blocks = glob('/ | ||
| + | if (empty($sys_blocks)) { | ||
| + | | ||
| + | } | ||
| $disks = []; | $disks = []; | ||
| - | if ($return_var === 0) { | + | // [Этот блок находится внутри api/ |
| - | foreach ($output as $line) { | + | foreach |
| - | | + | $disk_name |
| - | $line = preg_replace('/ | + | |
| - | list($name, $size, $model) | + | // СТРОГИЙ |
| - | + | // Все остальные | |
| - | // Фильтруем виртуальные | + | if ($disk_name |
| - | if (strpos($name, ' | + | continue; |
| - | continue; | + | |
| - | } | + | |
| - | + | ||
| - | $disks[] = [ | + | |
| - | ' | + | |
| - | ' | + | |
| - | ' | + | |
| - | ]; | + | |
| } | } | ||
| + | | ||
| + | // Пропускаем виртуальные loop-устройства и CD-ROM | ||
| + | if (strpos($disk_name, | ||
| + | continue; | ||
| + | } | ||
| + | |||
| + | // Читаем размер диска | ||
| + | $size_sectors = (float)trim(file_get_contents(" | ||
| + | $size_gb = round(($size_sectors * 512) / (1024 * 1024 * 1024), 1); | ||
| + | | ||
| + | // Игнорируем слишком маленькие накопители (меньше 2 ГБ) | ||
| + | if ($size_gb < 2) { | ||
| + | continue; | ||
| + | } | ||
| + | |||
| + | // Читаем модель диска напрямую из sysfs ядра Linux | ||
| + | $model_path = " | ||
| + | $disk_model = " | ||
| + | | ||
| + | if (file_exists($model_path)) { | ||
| + | $disk_model = trim(file_get_contents($model_path)); | ||
| + | $disk_model = preg_replace('/ | ||
| + | } | ||
| + | |||
| + | $disks[] = [ | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ]; | ||
| } | } | ||
| - | echo json_encode([' | + | |
| - | ?> | + | echo json_encode([ |
| + | | ||
| + | | ||
| + | ], JSON_UNESCAPED_UNICODE); | ||
| + | exit; | ||
| </ | </ | ||
| + | < | ||
| - | ====== get_log.php ====== | + | === get_log.php === |
| - | <code php get_log.php> | + | |
| <?php | <?php | ||
| + | // Полный сбор истории логов (api/ | ||
| header(' | header(' | ||
| + | ini_set(' | ||
| - | $logFile | + | $log_path |
| - | if (file_exists($logFile)) { | + | if (!file_exists($log_path)) { |
| - | | + | |
| - | $lines = file($logFile); | + | ' |
| - | if (!empty($lines)) { | + | ' |
| - | $lastLine | + | ' |
| - | + | ' | |
| - | // Разделяем строку по символу " | + | |
| - | $parts = explode('|', | + | exit; |
| - | + | } | |
| - | if (count($parts) === 2) { | + | |
| - | echo json_encode([ | + | $lines = file($log_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
| - | ' | + | $progress = 0; |
| - | | + | $status |
| - | ' | + | $clean_lines = []; |
| - | | + | |
| - | exit; | + | // Парсим |
| - | } | + | foreach ($lines as $line) { |
| + | if (strpos($line, | ||
| + | $progress | ||
| + | } elseif | ||
| + | | ||
| + | $clean_lines[] | ||
| + | } elseif | ||
| + | $status = trim(str_replace(' | ||
| + | | ||
| + | } else { | ||
| + | // Все остальные строки (вывод rsync, mkfs, wipefs) добавляем как есть | ||
| + | $clean_lines[] = $line; | ||
| } | } | ||
| } | } | ||
| - | // Если Bash-скрипт еще не успел создать лог-файл, | + | echo json_encode([ |
| - | echo json_encode([' | + | |
| - | ?> | + | |
| + | | ||
| + | | ||
| + | ], JSON_UNESCAPED_UNICODE); | ||
| + | exit; | ||
| </ | </ | ||
| - | ====== start_install.php ====== | + | < |
| - | <code php start_install.php> | + | |
| + | === start_install.php === | ||
| <?php | <?php | ||
| header(' | header(' | ||
| Строка 741: | Строка 1157: | ||
| // Пишем конфиг с нуля (всегда новые данные) | // Пишем конфиг с нуля (всегда новые данные) | ||
| - | $configContent = " | + | // [Этот блок находится внутри api/ |
| - | $configContent .= " | + | // Формируем чистые строки конфигурации для Bash без скрытых символов \r |
| - | $configContent .= " | + | $configContent = " |
| + | $configContent .= " | ||
| + | $configContent .= " | ||
| $configContent .= " | $configContent .= " | ||
| - | $configContent .= " | + | $configContent .= " |
| - | $configContent .= " | + | |
| + | // Жестко очищаем массив дисков от любых пробелов и переносов перед склейкой | ||
| + | $clean_disks = array_map(' | ||
| + | $configContent .= " | ||
| $configContent .= " | $configContent .= " | ||
| $configContent .= " | $configContent .= " | ||
| + | |||
| $target_file = __DIR__ . '/ | $target_file = __DIR__ . '/ | ||
| Строка 757: | Строка 1180: | ||
| | | ||
| // Передаем управление ОДНОМУ управляющему скрипту | // Передаем управление ОДНОМУ управляющему скрипту | ||
| - | | + | exec(' |
| | | ||
| echo json_encode([' | echo json_encode([' | ||
| Строка 764: | Строка 1187: | ||
| } | } | ||
| exit; | exit; | ||
| + | |||
| + | |||
| </ | </ | ||
| - | ====== system_install.sh | + | < |
| - | <code bash system_install.sh> | + | |
| + | === system_install.sh === | ||
| #!/bin/bash | #!/bin/bash | ||
| - | # Скорректированный | + | # Скрипт точного клонирования и создания Recovery |
| cd " | cd " | ||
| LOG_FILE="/ | LOG_FILE="/ | ||
| - | log_msg() { echo " | ||
| - | # Монтирование | + | log_msg() { |
| + | echo " | ||
| + | echo " | ||
| + | } | ||
| + | |||
| + | # ===================================================================== | ||
| + | # 1. Чтение конфигурации и ЖЕСТКОЕ МОНТИРОВАНИЕ ФИЗИЧЕСКИХ ДИСКОВ | ||
| + | # ===================================================================== | ||
| + | # Импортируем переменные напрямую (на случай, | ||
| + | if [ -f " | ||
| + | source <(tr -d ' | ||
| + | elif [ -f " | ||
| + | source <(tr -d ' | ||
| + | fi | ||
| + | |||
| + | if [ -z " | ||
| + | SELECTED_DISKS=" | ||
| + | fi | ||
| IFS=' ' read -r -a DISKS_ARRAY <<< | IFS=' ' read -r -a DISKS_ARRAY <<< | ||
| - | FIRST_DISK="/ | ||
| - | / | ||
| - | / | ||
| - | / | ||
| - | log_msg " | + | # Вычисляем чистые имена |
| - | # Pacstrap | + | FIRST_BTRFS_DISK=" |
| - | /usr/bin/pacstrap | + | FIRST_EFI_DISK=" |
| - | if [ $? -ne 0 ]; then | + | # Принудительно монтируем реальные физические разделы жестких дисков через sudo |
| - | log_msg " | + | # чтобы перепримонтировать диски строго по Btrfs-полочкам |
| + | sudo / | ||
| + | |||
| + | log_msg " | ||
| + | sudo / | ||
| + | |||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | |||
| + | |||
| + | log_msg " | ||
| + | log_msg " | ||
| + | |||
| + | log_msg " | ||
| + | # Проверяем, | ||
| + | sudo /usr/bin/df -h /mnt >> " | ||
| + | sudo /usr/bin/df -h / | ||
| + | |||
| + | |||
| + | # 2. ЗАПУСК КЛОНИРОВАНИЯ (RSYNC): Пишем через tee -a, обходя ошибки прав | ||
| + | / | ||
| + | |||
| + | if [ ${PIPESTATUS[0]} | ||
| + | log_msg " | ||
| exit 1 | exit 1 | ||
| fi | fi | ||
| - | log_msg "[PROGRESS] 75" | + | # ===================================================================== |
| - | /usr/bin/genfstab | + | # 🔍 ИНСПЕКЦИЯ РЕЗУЛЬТАТОВ ЗАПИСИ (ФИЗИЧЕСКИЙ КОНТРОЛЬ КЛОНИРОВАНИЯ И GRUB) |
| + | # ===================================================================== | ||
| + | log_msg "[DIAGNOSTIC] === СТАРТ ПРОВЕРКИ ФИЗИЧЕСКОЙ ЗАПИСИ ДАННЫХ ===" | ||
| + | |||
| + | log_msg " | ||
| + | sudo /usr/bin/du -sh /mnt/ --exclude=/ | ||
| + | |||
| + | log_msg " | ||
| + | sudo /usr/bin/ls -la /mnt/ >> " | ||
| + | |||
| + | log_msg " | ||
| + | sudo /usr/bin/du -sh / | ||
| + | |||
| + | log_msg " | ||
| + | sudo /usr/bin/ls -R / | ||
| + | |||
| + | log_msg " | ||
| + | sudo / | ||
| + | |||
| + | log_msg " | ||
| + | # ===================================================================== | ||
| + | |||
| + | |||
| + | # 3. ЗАПИСЬ " | ||
| + | log_msg " | ||
| + | log_msg " | ||
| + | |||
| + | / | ||
| + | / | ||
| + | |||
| + | # Архивируем чистую систему напрямую в подтом @security через tee | ||
| + | / | ||
| + | |||
| + | if [ ${PIPESTATUS[0]} -ne 0 ]; then | ||
| + | log_msg " | ||
| + | / | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | / | ||
| + | / | ||
| + | log_msg " | ||
| + | |||
| + | log_msg " | ||
| + | log_msg " | ||
| + | |||
| + | # 4. Генерируем чистый fstab для новых Btrfs-разделов | ||
| + | # ИСПРАВЛЕНО: | ||
| + | sudo / | ||
| log_msg " | log_msg " | ||
| - | log_msg " | + | log_msg " |
| + | |||
| + | # ===================================================================== | ||
| + | # 5. ХОСТ-СБОРКА GRUB UEFI И СТАНДАРТНЫЙ CHROOT НАСТРОЕК (БЕЗОШИБОЧНЫЙ) | ||
| + | # ===================================================================== | ||
| + | log_msg " | ||
| + | log_msg " | ||
| + | |||
| + | # 1. Принудительно заставляем хост собрать GRUB для целевой системы, | ||
| + | # Хост имеет 100% доступ к дискам, | ||
| + | sudo / | ||
| + | |||
| + | # 2. АВТОНОМНЫЙ ФИКС: Копируем модули GRUB напрямую на FAT32 раздел | ||
| + | sudo / | ||
| + | sudo /usr/bin/cp -r / | ||
| + | |||
| + | # 3. не сканировал диски хоста и гарантированно НЕ ронял PHP-FPM в 502 ошибку! | ||
| + | export GRUB_DISABLE_OS_PROBER=true | ||
| + | # Генерируем конфигурацию GRUB прямо из контекста хоста внутрь рейда | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | log_msg " | ||
| + | |||
| + | # 4. Монтируем только базовые папки для chroot настроек пользователей | ||
| + | sudo / | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | # 5. Передаем переменные во временный файл конфигурации окружения | ||
| + | cat <<EOF | sudo / | ||
| + | SYS_HOSTNAME=$(echo " | ||
| + | SYS_USER=$(echo " | ||
| + | SYS_PASS=$(echo " | ||
| + | SYS_TIMEZONE=$(echo " | ||
| + | EOF | ||
| + | |||
| + | # 6. Фильтрует скрытые символы \r и запускает chroot_configure.sh ТЕПЕРЬ ТОЛЬКО ДЛЯ ПАРОЛЕЙ И ЮЗЕРОВ | ||
| + | sudo /usr/bin/tr -d ' | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | # 7. Ленивое безопасное размонтирование и зачистка | ||
| + | sudo /usr/bin/rm -f / | ||
| + | sudo /usr/bin/rm -f / | ||
| + | sudo / | ||
| + | sudo / | ||
| + | sudo / | ||
| + | |||
| + | log_msg " | ||
| + | log_msg " | ||
| </ | </ | ||
| + | < | ||
| + | ===validate_input.php=== | ||
| - | ====== validate_input.php ====== | ||
| - | <code php index.php> | ||
| <?php | <?php | ||
| + | // validate_input.php | ||
| // Функция полной очистки строк | // Функция полной очистки строк | ||
| function sanitize_and_validate($data, | function sanitize_and_validate($data, | ||
| Строка 845: | Строка 1412: | ||
| } | } | ||
| </ | </ | ||
| - | |||
tmp_full.1779009488.txt.gz · Последнее изменение: — VladPolskiy
