2026-08-20 04:18:37 +09:00

XLX Server — панель управления XLX/DMR-рефлектором с платной регистрацией

Серверная часть частного XLX-рефлектора: устанавливает и обслуживает xlxd (мультипротокольный рефлектор XLX для DMR/D-STAR/YSF), а поверх него даёт PHP-панель, в которой радиолюбитель регистрируется по позывному, оплачивает доступ (YooKassa или ручной перевод с чеком) и получает внутренний DMR ID и отдельный пароль для подключения хотспота Pi-Star. Администратору панель даёт status/start/stop/restart xlxd, редактирование его конфигурационных файлов (xlxd.blacklist, xlxd.whitelist, xlxd.interlink, xlxd.terminal) и дашборд последних событий из лога рефлектора.

Проект — серверный ответ на репозиторий pistar (скрипты, которые прописывают чужие рефлекторы в хотспоты): здесь рефлектор свой, с учётом пользователей и оплатой.

Стек

  • PHP 8 без фреймворка: свой автозагрузчик (bootstrap.php, префикс Xlx\), роутинг на if в public/index.php, PDO.
  • MariaDB/MySQL, схема в database/schema.sql.
  • Apache 2 + libapache2-mod-php, FallbackResource /index.php.
  • systemd (xlxd.service), Bash-установщики, sudoers-правило для управления сервисом от www-data.
  • Frontend: ванильные HTML/CSS/JS (public/assets), без сборки и зависимостей.
  • Внешние зависимости: xlxd (сборка из https://github.com/LX3JL/xlxd.git), API YooKassa https://api.yookassa.ru/v3.
  • Железо/протоколы: DMR (UDP 62030 для Pi-Star/MMDVM), YSF (42000), дашборд xlxd (8080).

Структура

Путь Назначение
bootstrap.php Автозагрузчик, выбор конфига (config/config.php, иначе config/config.example.php), таймзона.
public/index.php Единственная точка входа: HTML-страницы / и /admin, JSON API, webhook YooKassa.
public/views/home.php Публичная страница: описание рефлектора, проверка позывного, форма регистрации.
public/views/admin.php Админка: поле admin token, форма настроек xlxd, редактор конфиг-файлов, управление сервисом.
public/assets/app.js, admin.js, styles.css Логика форм и запросов к API, стили.
src/Support/Config.php Доступ к конфигу через точечные ключи (billing.yookassa.shop_id).
src/Support/Database.php Создание PDO.
src/Support/Input.php, Response.php Чтение JSON-тела запроса, JSON-ответы и ошибки.
src/Support/Security.php Нормализация и валидация позывного (/^[A-Z0-9][A-Z0-9\/-]{2,18}$/), генерация пароля, подсказка пароля, проверка admin-токена.
src/Domain/RegistrationService.php Проверка позывного на дубли по локальной базе, регистрация пользователя, создание платежа.
src/Domain/PaymentService.php Создание платежа по тарифу, подтверждение вручную, приём и одобрение чека, обработка платежа YooKassa, продление access_expires_at.
src/Domain/YooKassaClient.php HTTP-клиент YooKassa: создание платежа с ключом идемпотентности, чтение платежа.
src/Domain/IdAllocator.php Выдача внутреннего DMR ID из диапазона (по умолчанию 9000001–9099999) с обработкой гонок по unique-индексу.
src/Domain/AccessCredentialService.php Генерация и ротация отдельного DMR-пароля (хранится хешем, в базе остаётся только подсказка вида abc...xyz).
src/Domain/AccessExportService.php CSV активных пользователей: позывной, DMR ID, username, подсказка пароля, срок доступа.
src/Domain/XlxdSettingsService.php Настройки xlxd в таблице server_settings, рендер /etc/xlx/xlxd.env, запись файла при apply=true.
src/Domain/XlxdConfigFileService.php Чтение и запись четырёх файлов xlxd из XLX_INSTALL_PATH (blacklist/whitelist/interlink/terminal).
src/Domain/XlxdRuntimeService.php systemctl is-active/is-enabled, `sudo -n systemctl start
database/schema.sql Таблицы: users, callsign_checks, dmr_ids, tariffs, payments, payment_receipts, access_credentials, server_settings, connection_logs, audit_logs, xlx_service_events + тариф по умолчанию (300 RUB / 30 дней).
scripts/install-system.sh Полный установщик: пакеты, MariaDB, схема, генерация config/config.php, /etc/xlx/xlxd.env, Apache vhost, sudoers, сборка xlxd, вывод admin-токена и webhook URL.
scripts/install-xlxd.sh Только сборка и установка xlxd из исходников, установка xlx-control и юнита.
scripts/xlx-control start/stop/restart/status/render-modules для xlxd, генерация xlxd.module из списка модулей.
scripts/firewall-ufw.sh Открытие портов ufw: 22/80/443 TCP и 8880, 10001, 10002, 30001, 30051, 42000, 62030 UDP.
deploy/apache/xlx-panel.conf Шаблон vhost с плейсхолдерами __PANEL_PORT__, __PUBLIC_DIR__.
deploy/systemd/xlxd.service Юнит xlxd типа forking через xlx-control.
docs/ ARCHITECTURE.md, ROADMAP.md, INSTALL_SYSTEM.md, INSTALL_XLXD.md, PAYMENTS_AND_REGISTRATION.md, ADMIN_DASHBOARD.md.
README.upstream.md Прежний README автора.

Как запустить

Полная установка на чистый Ubuntu/Debian-сервер (от root):

sudo apt update
sudo apt install -y git
git clone <адрес репозитория> xlx
cd xlx
sudo bash scripts/install-system.sh

Панель копируется в /opt/xlx-server, поэтому каталог клона после установки не нужен. Порт панели выбирается автоматически: 80, иначе 8088/8090/8091/8099. В конце установщик печатает адрес сайта, адрес админки, admin token, webhook URL для YooKassa и реквизиты БД — их нужно сохранить, повторно они не показываются.

С доменом и платежами:

sudo \
  XLX_PUBLIC_HOST=xlx.example.ru \
  XLX_PUBLIC_SCHEME=https \
  YOOKASSA_SHOP_ID=<id> \
  YOOKASSA_SECRET_KEY=<см. .env / secret store> \
  bash scripts/install-system.sh

Только рефлектор, без панели:

sudo mkdir -p /etc/xlx
sudo cp config/xlxd.env.example /etc/xlx/xlxd.env
sudo nano /etc/xlx/xlxd.env
sudo bash scripts/install-xlxd.sh
sudo systemctl start xlxd

Эксплуатация:

sudo systemctl {start|stop|restart|status} xlxd
sudo tail -f /var/log/xlxd.log
sudo bash scripts/firewall-ufw.sh

Основные маршруты:

Метод и путь Назначение
GET /, GET /dashboard Публичная страница и регистрация.
GET /admin Админка.
GET /api/health Проверка живости API.
GET /api/dashboard/stats Статус сервиса, счётчики, последние события лога.
GET /api/callsigns/check?callsign= Проверка формата и занятости позывного.
POST /api/register Регистрация: позывной, email, пароль.
POST /api/payments/receipt Отправка номера/ссылки чека при ручном переводе.
POST /api/webhooks/yookassa?token= Webhook YooKassa, обрабатывается только событие payment.succeeded.
POST /api/admin/payments/confirm Ручное подтверждение платежа.
POST /api/admin/receipts/approve Одобрение чека.
GET /api/admin/access/export CSV активных пользователей.
GET/POST /api/admin/xlxd/settings Чтение и сохранение настроек xlxd (с опциональной записью xlxd.env).
GET/POST /api/admin/xlxd/config-files Чтение и запись blacklist/whitelist/interlink/terminal.
GET/POST /api/admin/xlxd/runtime Статус и управление сервисом (start, stop, restart, status).

Все /api/admin/* требуют заголовок X-Admin-Token со значением admin-токена.

Конфигурация

Конфиг — config/config.example.php, все значения читаются из переменных окружения. Установщик генерирует config/config.php, который через putenv() задаёт значения и подключает example-файл. Реальные секреты после установки лежат в config/config.php и /etc/xlx/xlxd.env на сервере; в репозитории только плейсхолдеры change_me.

Переменная Назначение Пример
XLX_PUBLIC_HOST Домен или IP панели xlx.example.ru
XLX_PUBLIC_SCHEME Схема для base URL https
XLX_PANEL_PORT Порт Apache для панели 80
XLX_APP_ENV, XLX_DEBUG, XLX_TIMEZONE Окружение, отладка, таймзона production, 0, Europe/Moscow
XLX_DB_HOST, XLX_DB_PORT, XLX_DB_NAME, XLX_DB_USER, XLX_DB_PASSWORD Доступ к БД 127.0.0.1, 3306, xlx_server, xlx_user, см. вывод установщика
XLX_REFLECTOR_NAME Имя рефлектора XLX391
XLX_SYSOP_CALLSIGN, XLX_SYSOP_EMAIL, XLX_COUNTRY Данные сисопа N0CALL, admin@example.ru, RU
XLX_DMR_PORT, XLX_YSF_PORT, XLX_DASHBOARD_PORT Порты рефлектора 62030, 42000, 8080
XLX_DEFAULT_MODULE, XLX_MODULES Модуль по умолчанию и список модулей A, ABCDEFGHIJKLMNOPQRSTUVWXYZ
XLX_CONFIG_PATH, XLX_INSTALL_PATH, XLX_SOURCE_PATH, XLX_LOG_PATH, XLX_SERVICE_NAME Пути и имя сервиса /etc/xlx, /xlxd, /usr/src/xlxd, /var/log/xlxd.log, xlxd
XLX_REPO_URL Репозиторий исходников xlxd https://github.com/LX3JL/xlxd.git
XLX_MIN_ID, XLX_MAX_ID Диапазон выдаваемых внутренних DMR ID 9000001, 9099999
XLX_BILLING_MODE, XLX_CURRENCY, XLX_DEFAULT_DURATION_DAYS Режим оплаты, валюта, срок доступа yookassa, RUB, 30
XLX_MANUAL_TRANSFER_ENABLED, XLX_MANUAL_TRANSFER_INSTRUCTIONS Ручной перевод и текст инструкции 1, произвольный текст
YOOKASSA_SHOP_ID, YOOKASSA_SECRET_KEY Реквизиты магазина YooKassa см. config/config.php на сервере
YOOKASSA_RETURN_URL, YOOKASSA_WEBHOOK_SECRET, YOOKASSA_CAPTURE Возврат после оплаты, токен webhook, автосписание <base>/payment/return, случайный токен, 1
XLX_SESSION_NAME, XLX_PASSWORD_MIN_LENGTH, XLX_DMR_PASSWORD_LENGTH Имя сессии, минимальная длина пароля кабинета, длина DMR-пароля xlx_session, 10, 16
XLX_ADMIN_TOKEN Токен для X-Admin-Token генерируется установщиком (48 символов)
XLX_APP_DIR Каталог установки панели /opt/xlx-server

Файл /etc/xlx/xlxd.env (шаблон config/xlxd.env.example) читают xlx-control и юнит systemd.

Состояние

Прототип, доведённый до устанавливаемого состояния, работа остановлена. Все 12 коммитов сделаны в один день — 2026-05-18, последний коммит 2026-05-18. VERSION0.2.0-dev.

Установщик, схема БД, API регистрации и оплаты, управление xlxd и редактор его конфигов написаны и внутренне согласованы. На боевом сервере проект не подтверждён из кода.

Что не доделано

  • Ключевой пункт из плана автора — UDP access gateway, который должен реально пускать к xlxd только оплаченных пользователей, — не реализован. Сейчас оплата и выданный DMR-пароль ни на что не влияют: xlxd принимает подключения по своим правилам, а связь панели с ним ограничена CSV-экспортом (/api/admin/access/export) и ручным редактированием whitelist/blacklist.
  • Нет личного кабинета и вообще авторизации пользователей: пароль при регистрации хешируется и сохраняется, но маршрута логина и сессий нет, хотя настройки сессии в конфиге есть. Пользователь получает DMR ID и пароль один раз в ответе на POST /api/register и посмотреть их повторно не может.
  • Админка защищена одним статическим токеном в заголовке, который вводится в поле на странице; ролей moderator/admin из схемы БД код не использует, admin_user_id приходит с клиента и не проверяется.
  • Таблицы callsign_checks, connection_logs, audit_logs, xlx_service_events созданы, но код в них ничего не пишет. Дашборд «последние события» парсит текстовый лог /var/log/xlxd.log, а не БД.
  • Проверка позывного идёт только по локальной базе зарегистрированных; сверки с внешним реестром (RadioID/QRZ) нет, хотя поле source в callsign_checks это предполагает.
  • Тарифов в UI нет — используется единственная запись, вставленная схемой (300 RUB / 30 дней); управления тарифами и продлением из админки нет.
  • docs/ROADMAP.md почти целиком не отмечен: из ~70 пунктов выполнено 4. Не сделаны миграции БД, бэкапы и обновления, rate-limit, SSL helper, уведомления пользователю, автопродление.
  • Тестов нет вообще, CI нет, зависимостей Composer нет.
  • Маркеров TODO/FIXME в коде нет — незавершённость видна по roadmap и пустым таблицам.
  • README.upstream.md описывает часть возможностей как готовые (например, «экспорт активных пользователей для будущего access gateway»), что стоит сверять с этим списком.
S
Описание
No description provided
Readme
99 KiB
Languages
PHP 54.2%
JavaScript 13.8%
Shell 13.1%
Hack 11.9%
CSS 7%