feat: outbound webhooks for node lifecycle — closes the "users + nodes" gap from the comparison
Last remaining actionable row from the docs.rw comparison table pulled two commits ago: "Webhook event support — Users + nodes (Remnawave) / Users only (Marzban)". Every webhook we send is subscription/payment events — user-side only, same as Marzban, even after last commit's revoke/hold/resume additions. Zero node events. node.added on creation, node.deleted on deletion (captures the node's label before it's gone, since delete_node doesn't return the row), node.enabled/node.disabled on the PATCH route — but only when the enabled field actually changes value, not on every save. Editing just the label, or PATCHing enabled to the same value it already had, correctly fires nothing — checked this specifically since a naive "enabled is in the request body" check would have spammed an event on every harmless edit of an already-enabled node. Verification: same two-part approach as the subscription lifecycle webhooks. AST-extracted the actual admin_update_node() body out of api.py (still can't import it directly) and ran it against a fake db/webhooks module — 5 cases: enabling, disabling, a same-value no-op save, and an unrelated-field-only edit, confirming the webhook fires exactly when and only when it should. Then a real local HTTP server for all four event types through the actual webhooks.send(), receiver-side HMAC recomputed independently from its own copy of the secret and compared against X-Signature, not trusted from the sender. README's feature list had also fallen behind the last three commits (webhooks, hold/pause, subscription search never got a bullet) — caught up all three while I was in there, not just the one this commit adds. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
b8c4949201
commit
bbbab9998e
3 changed files with 16 additions and 3 deletions
|
|
@ -616,7 +616,7 @@
|
|||
<h2>Платежи и вебхуки</h2>
|
||||
<p>Вкладка Платежи → «Настройка приёма платежей» собирает публичную оферту и политику конфиденциальности (<code>/offer</code>, <code>/privacy</code>) из введённых реквизитов — ЮKassa их спросит при регистрации магазина. Дата вступления в силу проставляется один раз, правки реквизитов её не двигают.</p>
|
||||
<p>Ключи ЮKassa проверяются вживую через их <code>/v3/me</code> перед сохранением; у Platega такого эндпоинта нет, ключи сохраняются без проверки. Оба провайдера включаются независимо.</p>
|
||||
<p>Исходящие вебхуки (Настройки → Webhook на события) — панель стучится на указанный URL при оплате (<code>payment.paid</code>), ручной выдаче подписки админом (<code>subscription.granted_by_admin</code>), отзыве (<code>subscription.revoked</code>), постановке на паузу (<code>subscription.held</code>) и возобновлении (<code>subscription.resumed</code>). Тело подписано <code>X-Signature</code> (HMAC-SHA256). Секрет выдаётся один раз и не меняется при правке URL — для интеграций со своими системами, без опроса API.</p>
|
||||
<p>Исходящие вебхуки (Настройки → Webhook на события) — панель стучится на указанный URL при оплате (<code>payment.paid</code>), ручной выдаче подписки админом (<code>subscription.granted_by_admin</code>), отзыве (<code>subscription.revoked</code>), постановке на паузу (<code>subscription.held</code>) и возобновлении (<code>subscription.resumed</code>), а также при добавлении (<code>node.added</code>), удалении (<code>node.deleted</code>) и включении/выключении ноды (<code>node.enabled</code>/<code>node.disabled</code> — только когда состояние реально поменялось, не на каждое сохранение формы редактирования). Тело подписано <code>X-Signature</code> (HMAC-SHA256). Секрет выдаётся один раз и не меняется при правке URL — для интеграций со своими системами, без опроса API.</p>
|
||||
<p>Вкладка Платежи → «Тарифы» — цены по срокам и общий рубильник приёма оплаты. Как и реквизиты с ключами провайдеров, это читается панелью напрямую из <code>.env</code> при каждом запросе — правки в UI применяются мгновенно везде (бот, API, проверка вебхуков), рестарт панели нигде не требуется.</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -723,7 +723,7 @@
|
|||
<div style="flex:0"><label class="f"> </label><button class="btn" onclick="saveWebhookSettings()">Сохранить</button></div>
|
||||
</div>
|
||||
<p class="check-hint">
|
||||
События: <code>payment.paid</code>, <code>subscription.granted_by_admin</code>, <code>subscription.revoked</code>, <code>subscription.held</code>, <code>subscription.resumed</code>. Тело — JSON <code>{"event": "...", "data": {...}}</code>, подписано заголовком <code>X-Signature</code> (HMAC-SHA256 от тела запроса на секрете ниже) — так получатель проверяет, что запрос реально от панели.
|
||||
События: <code>payment.paid</code>, <code>subscription.granted_by_admin</code>, <code>subscription.revoked</code>, <code>subscription.held</code>, <code>subscription.resumed</code>, <code>node.added</code>, <code>node.deleted</code>, <code>node.enabled</code>, <code>node.disabled</code>. Тело — JSON <code>{"event": "...", "data": {...}}</code>, подписано заголовком <code>X-Signature</code> (HMAC-SHA256 от тела запроса на секрете ниже) — так получатель проверяет, что запрос реально от панели.
|
||||
Секрет для проверки: <code id="webhook-secret-display">—</code>
|
||||
</p>
|
||||
<div id="webhook-result"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue