Webhookها
Webhookها به پنل اجازه میدهند خودش به سامانههای بیرونی خبر دهد که چه رخ داده
است: کاربر ساخته شد، نود افتاد، ترافیک تمام شد. پنل یک درخواست POST با بدنه
JSON به آدرس شما میفرستد، بنابراین اسکریپت، CRM، سامانه پایش یا چتبات میتواند
آن را دریافت کند.
فعالسازی
Webhookها بهصورت پیشفرض خاموشاند. تنظیمات در .env پنل تعیین میشوند:
| متغیر | پیشفرض | توضیح |
|---|---|---|
WEBHOOK_ENABLED | false | ارسال Webhookها را فعال میکند |
WEBHOOK_URL | — | آدرس گیرنده؛ چند آدرس — با کاما |
WEBHOOK_SECRET_HEADER | — | راز امضای payload، دقیقاً 64 کاراکتر [a-zA-Z0-9] |
WEBHOOK_ENABLED=true
WEBHOOK_URL=https://example.com/xlada/webhook
WEBHOOK_SECRET_HEADER=vsmu67Kmg6R8FjIOF1WUY8LWBHie4scdEqrfsKmyf4IAf8dY3nFS0wwYHkhh6ZvQ
با WEBHOOK_ENABLED=true هر دو متغیر الزامیاند. WEBHOOK_URL باید با
http:// یا https:// آغاز شود، در غیر این صورت کانتینر بالا نمیآید. راز
امضا را فقط در .env نگه دارید — گیرنده با آن اصالت درخواستها را بررسی
میکند.
اینکه کدام رویدادها فرستاده شوند را فایل
/var/lib/remnawave/configs/notifications/notifications-config.yml داخل
کانتینر تعیین میکند. بهصورت پیشفرض در ایمیج همه رویدادها روی هر دو کانال
فعالاند. رویدادی که در فایل نباشد خاموش شمرده میشود. برای اینکه ویرایشها
بمانند، فایل خودتان را روی شاخه
/var/lib/remnawave/configs/notifications mount کنید.
events:
user.created:
telegram: true
webhook: true
user.expired:
telegram: false
webhook: true
# آدرس اضافی فقط برای این رویداد
additionalWebhookUrls:
- https://example.com/xlada/critical
امضای payload
پنل با هر درخواست هدرهای سرویس را میفرستد:
| هدر | مقدار |
|---|---|
X-XLADA-Signature | HMAC-SHA256 از بدنه درخواست، در hex |
X-XLADA-Timestamp | زمان رویداد در ISO 8601 |
Content-Type | application/json |
User-Agent | XLADA |
امضا بهصورت HMAC-SHA256(راز, بدنه_درخواست) محاسبه و با هدر مقایسه میشود.
باید دقیقاً بدنه اصلی مقایسه شود، نه JSON تجزیهشده.
import { createHmac, timingSafeEqual } from 'node:crypto';
function isValidSignature(rawBody, secret, signature) {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}
فرمت payload
در بدنه همیشه چهار فیلد وجود دارد: scope، event، timestamp و data.
رویدادهای کاربر بهعلاوه meta دارند.
{
"scope": "service",
"event": "service.panel_started",
"timestamp": "2026-01-07T11:57:29.426Z",
"data": {
"panelVersion": "1.1.1"
}
}
فیلد meta فقط در رویدادهای یادآوری پر میشود:
| فیلد | رویداد | مقدار |
|---|---|---|
notConnectedAfterHours | user.not_connected | چند ساعت کاربر متصل نشده است |
expiration | user.expiration | ساعت نسبت به expireAt: منفی — پیش از انقضا، مثبت — پس از آن |
در همه رویدادهای دیگر meta برابر null است.
فهرست رویدادها
حوزه (scope) | رویدادها |
|---|---|
user | user.created, user.modified, user.deleted, user.revoked, user.disabled, user.enabled, user.limited, user.expired, user.traffic_reset, user.first_connected, user.bandwidth_usage_threshold_reached, user.not_connected, user.expiration |
user_hwid_devices | user_hwid_devices.added, user_hwid_devices.deleted |
node | node.created, node.modified, node.disabled, node.enabled, node.deleted, node.connection_lost, node.connection_restored, node.traffic_notify |
service | service.panel_started, service.login_attempt_failed, service.login_attempt_success, service.subpage_config_changed, service.api_token_created, service.api_token_deleted |
errors | errors.bandwidth_usage_threshold_reached_max_notifications |
crm | crm.infra_billing_node_payment_in_7_days, crm.infra_billing_node_payment_in_48hrs, crm.infra_billing_node_payment_in_24hrs, crm.infra_billing_node_payment_due_today, crm.infra_billing_node_payment_overdue_24hrs, crm.infra_billing_node_payment_overdue_48hrs, crm.infra_billing_node_payment_overdue_7_days |
torrent_blocker | torrent_blocker.report |
رویدادهای user.not_connected و user.expiration فقط با فعال بودن
NOT_CONNECTED_USERS_NOTIFICATIONS_ENABLED و
EXPIRATION_NOTIFICATIONS_ENABLED میآیند. رویدادهای user_hwid_devices.*
فقط زمانی پدید میآیند که محدودیت دستگاه فعال باشد و صرفاً به Webhookها
میروند — در Telegram تکرار نمیشوند.
تلاشهای مجدد
تحویل از طریق صف انجام میشود: برای هر آدرس یک کار جدا با محدودیت 100 ارسال همزمان ساخته میشود. اگر گیرنده پاسخ ندهد، درخواست 3 بار با فاصله 5 ثانیه تکرار میشود — در کل تا چهار تلاش. پس از آن خطا در لاگ پنل نوشته میشود و کار با نگهداری محدود در صف میماند.
در notifications-config.yml میتوان برای رویداد مشخصی آدرسهای اضافی تعیین
کرد (additionalWebhookUrls). آنها به آدرسهای WEBHOOK_URL افزوده میشوند،
نه اینکه جایگزینشان شوند.
به Webhook بیدرنگ با 2xx پاسخ دهید و پردازش را به پسزمینه ببرید. تا وقتی
گیرنده پاسخ ندهد، پنل اتصال را باز نگه میدارد و پاسخهای کند صف را انبار
میکنند.
گامهای بعدی
- اعلانها — Telegram، آستانههای ترافیک و یادآوریها.
- متغیرهای محیطی — همه کلیدهای پنل.
- API Specification — طرحهای داده برای هر رویداد.
- امنیت پنل — چگونه گیرنده Webhook را محافظت کنیم.