панель: многостраничная, Last Heard, SSE, контроль доступа

Панель:
 • шесть страниц с маршрутизацией по хэшу: Обзор, Last Heard, Хотспоты,
   Группы, Доступ, Настройки;
 • журнал приходит живым потоком (SSE) вместо опроса раз в две секунды;
 • Last Heard: кто, куда, когда, сколько, через какой хотспот;
 • в таблице хотспотов — ВСЁ, что устройство сообщает в RPTC.

Контроль доступа (два независимых уровня, их легко перепутать):
 • хотспот пускается по паролю — у каждого может быть свой, плюс режим
   «только из списка» и отключение конкретного устройства;
 • абонент (поле src, владелец рации) допускается белым/чёрным списком с
   поддержкой диапазонов. Встроено в штатный хук guard ядра.

Два бага по дороге:
 • поля RPTC читались как colorcode/latitude/longitude, а разборщик отдаёт
   color_code/lat/lon — половина таблицы молча пустовала;
 • столбец «описание» на деле содержит СТРАНУ: именно туда Pi-Star кладёт
   country. Переименован.

tests/test_access.py 20/20, tests/test_panel.py 31/31 (включая наполнение
Last Heard настоящим рукопожатием и голосовым вызовом по UDP).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvX94Vwy7vXWEuiFm1g9Av
Этот коммит содержится в:
2026-07-29 01:32:46 +09:00
co-authored by Claude Opus 4.8
родитель 47cb85327a
Коммит cb4e8a9bb1
9 изменённых файлов: 1045 добавлений и 125 удалений
+12 -3
Просмотреть файл
@@ -32,9 +32,18 @@ DEFAULTS: dict[str, Any] = {
{"tg": 1, "name": "Общая", "slot": 1},
],
"parrot": {"enabled": True, "tg": PARROT_TG, "delay": 2.0},
# Хотспоты, которым разрешено подключаться. Пустой список = пускать любой,
# кто знает пароль (удобно на старте, в панели можно перевести в белый список).
"peers_allowlist": [],
# Зарегистрированные хотспоты: у каждого может быть свой пароль.
# [{"peer_id": 2501001, "password": "...", "name": "Гараж", "enabled": true}]
# Пустой password = использовать общий passphrase.
"hotspots": [],
# true — пускать любой хотспот, знающий общий passphrase (удобно на старте);
# false — только те, что перечислены в hotspots.
"allow_unknown_hotspots": True,
# Кому разрешён ВЫХОД В ЭФИР (поле src в кадре). Это не то же, что доступ
# хотспота: на одном устройстве работают разные люди.
# mode: off | allow (белый список) | deny (чёрный список)
# list: числа и диапазоны "2501000-2501999"
"subscribers": {"mode": "off", "list": []},
"log_keep": 500, # сколько последних событий держать для панели
}