پرش به مطلب اصلی

Webhookها

Webhookها به پنل اجازه می‌دهند خودش به سامانه‌های بیرونی خبر دهد که چه رخ داده است: کاربر ساخته شد، نود افتاد، ترافیک تمام شد. پنل یک درخواست POST با بدنه JSON به آدرس شما می‌فرستد، بنابراین اسکریپت، CRM، سامانه پایش یا چت‌بات می‌تواند آن را دریافت کند.

فعال‌سازی​

Webhookها به‌صورت پیش‌فرض خاموش‌اند. تنظیمات در .env پنل تعیین می‌شوند:

متغیرپیش‌فرضتوضیح
WEBHOOK_ENABLEDfalseارسال 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-SignatureHMAC-SHA256 از بدنه درخواست، در hex
X-XLADA-Timestampزمان رویداد در ISO 8601
Content-Typeapplication/json
User-AgentXLADA

امضا به‌صورت 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 فقط در رویدادهای یادآوری پر می‌شود:

فیلدرویدادمقدار
notConnectedAfterHoursuser.not_connectedچند ساعت کاربر متصل نشده است
expirationuser.expirationساعت نسبت به expireAt: منفی — پیش از انقضا، مثبت — پس از آن

در همه رویدادهای دیگر meta برابر null است.

فهرست رویدادها​

حوزه (scope)رویدادها
useruser.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_devicesuser_hwid_devices.added, user_hwid_devices.deleted
nodenode.created, node.modified, node.disabled, node.enabled, node.deleted, node.connection_lost, node.connection_restored, node.traffic_notify
serviceservice.panel_started, service.login_attempt_failed, service.login_attempt_success, service.subpage_config_changed, service.api_token_created, service.api_token_deleted
errorserrors.bandwidth_usage_threshold_reached_max_notifications
crmcrm.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_blockertorrent_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 پاسخ دهید و پردازش را به پس‌زمینه ببرید. تا وقتی گیرنده پاسخ ندهد، پنل اتصال را باز نگه می‌دارد و پاسخ‌های کند صف را انبار می‌کنند.

گام‌های بعدی​