EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
rmm-webhook.js
Go to the documentation of this file.
1/**
2 * @description RMM-PSA Webhook Notifier Plugin for MeshCentral
3 * @author Independent Business Group
4 * @copyright 2026
5 * @license Apache-2.0
6 * @version 1.0.0
7 *
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
12 *
13 * This provides instant updates (< 2s) vs 5-minute polling lag.
14 */
15
16'use strict';
17
18module.exports['rmm-webhook'] = function(pluginHandler) {
19 const obj = {};
20 const parent = pluginHandler.parent;
21
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';
26
27 const webhookSecret = process.env.MESHCENTRAL_WEBHOOK_SECRET ||
28 parent.config.settings.rmmWebhookSecret ||
29 '';
30
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('=================================================');
36
37 /**
38 * Send webhook to RMM-PSA backend
39 */
40 async function sendWebhook(event, node, meshId) {
41 try {
42 const https = require('https');
43 const http = require('http');
44 const url = require('url');
45
46 const parsedUrl = url.parse(webhookUrl);
47 const protocol = parsedUrl.protocol === 'https:' ? https : http;
48
49 const payload = JSON.stringify({
50 event: event,
51 nodeId: node._id || node.nodeid,
52 nodeName: node.name,
53 hostname: node.host,
54 meshId: meshId || node.meshid,
55 connected: (node.conn & 1) === 1,
56 timestamp: new Date().toISOString(),
57 node: {
58 platform: node.osdesc,
59 ip: node.ip,
60 mac: node.mac
61 }
62 });
63
64 const options = {
65 hostname: parsedUrl.hostname,
66 port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
67 path: parsedUrl.path,
68 method: 'POST',
69 headers: {
70 'Content-Type': 'application/json',
71 'Content-Length': payload.length,
72 'X-MeshCentral-Secret': webhookSecret
73 }
74 };
75
76 const req = protocol.request(options, (res) => {
77 let responseData = '';
78 res.on('data', (chunk) => { responseData += chunk; });
79 res.on('end', () => {
80 if (res.statusCode === 200) {
81 console.log(`✅ [RMM-Webhook] ${event} for ${node.name} (${res.statusCode})`);
82 } else {
83 console.log(`⚠️ [RMM-Webhook] ${event} for ${node.name} (${res.statusCode}) - ${responseData}`);
84 }
85 });
86 });
87
88 req.on('error', (error) => {
89 console.error(`❌ [RMM-Webhook] Error sending ${event}:`, error.message);
90 });
91
92 req.write(payload);
93 req.end();
94
95 } catch (error) {
96 console.error('❌ [RMM-Webhook] Send error:', error.message);
97 }
98 }
99
100 /**
101 * Hook: Device connected
102 * Called when a device connects to MeshCentral
103 */
104 obj.hook_onDeviceConnected = function(device, meshId) {
105 console.log(`📱 [RMM-Webhook] Device connected: ${device.name}`);
106 sendWebhook('device.connect', device, meshId);
107 };
108
109 /**
110 * Hook: Device disconnected
111 * Called when a device disconnects from MeshCentral
112 */
113 obj.hook_onDeviceDisconnected = function(device, meshId) {
114 console.log(`📴 [RMM-Webhook] Device disconnected: ${device.name}`);
115 sendWebhook('device.disconnect', device, meshId);
116 };
117
118 /**
119 * Hook: Device changed
120 * Called when device information is updated
121 */
122 obj.hook_onDeviceChanged = function(device, meshId) {
123 console.log(`🔄 [RMM-Webhook] Device changed: ${device.name}`);
124 sendWebhook('device.change', device, meshId);
125 };
126
127 /**
128 * Hook: Node connect event
129 * Alternative hook for node connections
130 */
131 obj.hook_onNodeConnect = function(device, meshId) {
132 console.log(`📱 [RMM-Webhook] Node connected: ${device.name}`);
133 sendWebhook('node.connect', device, meshId);
134 };
135
136 /**
137 * Hook: Node disconnect event
138 * Alternative hook for node disconnections
139 */
140 obj.hook_onNodeDisconnect = function(device, meshId) {
141 console.log(`📴 [RMM-Webhook] Node disconnected: ${device.name}`);
142 sendWebhook('node.disconnect', device, meshId);
143 };
144
145 // Export functions (required by plugin system)
146 obj.exports = [];
147
148 return obj;
149};