2 * @description RMM-PSA Webhook Notifier Plugin for MeshCentral
3 * @author Independent Business Group
8 * This plugin sends webhooks to the RMM-PSA backend for real-time device events:
9 * - Device connect/disconnect
10 * - Device information changes
11 * - Node status updates
13 * This provides instant updates (< 2s) vs 5-minute polling lag.
18module.exports['rmm-webhook'] = function(pluginHandler) {
20 const parent = pluginHandler.parent;
22 // Configuration from environment variables or config.json
23 const webhookUrl = process.env.MESHCENTRAL_WEBHOOK_URL ||
24 parent.config.settings.rmmWebhookUrl ||
25 'https://rmm-psa-backend-t9f7k.ondigitalocean.app/api/webhooks/meshcentral';
27 const webhookSecret = process.env.MESHCENTRAL_WEBHOOK_SECRET ||
28 parent.config.settings.rmmWebhookSecret ||
31 console.log('=================================================');
32 console.log('📡 RMM-PSA Webhook Notifier Plugin Loaded');
33 console.log(` Webhook URL: ${webhookUrl}`);
34 console.log(` Secret configured: ${webhookSecret ? 'YES ✅' : 'NO ⚠️'}`);
35 console.log('=================================================');
38 * Send webhook to RMM-PSA backend
40 async function sendWebhook(event, node, meshId) {
42 const https = require('https');
43 const http = require('http');
44 const url = require('url');
46 const parsedUrl = url.parse(webhookUrl);
47 const protocol = parsedUrl.protocol === 'https:' ? https : http;
49 const payload = JSON.stringify({
51 nodeId: node._id || node.nodeid,
54 meshId: meshId || node.meshid,
55 connected: (node.conn & 1) === 1,
56 timestamp: new Date().toISOString(),
58 platform: node.osdesc,
65 hostname: parsedUrl.hostname,
66 port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
70 'Content-Type': 'application/json',
71 'Content-Length': payload.length,
72 'X-MeshCentral-Secret': webhookSecret
76 const req = protocol.request(options, (res) => {
77 let responseData = '';
78 res.on('data', (chunk) => { responseData += chunk; });
80 if (res.statusCode === 200) {
81 console.log(`✅ [RMM-Webhook] ${event} for ${node.name} (${res.statusCode})`);
83 console.log(`⚠️ [RMM-Webhook] ${event} for ${node.name} (${res.statusCode}) - ${responseData}`);
88 req.on('error', (error) => {
89 console.error(`❌ [RMM-Webhook] Error sending ${event}:`, error.message);
96 console.error('❌ [RMM-Webhook] Send error:', error.message);
101 * Hook: Device connected
102 * Called when a device connects to MeshCentral
104 obj.hook_onDeviceConnected = function(device, meshId) {
105 console.log(`📱 [RMM-Webhook] Device connected: ${device.name}`);
106 sendWebhook('device.connect', device, meshId);
110 * Hook: Device disconnected
111 * Called when a device disconnects from MeshCentral
113 obj.hook_onDeviceDisconnected = function(device, meshId) {
114 console.log(`📴 [RMM-Webhook] Device disconnected: ${device.name}`);
115 sendWebhook('device.disconnect', device, meshId);
119 * Hook: Device changed
120 * Called when device information is updated
122 obj.hook_onDeviceChanged = function(device, meshId) {
123 console.log(`🔄 [RMM-Webhook] Device changed: ${device.name}`);
124 sendWebhook('device.change', device, meshId);
128 * Hook: Node connect event
129 * Alternative hook for node connections
131 obj.hook_onNodeConnect = function(device, meshId) {
132 console.log(`📱 [RMM-Webhook] Node connected: ${device.name}`);
133 sendWebhook('node.connect', device, meshId);
137 * Hook: Node disconnect event
138 * Alternative hook for node disconnections
140 obj.hook_onNodeDisconnect = function(device, meshId) {
141 console.log(`📴 [RMM-Webhook] Node disconnected: ${device.name}`);
142 sendWebhook('node.disconnect', device, meshId);
145 // Export functions (required by plugin system)