Skip to content

Каталог событий ​

Все события, которые порождает панель, с областью доступа (scope), нужной получателю, и полями, которые они несут. Подписчики-вебхуки, дополнения, чаты Telegram и адреса электронной почты выбирают из этого списка. Как настроить подписчика, см. Вебхуки и события; как события ставятся в очередь и доставляются — Шина событий.

Тот же каталог отдаёт сама панель по адресу GET /api/v1/events; читать его может любой API-токен.

Конверт ​

Каждое событие приходит в вебхук как POST с телом JSON:

json
{
  "v": 1,
  "id": 4182,
  "event": "user.quota_warning",
  "time": 1760180000,
  "data": {
    "userId": 57,
    "name": "alice",
    "adminId": 3,
    "usedPercent": 81,
    "threshold": 80,
    "used": 86973087744,
    "volume": 107374182400
  },
  "recipients": [3]
}
Поле
vверсия конверта, 1
idидентификатор события: одинаков для всех подписчиков и при каждом повторе
eventимя события
timeкогда событие возникло, в секундах Unix
dataданные: поля, перечисленные для события ниже
recipientsидентификаторы администраторов, которых касается событие, если касается; иначе поле отсутствует

Время в данных указывается в секундах Unix, объём трафика — в байтах. Данные никогда не содержат пароль, ключ или ссылку подписки; если они нужны, читайте их через API с токеном.

Заголовки и подпись ​

Заголовок
Content-Typeapplication/json
User-Agentnexora-panel
X-Nexora-Eventимя события
X-Nexora-Deliveryидентификатор доставки: одинаков при каждом повторе и при Отправить снова
X-Nexora-Signaturet=<unix seconds>,v1=<hex>, есть, если у подписчика задан секрет

v1 — это HMAC-SHA256 в шестнадцатеричном виде от метки времени, точки и исходного тела, с ключом — секретом подписи подписчика:

text
v1 = hex( HMAC-SHA256( secret, "<t>" + "." + <raw body bytes> ) )

Получатель должен:

  1. Прочитать исходное тело до разбора; заново сериализованное тело не совпадёт.
  2. Заново вычислить v1 своим секретом и сравнить за постоянное время.
  3. Отклонять запрос, чей t отличается от его собственных часов больше чем на пять минут, чтобы перехваченную доставку нельзя было повторить позже.
  4. Быстро отвечать 2xx. Любой другой ответ или отсутствие ответа в течение десяти секунд считается ошибкой, и доставка повторяется.

На Python:

python
import hashlib, hmac, time

def verify(secret: bytes, header: str, body: bytes, max_skew: int = 300) -> bool:
    parts = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
    t, v1 = parts.get("t"), parts.get("v1")
    if not t or not v1 or abs(time.time() - int(t)) > max_skew:
        return False
    mac = hmac.new(secret, t.encode() + b"." + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, v1)

Чтобы обрабатывать каждое событие один раз, храните уже обработанные id. Восстановление из резервной копии возвращает идентификаторы копии, поэтому получатель, который помнит id между восстановлениями, должен начать сначала, получив panel.restore_applied.

Кто что получает ​

Область доступа. Каждое событие называет область, нужную получателю. Дополнение может запросить событие, только если и его токен имеет эту область, и экран согласия показывает обе (область :write включает соответствующую :read). Чат Telegram или адрес почты получает событие, только если роль его учётной записи имеет эту область. Вебхук, который главный администратор добавил вручную, получает все события, названные в его фильтре.

Семейства. Часть до точки — семейство, и оно определяет маршрутизацию:

СемействоО чёмНазывает учётную запись
user.*учётная запись клиентада: adminId — реселлер-владелец (0 для панели)
node.*узелнет
admin.*учётная запись операторада: adminId — эта учётная запись
panel.*сама панельнет

Подписчик, у которого в Только события о выбрана одна учётная запись, а также любой чат или адрес реселлера получают только события, называющие эту учётную запись. Поэтому события node.* и panel.* никогда не доходят до реселлера.

Массовая операция порождает одно событие. Массовая операция на странице пользователей порождает один user.bulk, а генерация пачки учётных записей — один user.generated, сколько бы учётных записей они ни затронули.

user.* ​

Каждое событие user.* несёт эти три поля и поля из своей строки:

ПолеТип
userIdintegerидентификатор учётной записи
namestringимя учётной записи
adminIdintegerреселлер-владелец; 0 для панели
СобытиеОбластьКогда возникаетДоп. поля
user.createdusers:readУчётная запись создана.
user.deletedusers:readУчётная запись удалена.
user.enabledusers:readПанель снова включила учётную запись, например после продления или сброса расхода.
user.disabledusers:readПанель выключила учётную запись. reason — volume, expiry, resale-volume или resale-expiry.reason
user.quota_reachedusers:readСлучай user.disabled по трафику: учётная запись израсходовала оплаченное.reason
user.expiredusers:readСлучай user.disabled по дате: время учётной записи вышло.reason
user.quota_warningusers:readУчётная запись перешла порог Израсходовано трафика (по умолчанию 80 %). Один раз на лимит и порог.usedPercent, threshold, used, volume
user.expiringusers:readУчётная запись вошла в одно из окон Дней до истечения (по умолчанию 7 и 1). Один раз на окно.expiry, daysLeft, threshold
user.renewedusers:readУчётная запись продлена. Несёт новую дату и трафик.expiry, volume
user.activatedusers:readТариф, который начинается с первого подключения, начался; дата окончания теперь зафиксирована.expiry
user.first_fetchusers:readКлиент впервые забрал конфигурации учётной записи. client называет приложение.client
user.device_limit_reachedusers:readУстройству отказано в подписке, потому что лимит устройств учётной записи заполнен. Не чаще раза в час на устройство.hwid, limit

Два события user.* несут свои поля вместо трёх перечисленных:

СобытиеОбластьКогда возникаетПоля
user.bulkusers:readЗавершилась одна массовая операция. op — операция; ids — учётные записи, до которых она дошла; adminId — реселлер, если её выполнил он.op, matched, affected, blocked, failed, actor, adminId, ids
user.generatedusers:readПачка учётных записей сгенерирована по шаблону имени.pattern, count, skipped, planId, actor, adminId

Пороги задаются на странице Вебхуки и события.

node.* ​

Большинство событий node.* несут идентификатор и имя узла:

СобытиеОбластьКогда возникаетПоля
node.connectednodes:readУзел начал отвечать панели. warnings перечисляет то, что он не запустил.nodeId, name, status, message, warnings
node.disconnectednodes:readУзел перестал отвечать. message — ошибка.nodeId, name, status, message, warnings
node.limit_reachednodes:readУзел достиг своего периодического лимита трафика и выключен. period — цикл.nodeId, name, used, limit, period
node.disk_highnodes:readКорневая файловая система узла перешла порог Заполнение диска (по умолчанию 90 %). Один раз на переход.поля хоста
node.disk_recoverednodes:readЗначение опустилось ниже порога не меньше чем на пять пунктов.поля хоста
node.memory_highnodes:readПамять узла перешла порог Использование памяти (по умолчанию 90 %). Один раз на переход.поля хоста
node.memory_recoverednodes:readЗначение опустилось ниже порога не меньше чем на пять пунктов.поля хоста
node.rejections_highnodes:readЗа час узел отклонил через один блокирующий аутбаунд больше соединений, чем Отклонённых соединений в час (по умолчанию 100). outbound — тег этого аутбаунда; window — 1h. Один раз на переход.nodeId, name, outbound, count, threshold, window

Поля хоста: nodeId, name, resource (disk или memory), usedPercent, threshold, used и total (байты). Диск и память считываются каждые пять минут.

admin.* ​

СобытиеОбластьКогда возникаетПоля
admin.loginadmins:writeОператор вошёл.adminId, username, ip
admin.login_failedadmins:writeВход отклонён. username — то, что было введено; stage равен mfa при неверном двухфакторном коде.username, ip, stage
admin.login_new_ipadmins:writeОператор вошёл с адреса, отличного от адреса предыдущего входа.adminId, username, ip, previousIp
admin.2fa_changedadmins:writeОператор включил или выключил двухфакторную аутентификацию.adminId, username, enabled
admin.owner_changedadmins:writeПанель передана другому главному администратору. previous называет учётную запись, которая её отдала.adminId, username, previous
admin.resale_cap_reachedadmins:writeРеселлер израсходовал свой лимит или его учётная запись истекла; его пользователи отключились вместе с ним. reason — volume или expiry.adminId, username, reason, used, volume

panel.* ​

СобытиеОбластьКогда возникаетПоля
panel.startedstats:readПанель запустилась. restored говорит, что по пути было применено восстановление.version, listen, restored
panel.stoppingstats:readПанель штатно завершает работу, в том числе при перезапуске. panel.started без предшествующего panel.stopping означает, что панель остановилась неожиданно.version
panel.crashedstats:readПри ответе на запрос перехвачена внутренняя ошибка; панель продолжает работать. Не чаще раза в пять минут на маршрут и сообщение.method, path, error, version
panel.setup_completedsettings:readМастер настройки завершён, главный администратор создан.username, restart
panel.settings_changedsettings:readИзменена общая настройка панели (адрес, TLS, базовый путь и т. п.). Значение никогда не передаётся.setting, actor
panel.restore_appliedusers:readПоверх этой панели восстановлена резервная копия.superseded, tables, rows
panel.backup_donebackup:readРезервное копирование по расписанию записало архив.name, size
panel.backup_failedbackup:readРезервное копирование по расписанию не удалось; новая попытка — в течение часа.error
panel.backup_upload_failedbackup:readАрхив не дошёл до места хранения резервных копий. Он остаётся на сервере панели.destination, name, error
panel.cert_renewedcertificates:readСертификат, которым управляет панель, продлён.поля сертификата
panel.cert_renew_failedcertificates:readПродление не удалось; новая попытка — каждые шесть часов.поля сертификата, error
panel.cert_expiringcertificates:readЗагруженный вами сертификат подходит к концу срока; панель не может его продлить. Один раз на сертификат.поля сертификата
panel.ruleset_refresh_failedpool:readЕжечасное обновление наборов правил не удалось.error
panel.update_availablestats:readЕсть более новый выпуск панели. version — новый, current — работающий. Один раз на выпуск.version, current, url
panel.license_changedlicense:writeИзменилось состояние лицензии: лицензирована, истекла или бесплатна, либо новые лимиты.licensed, reason, previousReason, licenseId, issuedTo, expiresAt, limits
panel.license_expiringlicense:writeЛицензия заканчивается через 30, 14, 7 или 1 день. Один раз на порог. Льготного периода после даты нет.licenseId, issuedTo, expiresAt, daysLeft, threshold
panel.license_unverifiedlicense:writeПанель несколько дней не может подтвердить свою лицензию. deadline — когда она перейдёт на бесплатный уровень, если так продолжится.licenseId, verifiedAt, deadline, daysLeft
panel.ip_bannedsecurity:readЗащита входа заблокировала адрес.ip, until, reason
panel.addon_registeredtokens:writeДополнение зарегистрировано.поля дополнения
panel.addon_removedtokens:writeДополнение удалено вместе с его токеном и вебхуком. Само дополнение получает это событие первым, каким бы ни был его фильтр.поля дополнения
panel.addon_unhealthytokens:writeПуть проверки состояния дополнения дважды подряд не ответил. Один раз на переход.поля дополнения, error
panel.addon_recoveredtokens:writeНеисправное дополнение снова ответило на путь проверки состояния.поля дополнения
panel.addon_update_waitingtokens:writeНовая версия подписанного дополнения просит больше, чем у него есть, и ждёт одобрения.поля дополнения, version
panel.pingлюбаяТестовое событие, которое Отправить тестовое событие шлёт одному подписчику.subscriberId, name, by

Поля сертификата: kind (node или panel), certificateId, nodeId, name и notAfter. Поля дополнения: addonId, slug, name и signed.

Что события лицензии значат для вашей панели, см. Лицензия; о дополнениях — Страница дополнений.

Текст и изображения — CC BY 4.0.