# reg — панель регистрации DMR ID Небольшой PHP-скрипт для радиолюбительского DMR-сервера (XLX/HBLink, узел DMRYKT). Решает одну задачу: выдача и учёт локальных DMR ID. Радиолюбитель заполняет заявку с позывным и контактом в Telegram, скрипт присваивает свободный ID из диапазона 1410000–1419999 и кладёт заявку в очередь. Администратор через веб-админку подтверждает, редактирует, блокирует или удаляет записи, после чего подтверждённые записи выгружаются в файл `dmrid.dat` формата `ID;CALLSIGN;`, который читают XLX и Pi-Star. Базы данных нет: всё хранится в JSON-файле и в текстовом `.dat`. ## Стек - PHP (процедурный, без фреймворка и без Composer). Версия не определена из кода, но используются стрелочные функции — требуется PHP 7.4+. - Хранилище: `db.json` (структура `{"count": N, "results": [...]}`) и `dmrid.dat` (`ID;CALLSIGN;` построчно). - Frontend: HTML, CSS, ванильный JavaScript (клиентская валидация и AJAX-проверка уникальности). - Веб-сервер с PHP — по README ожидается размещение в `/var/www/xlxd/reg` рядом с XLX-сервером. - Внешних API нет. ## Структура | Путь | Назначение | | --- | --- | | `index.php` | Стартовая страница со ссылками на админку, XLX-сервер и файлы баз | | `register.php` | Публичная форма заявки на DMR ID: валидация позывного и Telegram, генерация свободного ID, запись в `db.json` с `confirmed: false` | | `admin.php` | Админка: HTML, таблица подтверждённых записей, поиск, сортировка, пагинация, форма добавления вручную | | `adminLogic.php` | Вся логика админки, подключается первой строкой `admin.php`. Обрабатывает `sync`, `confirm`, `block`, `delete`, `edit`, `ajax_check`, добавление через POST, а также поиск, сортировку и пагинацию | | `check_unique.php` | Отдельный JSON-эндпоинт проверки уникальности ID и позывного по `db.json` и `dmrid.dat` | | `db.json` | Основная база заявок и записей | | `dmrid.dat` | Выгрузка подтверждённых записей для XLX/Pi-Star | | `assets/css/style.css` | Стили админки | | `assets/js/main.js` | Клиентская валидация формы добавления и AJAX-проверка уникальности | | `savedata/` | Резервная копия предыдущей версии: `admin.php`, `logic.php`, `db.json` (232 записи), `dmrid.dat` | | `README` | Исходное описание структуры проекта от автора | | `README.upstream.md` | Исходная инструкция по установке от автора | ## Как запустить Отдельного запуска нет — это набор PHP-файлов, который кладётся в корень веб-сервера. По инструкции автора (`README.upstream.md`): ```bash git clone <репозиторий> /var/www/xlxd/reg chmod 666 /var/www/xlxd/reg/db.json chmod 666 /var/www/xlxd/reg/dmrid.dat ln -s /var/www/xlxd/reg/dmrid.dat /xlxd/dmrid.dat ``` Адреса после установки: - админка — `https://ваш-сайт/reg/` - форма регистрации — `https://ваш-сайт/reg/register.php` Права `666` на файлы данных обязательны: процесс PHP пишет в них напрямую через `file_put_contents`. ## Конфигурация Файлов конфигурации и переменных окружения нет. Всё, что можно поменять, захардкожено в исходниках: | Что | Где | Значение | | --- | --- | --- | | Пути к файлам данных | `adminLogic.php`, `register.php`, `check_unique.php` | `__DIR__ . '/db.json'`, `__DIR__ . '/dmrid.dat'` | | Диапазон выдаваемых ID | `register.php` | `rand(1410000, 1419999)` | | Формат позывного | `register.php`, `adminLogic.php` | `/^[A-Z0-9]{4,7}$/` | | Формат Telegram | `register.php` | `/^[a-zA-Z0-9_]{5,32}$/` | | Допустимый ID при ручном добавлении | `adminLogic.php` | 6–7 цифр, от 100000 до 9999999 | | Записей на страницу | `adminLogic.php` | `$perPage = 15` | | Ссылки на внешние ресурсы | `index.php`, `admin.php` | `https://xlx.dmrykt.ru/`, `https://dmrykt.ru/` | | Путь к стилям | `admin.php` | `/reg/assets/css/style.css` — приложение обязано лежать в подкаталоге `/reg` | ## Состояние Рабочий, но простой скрипт; развитие остановлено. 8 коммитов, последний — 12.07.2025, из них четыре последних правили только README. Реального кода — один заход «Add files via upload» 11.07.2025. Функциональность закрыта: заявка, подтверждение, редактирование, блокировка, удаление, синхронизация в `.dat`, поиск, сортировка, пагинация. `db.json` и `dmrid.dat` в репозитории пустые — боевые данные вычищены, но в `savedata/` лежит старый снимок на 232 записи. ## Что не доделано - **Админка полностью открыта.** В `admin.php` и `adminLogic.php` нет ни аутентификации, ни проверки сессии, ни CSRF-токенов. Любой, кто знает адрес, может добавлять, редактировать, блокировать и удалять записи обычными GET-запросами вида `admin.php?delete=1410001`. Прикрывать доступ нужно на уровне веб-сервера (basic auth или ограничение по IP). - Нет блокировок при записи. `file_get_contents` + `file_put_contents` без `flock`: две одновременные заявки могут затереть друг друга. - Обработчик `edit` в `adminLogic.php` дублирует работу: сначала точечно правит строку в `dmrid.dat`, а следом полностью перезаписывает файл из всех записей `db.json` — включая неподтверждённые. Это расходится с логикой `sync`, которая пишет только подтверждённые. - В `check_unique.php` разбор `db.json` идёт по верхнему уровню (`foreach ($json as $entry)`), а не по `$json['results']` — проверка по JSON-базе фактически не работает. Этот файл к тому же дублирует `ajax_check` из `adminLogic.php` и, судя по коду фронтенда, не вызывается. - Сравнение `$item['id'] === $confirmId` строгое, а ID в `db.json` может оказаться строкой — тогда подтверждение и блокировка молча ничего не сделают. - В `admin.php` в шапке таблицы (``) используется `$row['id']` вне цикла — обращение к неопределённой переменной. - Сообщения об ошибках выводятся без `htmlspecialchars` в `register.php`, хотя тексты и константные. - Заявки принимаются без капчи и без ограничения частоты — форма открыта для спама. - В `README.upstream.md` указан адрес кошелька USDT (TRC20) для донатов — при публикации репозитория стоит решить, нужен ли он там. - В `savedata/db.json` лежат персональные данные пользователей: позывные, имена, фамилии, города и Telegram-контакты 232 человек. Перед тем как делать репозиторий публичным, этот файл лучше убрать. - Тестов нет, CI нет, логирования действий администратора нет.