Это старая версия документа!
Содержание
Файл менеджер
🗺️ Общая архитектурная схема проекта
[ ПОЛЬЗОВАТЕЛЬ / БОТ / ХАКЕР ]
│
▼
┌────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────┘ └─────────────────────────┘
🧩 Карта маскировки данных в backend/config/
Поскольку мы закладываем параноидальную защиту, вот схема того, как скрыты наши конфигурационные файлы, к которым имеет доступ только серверный PHP:
backend/config/
│
├── salt.php ───► [Возвращает: Секретный токен Супера (16 симв.)]
│ [ Секретный токен Админа (16 симв.)]
│ (Хакер в коде видит только шум, роли замаскированы)
│
├── email.php ───► [Возвращает: настоящий_email@супера.com]
│ (Спрятан от парсеров, выводится только при ошибке админа)
│
├── ip_blacklist.txt ───► [Список забаненных IP-адресов]
│
└── users.json ───► [ База данных - Перетасованный массив ]
┌──────────────────────────────────────────────┐
│ ID: 85910432 (Рандомный, отсортирован) │
│ Пароль: $2y$10$... (Защищенный хэш) │
│ Роль: x7R9wQ2pM4zL1vK8 (Случайный шум) │
│ Книга: $2y$10$... (Хэш контрольной строки) │
└──────────────────────────────────────────────┘
📂 Карта песочницы на диске (storage/)
Абсолютно безликая структура. Никаких названий компаний и имен. Только цифровые идентификаторы, сгенерированные нашей системой тасования.
storage/
│
├── 85910432/ ───► Личная папка Супер-админа (или Главного Админа)
│ ├── .system/
│ │ └── activity.log ───► Локальный лог действий на 100 строк (Ротация)
│ │
│ ├── Релиз_v1.0_Стабильный/ ───► Папка, которая отобразится в выпадающем списке
│ │ ├── app.exe ───► Файл для скачивания
│ │ ├── readme.txt ───► Текст для вкладки README
│ │ ├── license.txt ───► Текст для вкладки Лицензия
│ │ ├── comment.txt ───► Текст для вкладки Комментарий
│ │ └── .meta.json ───► Кто, что и когда правил в этом релизе
│ │
│ └── Релиз_v2.0_Бета/
│
├── 10294811/ ───► Личная папка Обычного Администратора №2
│ └── ...
│
└── system_logs/ ───► Доступ только для Токена Супер-админа
├── admins_activity.log ───► Кто из админов что делал в системе
└── app_errors.log ───► Все наши технические маяки для отладки
Создаем файл backend/config/paths.php
Этот файл использует магические константы PHP и глобальные массивы сервера, чтобы автоматически подстроиться под любое окружение.
test
assets/js/app.js document.addEventListener('DOMContentLoaded', function() { const releasesDropdown = document.getElementById('releases-dropdown'); const btnDownloadZip = document.getElementById('btn-download-zip'); const filesList = document.getElementById('files-list'); const textEditor = document.getElementById('text-editor'); const tabButtons = document.querySelectorAll('.tab-btn'); Локальное хранилище для текстов текущего выбранного релиза,
// чтобы переключать вкладки мгновенно без повторных запросов к серверу
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
// 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()} отсутствует.`;
});
});
});
