Инструменты пользователя

Инструменты сайта


tmp_27.06.2026

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Следующая версия
Предыдущая версия
tmp_27.06.2026 [2026/06/27 10:43] – создано VladPolskiytmp_27.06.2026 [2026/06/27 15:35] (текущий) VladPolskiy
Строка 1: Строка 1:
-====== Файл менеджер ====== +// assets/js/app.js
-===== 🗺️ Общая архитектурная схема проекта ===== +
-<code> +
-       [ ПОЛЬЗОВАТЕЛЬ БОТ ХАКЕР ] +
-                     │ +
-                     ▼ +
-  ┌────────────────────────────────────────────────────────┐ +
-  │ 1. index.php (Главный шлюз приложения)                 │ +
-  │    - Проверяет сессию                                  │ +
-  │    - Если первый запуск ➔ перенаправляет на установку │ +
-  └──────────────────┬─────────────────────────────────────┘ +
-                     │ +
-                     ▼ +
-  ┌────────────────────────────────────────────────────────┐ +
-  │ 2. backend/core/initializer.php (Динамический корень)  │ +
-  │    - Вычисляет APP_ROOT через __DIR__                  │ +
-  │    - Создает папки: config/, storage/, system_logs/    │ +
-  └──────────────────┬─────────────────────────────────────┘ +
-                     │ +
-                     ▼ +
-  ┌────────────────────────────────────────────────────────┐ +
-  │ 3. backend/auth/check_ip.php (Первый рубеж защиты)     │ +
-  │    - Сверяет IP с config/ip_blacklist.txt              │ +
-  │    - Бан на месте: die()                               │ +
-  └──────────────────┬─────────────────────────────────────┘ +
-                     │ +
-                     ├────────────────────────┐ +
-                     ▼ (Если не авторизован)  ▼ (Если авторизован) +
-  ┌────────────────────────────────────────┐ ┌──────────────────────────────────────┐ +
-  │ 4. ЭКРАН АВТОРИЗАЦИИ / ИНИЦИАЛИЗАЦИИ   │ │ 5. ГЛАВНЫЙ ИНТЕРФЕЙС (ДВА ОКНА)      │ +
-  │    - Поля: Логин / Пароль              │ │                                      │ +
-  │    - Скрытое поле: Honeypot (Ловушка)  │ │ ┌──────────────────────────────────┐ │ +
-  │    - Восстановление: Строка из книги   │ │ │ ВЕРХНЕЕ ОКНО                     │ │ +
-  │                                        │ │ │ [Имя / Путь] [Релизы ▼] [ZIP]    │ │ +
-  │    backend/auth/validate_input.php     │ │ ├──────────────────────────────────┤ │ +
-  │    - Вырезает XSS / Path Traversal     │ │ │ Список файлов релиза             │ │ +
-  │                                        │ │ │ (Имя | Примечание | Дата/Время)  │ │ +
-  │    backend/auth/login.php/ register.php│ │ └──────────────────────────────────┘ │ +
-  │    - Хэширует пароли и текст книги     │ │ ┌──────────────────────────────────┐ │ +
-  │    - Генерирует случайный 8-значный ID │ │ │ НИЖНЕЕ ОКНО                      │ │ +
-  │    - Сортирует и перетасовывает базу   │ │ │ [README] [Лицензия] [Комментарий]│ │ +
-  │                                        │ │ │ [Лог действий (для админа)]      │ │ +
-  │                                        │ │ ├──────────────────────────────────┤ │ +
-  │                                        │ │ │ Динамический текст (Fetch API)   │ │ +
-  │                                        │ │ └──────────────────────────────────┘ │ +
-  └────────────────────────────────────────┘ └──────────────────┬───────────────────┘ +
-                                                                │ +
-                                                                ▼ (Любое действие) +
-                                             ┌──────────────────────────────────────┐ +
-                                             │ 6. backend/router.php                │ +
-                                             │    - Принимает AJAX / Fetch          │ +
-                                             │    - Ставит "Маяки" при затыках      │ +
-                                             └──────────────────┬───────────────────┘ +
-                                                                │ +
-                                            ┌───────────────────┴───────────────────┐ +
-                                            ▼ (Действия Админа)                     ▼ (Логи системы) +
-                               ┌─────────────────────────┐             ┌─────────────────────────┐ +
-                               │ backend/actions/        │             │ storage/system_logs/    │ +
-                               │ - list.php              │             │ - app_errors.log        │ +
-                               │ - mkdir.php             │             │  (Маяки отладки для нас)│ +
-                               │ - delete.php            │             │ - admins_activity.log   │ +
-                               └─────────────────────────┘             └─────────────────────────┘+
  
-</code>+document.addEventListener('DOMContentLoaded', function() { 
 +     // --- НАДЕЖНОЕ УПРАВЛЕНИЕ МОДАЛКОЙ С ДЕБАГ-МАЯКОМ --- 
 +    // --- УПРАВЛЕНИЕ ЗАГРУЗЧИКОМ ПРОЕКТОВ --- 
 +    const btnCreateProject   = document.getElementById('btn-create-project'); 
 +    const projectModal       = document.getElementById('project-modal'); 
 +    const btnModalCancel     = document.getElementById('btn-modal-cancel'); 
 +    const btnModalSubmit     = document.getElementById('btn-modal-submit'); 
 +    const newProjectName     = document.getElementById('new-project-name'); 
 +    const modalErrorBox      = document.getElementById('modal-error-box'); 
 +     
 +    const folderInput        = document.getElementById('project-folder-input'); 
 +    const btnSelectFolder    = document.getElementById('btn-select-folder'); 
 +    const folderStatus       = document.getElementById('selected-folder-status');
  
-===== 🧩 Карта маскировки данных в backend/config/ ===== +    if (btnCreateProject && projectModal) { 
-Поскольку мы закладываем параноидальную защиту, вот схема того, как скрыты наши конфигурационные файлы, к которым имеет доступ только серверный PHP:+         
 +        // Функция проверки условий для активации кнопки агрузить" 
 +        function checkInputsValidity() { 
 +            const nameVal = newProjectName.value.trim(); 
 +            const hasFiles = folderInput.files && folderInput.files.length > 0; 
 +             
 +            if (nameVal !== "" && hasFiles) { 
 +                btnModalSubmit.disabled = false; // Включаем кнопку 
 +                btnModalSubmit.style.opacity = "1"; 
 +            } else { 
 +                btnModalSubmit.disabled = true;  // Выключаем кнопку 
 +                btnModalSubmit.style.opacity = "0.6"; 
 +            } 
 +        }
  
-<code> +        // Следим за вводом имени 
-backend/config/ +        newProjectName.addEventListener('input'checkInputsValidity);
-│ +
-├── salt.php           ───► [Возвращает: Секретный токен Супера (16 симв.)] +
-│                                    Секретный токен Админа  (16 симв.)] +
-│                           (Хакер в коде видит только шум, роли замаскированы) +
-│ +
-├── email.php          ───► [Возвращает: настоящий_email@супера.com] +
-│                           (Спрятан от парсеров, выводится только при ошибке админа) +
- +
-├── ip_blacklist.txt   ───► [Список забаненных IP-адресов] +
-│ +
-└── users.json         ───► [ База данных - Перетасованный массив ] +
-                            ┌──────────────────────────────────────────────┐ +
-                            │ ID: 85910432 (Рандомныйотсортирован      │ +
-                            │ Пароль: $2y$10$... (Защищенный хэш)          │ +
-                            │ Роль: x7R9wQ2pM4zL1vK8 (Случайный шум)       │ +
-                            │ Книга: $2y$10$... (Хэш контрольной строки)   │ +
-                            └──────────────────────────────────────────────┘+
  
-</code>+        // Клик по кастомной кнопке -триггерим скрытый системный инпут выбора папки 
 +        btnSelectFolder.addEventListener('click', function() { 
 +            folderInput.click(); 
 +        });
  
-===== 📂 Карта песочницы на диске (storage/) ===== +        // Следим за тем, что пользователь выбрал папку на ПК 
-Абсолютно безликая структура. Никаких названий компаний и имен. Только цифровые идентификаторы, сгенерированные нашей системой тасования+        folderInput.addEventListener('change', function() { 
-<code+            if (this.files && this.files.length 0) { 
-storage/ +                // Берем имя корневой папки, которую выбрал юзер 
-│ +                const firstFile = this.files[0]; 
-├── 85910432                ───► Личная папка Супер-админа (или Главного Админа) +                const rootFolderName = firstFile.webkitRelativePath.split('/')[0]; 
-│   ├── .system/ +                folderStatus.innerHTML = `Выбрано файлов: <strong>${this.files.length}</strong><br><span style="font-size:11px; color:#2da44e;">(из папки: ${rootFolderName})</span>`; 
-│   │   └── activity.log      ───► Локальный лог действий на 100 строк (Ротация) +            } else { 
-│   │ +                folderStatus.textContent = "Папка не выбрана"; 
-│   ├── Релиз_v1.0_Стабильный/ ───► Папка, которая отобразится в выпадающем списке +            } 
-│   │   ├── app.exe           ───► Файл для скачивания +            checkInputsValidity(); 
-│   │   ├── readme.txt        ───► Текст для вкладки README +        });
-│   │   ├── license.txt       ───► Текст для вкладки Лицензия +
-│   │   ├── comment.txt       ───► Текст для вкладки Комментарий +
-│   │   └── .meta.json        ───► Кто, что и когда правил в этом релизе +
-│   │ +
-│   └── Релиз_v2.0_Бета/ +
-│ +
-├── 10294811/                 ───► Личная папка Обычного Администратора №2 +
-│   └── ... +
-│ +
-└── system_logs/              ───► Доступ только для Токена Супер-админа +
-    ├── admins_activity.log   ───► Кто из админов что делал в системе +
-    └── app_errors.log        ───► Все наши технические маяки для отладки+
  
-</code>+        // Открытие модалки 
 +        btnCreateProject.addEventListener('click', function() { 
 +            newProjectName.value = ''; 
 +            folderInput.value = ''; // Сбрасываем выбранные файлы 
 +            folderStatus.textContent = "Папка не выбрана"; 
 +            if (modalErrorBox) modalErrorBox.classList.add('hidden'); 
 +            btnModalSubmit.disabled = true; 
 +            btnModalSubmit.style.opacity = "0.6"; 
 +            projectModal.classList.remove('hidden'); 
 +            newProjectName.focus(); 
 +        }); 
 + 
 +        // Отмена 
 +        btnModalCancel.addEventListener('click', function() { 
 +            projectModal.classList.add('hidden'); 
 +        }); 
 + 
 +        // Нажатие на кнопку "Загрузить" -> отправка терабайтов кода по AJAX 
 +        btnModalSubmit.addEventListener('click', async function() { 
 +            const projectName = newProjectName.value.trim(); 
 +            if (modalErrorBox) modalErrorBox.classList.add('hidden'); 
 + 
 +            const formData = new FormData(); 
 +            formData.append('name', projectName); 
 + 
 +            // КРИТИЧЕСКИ ВАЖНО: Пакуем файлы вместе с их путями вложения 
 +            for (let i = 0; i folderInput.files.length; i++) { 
 +                let file = folderInput.files[i]; 
 +                formData.append('project_files[]', file); 
 +                // Передаем бэкенду относительный путь (например: "my_proj/src/main.php"
 +                formData.append('project_paths[]', file.webkitRelativePath); 
 +            } 
 + 
 +            // Меняем статус кнопки на время тяжелой загрузки 
 +            btnModalSubmit.disabled = true; 
 +            btnModalSubmit.textContent = "Загрузка..."; 
 + 
 +            try { 
 +                const response = await fetch('backend/router.php?action=create_project',
 +                    method: 'POST', 
 +                    body: formData 
 +                }); 
 +                const result = await response.json(); 
 +                 
 +                if (result.success) { 
 +                    projectModal.classList.add('hidden'); 
 +                    btnModalSubmit.textContent = "Загрузить"; 
 +                    location.reload(); 
 +                } else { 
 +                    btnModalSubmit.disabled = false; 
 +                    btnModalSubmit.textContent = "Загрузить"; 
 +                    if (modalErrorBox) { 
 +                        modalErrorBox.textContent = result.error; 
 +                        modalErrorBox.classList.remove('hidden'); 
 +                    } 
 +                } 
 +            } catch (error) { 
 +                // Возвращаем кнопку в исходное рабочее состояние 
 +                btnModalSubmit.disabled = false; 
 +                btnModalSubmit.textContent = "Загрузить"; 
 +                 
 +                // КРАСИВО ВЫВОДИМ ОШИБКУ СЕТИ В ТВОЮ ПЛАШКУ БЕЗ ВСЯКИХ АЛЕРТОВ 
 +                if (modalErrorBox) { 
 +                    modalErrorBox.textContent = "Критическая ошибка отправки файлов на сервер."; 
 +                    modalErrorBox.classList.remove('hidden'); 
 +                } 
 +            } 
 +        }); 
 +    } 
 + 
 + 
 + 
 + 
 + 
 + 
 + 
 +    // Локальное хранилище для текстов текущего выбранного релиза, 
 +    // чтобы переключать вкладки мгновенно без повторных запросов к серверу 
 +    let currentReleaseTexts = { 
 +        readme: '', 
 +        license: '', 
 +        comment: '', 
 +        log: 'Здесь будет выводиться лог действий (100 строк)...' 
 +    }; 
 + 
 +    /** 
 +     * Отрисовка таблицы файлов в верхнем окне 
 +     * @param {Array} files Массив файлов из JSON 
 +     */ 
 +    function renderFilesTable(files) { 
 +        if (!files || files.length === 0) { 
 +            filesList.innerHTML = `<tr><td colspan="4" class="text-center text-muted">В данном релизе нет файлов для скачивания.</td></tr>`; 
 +            return; 
 +        } 
 + 
 +        let html = ''; 
 +        files.forEach(file => { 
 +            const icon = file.is_dir ? '📁' : '📄'; 
 +            html += ` 
 +                <tr> 
 +                    <td><span style="margin-right: 8px;">${icon}</span> ${escapeHtml(file.name)}</td> 
 +                    <td class="text-muted">—</td> <!-- Примечания (коммиты) прикрутим позже через .meta.json --> 
 +                    <td class="text-muted">${file.date}</td> 
 +                    <td style="text-align: right;"><span class="text-muted" style="font-size: 12px;">${file.size}</span></td> 
 +                </tr> 
 +            `; 
 +        }); 
 +        filesList.innerHTML = html; 
 +    } 
 + 
 +    // Вспомогательная функция защиты от XSS при отрисовке имен файлов 
 +    function escapeHtml(string) { 
 +        return String(string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); 
 +    } 
 + 
 +    // 1. СЛУШАЕМ ВЫБОР РЕЛИЗА В ВЫПАДАЮЩЕМ СПИСКЕ 
 +    releasesDropdown.addEventListener('change', async function() { 
 +        const selectedRelease = this.value; 
 + 
 +        if (!selectedRelease) { 
 +            // Если сбросили выбор — возвращаем интерфейс в исходное состояние 
 +            btnDownloadZip.disabled = true; 
 +            filesList.innerHTML = `<tr><td colspan="4" class="text-center text-muted">Выберите релиз из списка выше для просмотра файлов...</td></tr>`; 
 +            textEditor.value = 'Текст файла отсутствует или релиз не выбран...'; 
 +            return; 
 +        } 
 + 
 +        filesList.innerHTML = `<tr><td colspan="4" class="text-center text-muted">Загрузка данных релиза...</td></tr>`; 
 + 
 +        // Дергаем наш апи-модуль 
 +        const result = await Api.getReleaseData(selectedRelease); 
 + 
 +        if (result.success) { 
 +            // Активируем кнопку ZIP 
 +            btnDownloadZip.disabled = false; 
 +             
 +            // Сохраняем тексты в память для вкладок 
 +            currentReleaseTexts.readme = result.texts.readme || 'Файл README.txt пуст.'; 
 +            currentReleaseTexts.license = result.texts.license || 'Файл LICENSE.txt пуст.'; 
 +            currentReleaseTexts.comment = result.texts.comment || 'Файл COMMENT.txt пуст.'; 
 +             
 +            // Отрисовываем файлы в таблице верхнего окна 
 +            renderFilesTable(result.files); 
 + 
 +            // Автоматически показываем текст той вкладки, которая сейчас активна (по умолчанию README) 
 +            const activeTab = document.querySelector('.tab-btn.active').getAttribute('data-target'); 
 +            textEditor.value = currentReleaseTexts[activeTab]; 
 +        } else { 
 +            alert("Ошибка загрузки: " + result.error); 
 +            filesList.innerHTML = `<tr><td colspan="4" class="text-center" style="color: #cf222e;">${result.error}</td></tr>`; 
 +        } 
 +    }); 
 + 
 +    // 2. СЛУШАЕМ ПЕРЕКЛЮЧЕНИЕ ВКЛАДОК НИЖНЕГО ОКНА 
 +    tabButtons.forEach(button => { 
 +        button.addEventListener('click', function() { 
 +            tabButtons.forEach(btn => btn.classList.remove('active')); 
 +            this.classList.add('active'); 
 +             
 +            const targetTab = this.getAttribute('data-target'); 
 +             
 +            // Вытаскиваем текст из памяти без лишних запросов к PHP 
 +            textEditor.value = currentReleaseTexts[targetTab] || `Текст для вкладки ${targetTab.toUpperCase()} отсутствует.`; 
 +        }); 
 +    }); 
 +});
  
-===== Создаем файл backend/config/paths.php===== 
-Этот файл использует магические константы PHP и глобальные массивы сервера, чтобы автоматически подстроиться под любое окружение. 
-<code> 
-</code> 
  
-===== backend/config/salt.php ===== 
-Этот файл будет создаваться автоматически при самом первом запуске приложения. Он сгенерирует случайные 16-значные строки для ролей, которые заменят стандартные понятия super_admin и admin. Ни хакер, ни сторонний наблюдатель, заглянув в код, не смогут понять, какими правами обладает пользователь. 
-Шаг 1. Серверный генератор солей (backend/core/salt_generator.php)Этот скрипт проверяет, существует ли уже файл salt.php. Если файла нет, он использует современную и безопасную функцию random_bytes(), генерирует два уникальных токена ролей и упаковывает их в чистый PHP-формат, возвращающий массив.Создаем файл backend/core/salt_generator.php: 
tmp_27.06.2026.1782546208.txt.gz · Последнее изменение: VladPolskiy

Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: Public Domain
Public Domain Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki