Старый README сохранён как README.upstream.md.
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 YooKassahttps://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. VERSION — 0.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»), что стоит сверять с этим списком.