feat: Add messaging plugins, Docker discovery, fix reconciliation
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
|
||||
module.exports = {
|
||||
type: 'webhook',
|
||||
category: 'messaging',
|
||||
name: 'Universal REST Webhook',
|
||||
description: 'Send a generic HTTP POST request with a custom JSON payload. Variables {{to}} and {{message}} will be replaced.',
|
||||
|
||||
configSchema: [
|
||||
{ key: 'url', label: 'Webhook URL', type: 'url', required: true, placeholder: 'https://api.example.com/send' },
|
||||
{ key: 'method', label: 'HTTP Method', type: 'text', required: true, placeholder: 'POST' },
|
||||
{ key: 'headers', label: 'Custom Headers (JSON)', type: 'text', required: false, placeholder: '{"Authorization": "Bearer ...", "Content-Type": "application/json"}' },
|
||||
{ key: 'payloadTemplate', label: 'Payload Template', type: 'text', required: true, placeholder: '{"recipient": "{{to}}", "text": "{{message}}"}' },
|
||||
{ key: 'apiSecret', label: 'API Secret / Auth Token', type: 'password', required: false, secret: true }
|
||||
],
|
||||
|
||||
validate: async (config) => {
|
||||
if (!config.url) return { ok: false, error: 'URL is required' };
|
||||
if (!config.payloadTemplate) return { ok: false, error: 'Payload template is required' };
|
||||
try {
|
||||
if (config.headers) JSON.parse(config.headers);
|
||||
} catch (e) {
|
||||
return { ok: false, error: 'Headers must be valid JSON' };
|
||||
}
|
||||
return { ok: true };
|
||||
},
|
||||
|
||||
sendMessage: async (config, payload) => {
|
||||
const { to, message } = payload;
|
||||
let payloadStr = config.payloadTemplate || '{}';
|
||||
|
||||
// Replace template variables safely
|
||||
payloadStr = payloadStr.replace(/\{\{to\}\}/g, to).replace(/\{\{message\}\}/g, message);
|
||||
|
||||
// If there is an API secret, replace {{secret}} in the headers or url
|
||||
let headersObj = {};
|
||||
if (config.headers) {
|
||||
try {
|
||||
const parsed = JSON.parse(config.headers);
|
||||
for (const [k, v] of Object.entries(parsed)) {
|
||||
headersObj[k] = config.apiSecret ? String(v).replace(/\{\{secret\}\}/g, config.apiSecret) : v;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if (!headersObj['Content-Type']) {
|
||||
headersObj['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
const urlObj = new URL(config.url);
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
||||
path: urlObj.pathname + urlObj.search,
|
||||
method: config.method || 'POST',
|
||||
headers: headersObj
|
||||
};
|
||||
|
||||
const client = urlObj.protocol === 'https:' ? https : http;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = client.request(options, (res) => {
|
||||
let body = '';
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve({ status: res.statusCode, body });
|
||||
} else {
|
||||
reject(new Error(`Webhook failed: ${res.statusCode} ${body}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.write(payloadStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user