@theotherwillembotha/node-red-nginxproxymanager 0.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +156 -0
- package/build/GenerateNodes.js +14 -0
- package/build/Nodes.html +1386 -0
- package/build/Nodes.js +22 -0
- package/build/Plugins.js +70 -0
- package/build/icons/nginx.png +0 -0
- package/build/index.js +22 -0
- package/build/nginx/node/NginxGetHostsNode.js +63 -0
- package/build/nginx/node/NginxProxyManagerConfigNode.js +39 -0
- package/build/nginx/node/UpdateHostNode.js +61 -0
- package/build/nginx/service/NginxProxyManagerClient.js +50 -0
- package/build/nginx/service/NginxProxyManagerService.js +90 -0
- package/build/nginx/service/client/NginxClient.js +185 -0
- package/build/nginx/service/client/NginxHttpClient.js +95 -0
- package/build/nginx/service/client/types.js +3 -0
- package/icons/nginx.png +0 -0
- package/package.json +59 -0
package/build/Nodes.html
ADDED
|
@@ -0,0 +1,1386 @@
|
|
|
1
|
+
|
|
2
|
+
<!--
|
|
3
|
+
|
|
4
|
+
This file was automatically generated using the NODERED Core utility.
|
|
5
|
+
Any modifications to his file will be overwritten the next time the code is regenerated.
|
|
6
|
+
|
|
7
|
+
You have been warned.
|
|
8
|
+
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
<!-- ui-helper -->
|
|
12
|
+
<style>
|
|
13
|
+
/* ─── PluginCore Dialog overlay ─────────────────────────────────────────── */
|
|
14
|
+
.plugincore-overlay {
|
|
15
|
+
position: fixed;
|
|
16
|
+
top: 0;
|
|
17
|
+
left: 0;
|
|
18
|
+
right: 0;
|
|
19
|
+
bottom: 0;
|
|
20
|
+
background: rgba(0, 0, 0, 0.5);
|
|
21
|
+
z-index: 2000;
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.plugincore-dialog {
|
|
28
|
+
background: #fff;
|
|
29
|
+
border: 1px solid #aaa;
|
|
30
|
+
border-radius: 4px;
|
|
31
|
+
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
|
32
|
+
min-width: 540px;
|
|
33
|
+
max-width: 85vw;
|
|
34
|
+
max-height: 80vh;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Title bar — matches Node-RED's main nav bar colour */
|
|
41
|
+
.plugincore-dialog-titlebar {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
background: #3d3d3d;
|
|
45
|
+
color: #fff;
|
|
46
|
+
padding: 6px 8px 6px 12px;
|
|
47
|
+
flex-shrink: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.plugincore-dialog-title {
|
|
51
|
+
flex: 1;
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
font-size: 13px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Close button — jQuery UI styling as used in Node-RED dialogs */
|
|
57
|
+
.plugincore-dialog-close {
|
|
58
|
+
color: #ccc !important;
|
|
59
|
+
background: transparent !important;
|
|
60
|
+
border-color: transparent !important;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.plugincore-dialog-close:hover {
|
|
64
|
+
color: #fff !important;
|
|
65
|
+
background: rgba(255, 255, 255, 0.15) !important;
|
|
66
|
+
border-color: rgba(255, 255, 255, 0.3) !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Scrollable body that contains the jQuery UI tab widget */
|
|
70
|
+
.plugincore-dialog-body {
|
|
71
|
+
flex: 1;
|
|
72
|
+
overflow: auto;
|
|
73
|
+
padding: 10px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ─── PluginCore Table ───────────────────────────────────────────────────── */
|
|
77
|
+
.plugincore-table {
|
|
78
|
+
width: 100%;
|
|
79
|
+
border-collapse: collapse;
|
|
80
|
+
font-size: 12px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.plugincore-table th {
|
|
84
|
+
background: #f3f3f3;
|
|
85
|
+
border: 1px solid #ddd;
|
|
86
|
+
padding: 5px 10px;
|
|
87
|
+
text-align: left;
|
|
88
|
+
font-weight: 600;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.plugincore-table td {
|
|
93
|
+
border: 1px solid #eee;
|
|
94
|
+
padding: 5px 10px;
|
|
95
|
+
vertical-align: middle;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.plugincore-table tbody tr:nth-child(even) td {
|
|
99
|
+
background: #fafafa;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.plugincore-table tbody tr:hover td {
|
|
103
|
+
background: #f0f6ff;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.plugincore-status-enabled {
|
|
107
|
+
color: #27ae60;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.plugincore-status-disabled {
|
|
111
|
+
color: #c0392b;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
</style>
|
|
115
|
+
<script type="text/javascript">
|
|
116
|
+
window.PluginCore = window.PluginCore || {};
|
|
117
|
+
/**
|
|
118
|
+
* PluginCore.table(config) → jQuery <table>
|
|
119
|
+
*
|
|
120
|
+
* config: {
|
|
121
|
+
* columns : [{ key, label, render?(value, row) → string | jQuery }],
|
|
122
|
+
* rows : object[]
|
|
123
|
+
* }
|
|
124
|
+
*/
|
|
125
|
+
PluginCore.table = function(config) {
|
|
126
|
+
var $table = $('<table class="plugincore-table">');
|
|
127
|
+
// Header
|
|
128
|
+
var $thead = $('<thead>');
|
|
129
|
+
var $hr = $('<tr>');
|
|
130
|
+
config.columns.forEach(function(col) {
|
|
131
|
+
$hr.append($('<th>').text(col.label));
|
|
132
|
+
});
|
|
133
|
+
$thead.append($hr);
|
|
134
|
+
$table.append($thead);
|
|
135
|
+
// Body
|
|
136
|
+
var $tbody = $('<tbody>');
|
|
137
|
+
(config.rows || []).forEach(function(row) {
|
|
138
|
+
var $tr = $('<tr>');
|
|
139
|
+
config.columns.forEach(function(col) {
|
|
140
|
+
var $td = $('<td>');
|
|
141
|
+
var val = row[col.key];
|
|
142
|
+
if (col.render) {
|
|
143
|
+
var rendered = col.render(val, row);
|
|
144
|
+
if (rendered && typeof rendered === 'object' && rendered.jquery) {
|
|
145
|
+
$td.append(rendered);
|
|
146
|
+
} else {
|
|
147
|
+
$td.html(rendered != null ? String(rendered) : '');
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
$td.text(val != null ? String(val) : '');
|
|
151
|
+
}
|
|
152
|
+
$tr.append($td);
|
|
153
|
+
});
|
|
154
|
+
$tbody.append($tr);
|
|
155
|
+
});
|
|
156
|
+
$table.append($tbody);
|
|
157
|
+
return $table;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* PluginCore.dialog(options) → { close() }
|
|
161
|
+
*
|
|
162
|
+
* options: {
|
|
163
|
+
* title : string,
|
|
164
|
+
* tabs : [{ label: string, render($container) }]
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* Uses jQuery UI tabs for the tab widget (already bundled with Node-RED).
|
|
168
|
+
* Closes on: close button click, overlay click, or Escape key.
|
|
169
|
+
*/
|
|
170
|
+
PluginCore.dialog = function(options) {
|
|
171
|
+
// Only one dialog at a time
|
|
172
|
+
$('.plugincore-overlay').remove();
|
|
173
|
+
var uid = 'plugincore-' + Date.now();
|
|
174
|
+
var $overlay = $('<div class="plugincore-overlay">');
|
|
175
|
+
var $dialog = $('<div class="plugincore-dialog">');
|
|
176
|
+
// ── Title bar ──────────────────────────────────────────────────────
|
|
177
|
+
var $titlebar = $('<div class="plugincore-dialog-titlebar">');
|
|
178
|
+
$titlebar.append($('<span class="plugincore-dialog-title">').text(options.title || ''));
|
|
179
|
+
var $closeBtn = $('<button type="button" class="ui-button ui-corner-all ui-widget plugincore-dialog-close" title="Close">' + '<span class="ui-icon ui-icon-closethick"></span></button>');
|
|
180
|
+
$titlebar.append($closeBtn);
|
|
181
|
+
// ── Body + jQuery UI tabs ──────────────────────────────────────────
|
|
182
|
+
var $body = $('<div class="plugincore-dialog-body">');
|
|
183
|
+
var $tabsEl = $('<div>').attr('id', uid + '-tabs');
|
|
184
|
+
var $ul = $('<ul>');
|
|
185
|
+
(options.tabs || []).forEach(function(tab, i) {
|
|
186
|
+
var paneId = uid + '-pane-' + i;
|
|
187
|
+
$ul.append($('<li>').append($('<a>').attr('href', '#' + paneId).text(tab.label)));
|
|
188
|
+
var $pane = $('<div>').attr('id', paneId);
|
|
189
|
+
if (tab.render) {
|
|
190
|
+
tab.render($pane);
|
|
191
|
+
}
|
|
192
|
+
$tabsEl.append($pane);
|
|
193
|
+
});
|
|
194
|
+
$tabsEl.prepend($ul);
|
|
195
|
+
$body.append($tabsEl);
|
|
196
|
+
// ── Assemble ───────────────────────────────────────────────────────
|
|
197
|
+
$dialog.append($titlebar).append($body);
|
|
198
|
+
$overlay.append($dialog);
|
|
199
|
+
$('body').append($overlay);
|
|
200
|
+
// Initialise jQuery UI tabs (bundled with Node-RED)
|
|
201
|
+
$tabsEl.tabs();
|
|
202
|
+
// ── Close logic ───────────────────────────────────────────────────
|
|
203
|
+
var escNs = 'keydown.plugincore-dialog-' + uid;
|
|
204
|
+
|
|
205
|
+
function close() {
|
|
206
|
+
$overlay.fadeOut(150, function() {
|
|
207
|
+
$overlay.remove();
|
|
208
|
+
});
|
|
209
|
+
$(document).off(escNs);
|
|
210
|
+
}
|
|
211
|
+
$closeBtn.on('click', close);
|
|
212
|
+
$overlay.on('click', function(e) {
|
|
213
|
+
if ($(e.target).is($overlay)) {
|
|
214
|
+
close();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
$(document).on(escNs, function(e) {
|
|
218
|
+
if (e.key === 'Escape') {
|
|
219
|
+
close();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return {
|
|
223
|
+
close: close
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
</script>
|
|
228
|
+
<script type="text/javascript">
|
|
229
|
+
RED.nodes.registerType('NginxProxyManagerConfigNode', {
|
|
230
|
+
category: 'config',
|
|
231
|
+
label: function() {
|
|
232
|
+
return this.name
|
|
233
|
+
},
|
|
234
|
+
paletteLabel: 'Nginx Proxy Manager Config',
|
|
235
|
+
defaults: {
|
|
236
|
+
name: {
|
|
237
|
+
value: 'NginX Example',
|
|
238
|
+
required: true
|
|
239
|
+
},
|
|
240
|
+
url: {
|
|
241
|
+
value: 'http://nginxproxy:81',
|
|
242
|
+
required: true
|
|
243
|
+
},
|
|
244
|
+
email: {
|
|
245
|
+
value: 'info@example.com',
|
|
246
|
+
required: true
|
|
247
|
+
},
|
|
248
|
+
password: {
|
|
249
|
+
value: '',
|
|
250
|
+
required: true
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
oneditprepare: function() {
|
|
254
|
+
// **** NginxProxyManagerConfigNode **** //
|
|
255
|
+
{
|
|
256
|
+
let node = this;
|
|
257
|
+
$("#npm-test-connection-btn").on("click", function() {
|
|
258
|
+
let $btn = $(this);
|
|
259
|
+
let $result = $("#npm-test-connection-result");
|
|
260
|
+
let url = $("#node-config-input-url").val();
|
|
261
|
+
let username = $("#node-config-input-email").val();
|
|
262
|
+
let password = $("#node-config-input-password").val();
|
|
263
|
+
$result.text("Testing...").css("color", "gray");
|
|
264
|
+
$btn.prop("disabled", true);
|
|
265
|
+
$.ajax({
|
|
266
|
+
url: "nginxproxymanagerservice/testconnection",
|
|
267
|
+
type: "POST",
|
|
268
|
+
contentType: "application/json; charset=utf-8",
|
|
269
|
+
data: JSON.stringify({
|
|
270
|
+
connection: {
|
|
271
|
+
url,
|
|
272
|
+
username,
|
|
273
|
+
password
|
|
274
|
+
}
|
|
275
|
+
}),
|
|
276
|
+
success: function(response) {
|
|
277
|
+
$result.text("\u2714 " + response.message).css("color", "green");
|
|
278
|
+
},
|
|
279
|
+
error: function(jqXHR) {
|
|
280
|
+
let msg = "Connection failed";
|
|
281
|
+
try {
|
|
282
|
+
let body = JSON.parse(jqXHR.responseText);
|
|
283
|
+
if (body.error) {
|
|
284
|
+
msg = body.error;
|
|
285
|
+
}
|
|
286
|
+
} catch (e) {}
|
|
287
|
+
$result.text("\u2718 " + msg).css("color", "red");
|
|
288
|
+
},
|
|
289
|
+
complete: function() {
|
|
290
|
+
$btn.prop("disabled", false);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
$("#npm-show-hosts-btn").on("click", function() {
|
|
295
|
+
let $btn = $(this);
|
|
296
|
+
let url = $("#node-config-input-url").val();
|
|
297
|
+
let username = $("#node-config-input-email").val();
|
|
298
|
+
let password = $("#node-config-input-password").val();
|
|
299
|
+
let title = $("#node-config-input-name").val() || "Nginx Proxy Manager";
|
|
300
|
+
$btn.prop("disabled", true);
|
|
301
|
+
$.ajax({
|
|
302
|
+
url: "nginxproxymanagerservice/hosts",
|
|
303
|
+
type: "POST",
|
|
304
|
+
contentType: "application/json; charset=utf-8",
|
|
305
|
+
data: JSON.stringify({
|
|
306
|
+
connection: {
|
|
307
|
+
url,
|
|
308
|
+
username,
|
|
309
|
+
password
|
|
310
|
+
}
|
|
311
|
+
}),
|
|
312
|
+
success: function(response) {
|
|
313
|
+
PluginCore.dialog({
|
|
314
|
+
title: title,
|
|
315
|
+
tabs: [{
|
|
316
|
+
label: "Proxy Hosts",
|
|
317
|
+
render: function($container) {
|
|
318
|
+
var $table = PluginCore.table({
|
|
319
|
+
columns: [{
|
|
320
|
+
key: "id",
|
|
321
|
+
label: "ID"
|
|
322
|
+
}, {
|
|
323
|
+
key: "domain_names",
|
|
324
|
+
label: "Domain Names",
|
|
325
|
+
render: function(v) {
|
|
326
|
+
return v.join("<br>");
|
|
327
|
+
}
|
|
328
|
+
}, {
|
|
329
|
+
key: "forward_host",
|
|
330
|
+
label: "Forward Host"
|
|
331
|
+
}, {
|
|
332
|
+
key: "forward_port",
|
|
333
|
+
label: "Port"
|
|
334
|
+
}, {
|
|
335
|
+
key: "forward_scheme",
|
|
336
|
+
label: "Scheme"
|
|
337
|
+
}, {
|
|
338
|
+
key: "enabled",
|
|
339
|
+
label: "Enabled",
|
|
340
|
+
render: function(v) {
|
|
341
|
+
return $("<span>").addClass(v ? "plugincore-status-enabled" : "plugincore-status-disabled").text(v ? "\u2714 Enabled" : "\u2718 Disabled");
|
|
342
|
+
}
|
|
343
|
+
}],
|
|
344
|
+
rows: response.hosts.proxyHosts
|
|
345
|
+
});
|
|
346
|
+
$container.append($table);
|
|
347
|
+
}
|
|
348
|
+
}]
|
|
349
|
+
});
|
|
350
|
+
},
|
|
351
|
+
error: function(jqXHR) {
|
|
352
|
+
let msg = "Failed to fetch hosts";
|
|
353
|
+
try {
|
|
354
|
+
let body = JSON.parse(jqXHR.responseText);
|
|
355
|
+
if (body.error) {
|
|
356
|
+
msg = body.error;
|
|
357
|
+
}
|
|
358
|
+
} catch (e) {}
|
|
359
|
+
console.error("[NPM] " + msg);
|
|
360
|
+
},
|
|
361
|
+
complete: function() {
|
|
362
|
+
$btn.prop("disabled", false);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
oneditsave: function() {
|
|
369
|
+
// **** NginxProxyManagerConfigNode **** //
|
|
370
|
+
{
|
|
371
|
+
let node = this;
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
oneditcancel: function() {},
|
|
375
|
+
oneditdelete: function() {},
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
</script>
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
<script type="text/html" data-template-name='NginxProxyManagerConfigNode'>
|
|
382
|
+
<div id='section_NginxProxyManagerConfigNode'>
|
|
383
|
+
<div id="NginxProxyManagerConfig">
|
|
384
|
+
<div class="form-row">
|
|
385
|
+
<label class="towb_editorlabel" for="node-config-input-name"><i class="fa fa-font"></i> Name</label>
|
|
386
|
+
<input type="text" id="node-config-input-name" />
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div class="form-row">
|
|
390
|
+
<label class="towb_editorlabel" for="node-config-input-url"><i class="fa fa-link"></i> URL</label>
|
|
391
|
+
<input type="text" id="node-config-input-url" />
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
<div class="form-row">
|
|
395
|
+
<label class="towb_editorlabel" for="node-config-input-email"><i class="fa fa-envelope"></i> Email</label>
|
|
396
|
+
<input type="text" id="node-config-input-email" />
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<div class="form-row">
|
|
400
|
+
<label class="towb_editorlabel" for="node-config-input-password"><i class="fa fa-key"></i> Password</label>
|
|
401
|
+
<input type="password" id="node-config-input-password" />
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div class="form-row">
|
|
405
|
+
<label class="towb_editorlabel"></label>
|
|
406
|
+
<button type="button" id="npm-test-connection-btn" class="red-ui-button">
|
|
407
|
+
<i class="fa fa-plug"></i> Test Connection
|
|
408
|
+
</button>
|
|
409
|
+
<span id="npm-test-connection-result" style="margin-left: 10px; font-size: 12px;"></span>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<div class="form-row">
|
|
413
|
+
<label class="towb_editorlabel"></label>
|
|
414
|
+
<button type="button" id="npm-show-hosts-btn" class="red-ui-button">
|
|
415
|
+
<i class="fa fa-list"></i> Show Hosts
|
|
416
|
+
</button>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
</div>
|
|
421
|
+
</script>
|
|
422
|
+
|
|
423
|
+
<script type="text/markdown" data-help-name='NginxProxyManagerConfigNode'>
|
|
424
|
+
Connection configuration for an Nginx Proxy Manager instance. Referenced by all other nodes in this plugin.
|
|
425
|
+
|
|
426
|
+
### Properties
|
|
427
|
+
|
|
428
|
+
: *name* (string) : A descriptive label for this connection.
|
|
429
|
+
: *url* (string) : The base URL of the Nginx Proxy Manager admin API, including port (e.g. `http://192.168.1.10:81`).
|
|
430
|
+
: *email* (string) : The email address used to authenticate with Nginx Proxy Manager.
|
|
431
|
+
: *password* (string) : The password for the above account.
|
|
432
|
+
|
|
433
|
+
### Actions
|
|
434
|
+
|
|
435
|
+
**Test Connection** — verifies the supplied credentials against the live Nginx Proxy Manager API and shows an inline success or failure message without saving the node.
|
|
436
|
+
|
|
437
|
+
**Show Hosts** — retrieves the current list of proxy hosts and displays them in a dialog with a sortable table (ID, domain names, forward host, port, scheme, enabled status).
|
|
438
|
+
|
|
439
|
+
### References
|
|
440
|
+
- [Nginx Proxy Manager - Project](https://nginxproxymanager.com/)
|
|
441
|
+
- [Nginx Proxy Manager - GitHub](https://github.com/NginxProxyManager/nginx-proxy-manager)
|
|
442
|
+
</script>
|
|
443
|
+
<!-- UpdateNginxHostNode -->
|
|
444
|
+
<style>
|
|
445
|
+
.node_label_white {
|
|
446
|
+
fill: white
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
</style>
|
|
450
|
+
<!-- logger -->
|
|
451
|
+
<style>
|
|
452
|
+
.editorgroupborder {
|
|
453
|
+
border-width: 1px;
|
|
454
|
+
border-style: solid;
|
|
455
|
+
border-color: lightgray;
|
|
456
|
+
padding: 3px;
|
|
457
|
+
margin-top: 2px;
|
|
458
|
+
margin-bottom: 2px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.editorsectionheading {
|
|
462
|
+
font-weight: 600;
|
|
463
|
+
margin-bottom: 1px !important;
|
|
464
|
+
margin-top: 6px !important;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.nomargin {
|
|
468
|
+
margin-bottom: 1px !important;
|
|
469
|
+
margin-top: 1px !important;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.slidecontainer {
|
|
473
|
+
width: 100%;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.slider {
|
|
477
|
+
-webkit-appearance: none;
|
|
478
|
+
appearance: none;
|
|
479
|
+
width: 100%;
|
|
480
|
+
height: 25px;
|
|
481
|
+
background: #d3d3d3;
|
|
482
|
+
outline: none;
|
|
483
|
+
opacity: 0.7;
|
|
484
|
+
-webkit-transition: .2s;
|
|
485
|
+
transition: opacity .2s;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.slider:hover {
|
|
489
|
+
opacity: 1;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.slider::-webkit-slider-thumb {
|
|
493
|
+
-webkit-appearance: none;
|
|
494
|
+
appearance: none;
|
|
495
|
+
width: 25px;
|
|
496
|
+
height: 25px;
|
|
497
|
+
background: #04AA6D;
|
|
498
|
+
cursor: pointer;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.slider::-moz-range-thumb {
|
|
502
|
+
width: 25px;
|
|
503
|
+
height: 25px;
|
|
504
|
+
background: #04AA6D;
|
|
505
|
+
cursor: pointer;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.towb_editorlabel {
|
|
509
|
+
width: 120px !important;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.towb_editorfield {
|
|
513
|
+
width: 70% !important;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.towb_editorfield_short {
|
|
517
|
+
width: 30% !important;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
</style>
|
|
521
|
+
<script type="text/javascript">
|
|
522
|
+
// CSS
|
|
523
|
+
dropdownStyles = `
|
|
524
|
+
.loggertype-dropdown {
|
|
525
|
+
position: absolute;
|
|
526
|
+
background: white;
|
|
527
|
+
border: 1px solid #ccc;
|
|
528
|
+
border-radius: 4px;
|
|
529
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
530
|
+
max-height: 150px;
|
|
531
|
+
overflow-y: auto;
|
|
532
|
+
z-index: 1000;
|
|
533
|
+
min-width: 150px;
|
|
534
|
+
}
|
|
535
|
+
.loggertype-dropdown .dropdown-item {
|
|
536
|
+
padding: 6px 12px;
|
|
537
|
+
cursor: pointer;
|
|
538
|
+
}
|
|
539
|
+
.loggertype-dropdown .dropdown-item:hover {
|
|
540
|
+
background: #f0f0f0;
|
|
541
|
+
}
|
|
542
|
+
`;
|
|
543
|
+
$('<style>').text(dropdownStyles).appendTo('head');
|
|
544
|
+
|
|
545
|
+
</script>
|
|
546
|
+
<script type="text/javascript">
|
|
547
|
+
RED.nodes.registerType('UpdateNginxHostNode', {
|
|
548
|
+
category: 'nginxproxymanager',
|
|
549
|
+
icon: 'nginx.png',
|
|
550
|
+
color: '#647791',
|
|
551
|
+
labelStyle: 'node_label_white',
|
|
552
|
+
label: function() {
|
|
553
|
+
return this.name
|
|
554
|
+
},
|
|
555
|
+
paletteLabel: 'Nginx Update Host Node',
|
|
556
|
+
inputs: 1,
|
|
557
|
+
inputLabels: (i) => 'input',
|
|
558
|
+
outputs: 1,
|
|
559
|
+
outputLabels: (i) => ['event'][i],
|
|
560
|
+
defaults: {
|
|
561
|
+
name: {
|
|
562
|
+
value: 'Update Host Node Example',
|
|
563
|
+
required: true
|
|
564
|
+
},
|
|
565
|
+
proxymanagerconfig: {
|
|
566
|
+
type: 'NginxProxyManagerConfigNode',
|
|
567
|
+
required: true
|
|
568
|
+
},
|
|
569
|
+
selectedDevices: {
|
|
570
|
+
value: []
|
|
571
|
+
},
|
|
572
|
+
logEnabled: {
|
|
573
|
+
value: false,
|
|
574
|
+
required: true
|
|
575
|
+
},
|
|
576
|
+
logger: {
|
|
577
|
+
required: false,
|
|
578
|
+
value: '',
|
|
579
|
+
type: 'DelegatedConfigReferenceNode'
|
|
580
|
+
},
|
|
581
|
+
logTemplateOverrideEnabled: {
|
|
582
|
+
value: false
|
|
583
|
+
},
|
|
584
|
+
logTemplateOverride: {
|
|
585
|
+
value: 'message:{{msg}}'
|
|
586
|
+
},
|
|
587
|
+
metricsEnabled: {
|
|
588
|
+
value: false,
|
|
589
|
+
required: true
|
|
590
|
+
},
|
|
591
|
+
metricsReference: {
|
|
592
|
+
type: 'MetricsConfigNode',
|
|
593
|
+
required: false
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
oneditprepare: function() {
|
|
597
|
+
// **** UpdateNginxHostNode **** //
|
|
598
|
+
{
|
|
599
|
+
let node = this;
|
|
600
|
+
}
|
|
601
|
+
// **** logger **** //
|
|
602
|
+
{
|
|
603
|
+
let node = this;
|
|
604
|
+
// controls.
|
|
605
|
+
let logEnabled = $("#node-input-logEnabled");
|
|
606
|
+
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
607
|
+
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
608
|
+
let logEnabledSection = $("#section_logEnabled");
|
|
609
|
+
// functions.
|
|
610
|
+
let getLoggerTypes = async function() {
|
|
611
|
+
let types = [];
|
|
612
|
+
await $.ajax({
|
|
613
|
+
url: "nodetypeservice/find?tag=LoggerType",
|
|
614
|
+
type: "GET",
|
|
615
|
+
contentType: "application/json; charset=utf-8",
|
|
616
|
+
data: JSON.stringify({
|
|
617
|
+
id: node.id
|
|
618
|
+
}),
|
|
619
|
+
success: (response) => {
|
|
620
|
+
types = response;
|
|
621
|
+
},
|
|
622
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
623
|
+
console.log(jqXHR, textStatus, errorThrown);
|
|
624
|
+
},
|
|
625
|
+
});
|
|
626
|
+
return types;
|
|
627
|
+
}
|
|
628
|
+
let getLoggers = async () => {
|
|
629
|
+
let loggers = [];
|
|
630
|
+
// get the list of types again.
|
|
631
|
+
let loggerTypes = (await getLoggerTypes()).map(type => type.id);
|
|
632
|
+
// add all of the known loggers.
|
|
633
|
+
RED.nodes.eachConfig(cfg => {
|
|
634
|
+
if (loggerTypes.includes(cfg.type)) {
|
|
635
|
+
loggers.push({
|
|
636
|
+
id: cfg.id,
|
|
637
|
+
name: cfg.label(),
|
|
638
|
+
type: cfg.type
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
return loggers;
|
|
643
|
+
};
|
|
644
|
+
let updateLoggerSelectorList = async () => {
|
|
645
|
+
loggerSelector.empty();
|
|
646
|
+
(await getLoggers()).forEach(logger => {
|
|
647
|
+
$('<option value="' + logger.id + '">' + logger.name + '</option>').appendTo(loggerSelector);
|
|
648
|
+
});
|
|
649
|
+
$('<option value="_ADD_">none</option>').appendTo(loggerSelector);
|
|
650
|
+
}
|
|
651
|
+
logEnabled.on('change', function() {
|
|
652
|
+
if (logEnabled.prop("checked")) {
|
|
653
|
+
logEnabledSection.show();
|
|
654
|
+
node._def.defaults.logger.required = true;
|
|
655
|
+
} else {
|
|
656
|
+
logEnabledSection.hide()
|
|
657
|
+
node._def.defaults.logger.required = false;
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
logTemplateEnabled.on('change', function() {
|
|
661
|
+
if (logTemplateEnabled.prop("checked")) {
|
|
662
|
+
logTemplateEnabledSection.show();
|
|
663
|
+
node._def.defaults.logTemplateOverride.required = true;
|
|
664
|
+
} else {
|
|
665
|
+
logTemplateEnabledSection.hide()
|
|
666
|
+
node._def.defaults.logTemplateOverride.required = false;
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
node.logTemplateOverrideEditor = RED.editor.createEditor({
|
|
670
|
+
id: 'node-input-logTemplateOverrideEditor',
|
|
671
|
+
mode: 'ace/mode/handlebars',
|
|
672
|
+
value: node.logTemplateOverride
|
|
673
|
+
});
|
|
674
|
+
let loggerSelector = $("#logger-selector");
|
|
675
|
+
let loggerEditButton = $("#logger-edit-btn");
|
|
676
|
+
let loggerAddButton = $("#logger-add-btn");
|
|
677
|
+
loggerEditButton.on('click', async function(event) {
|
|
678
|
+
event.stopPropagation();
|
|
679
|
+
if (loggerEditButton.hasClass("disabled")) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
// figure out what type of logger this is.
|
|
683
|
+
let selectedLogger = (await getLoggers()).find(logger => logger.id === loggerSelector.val());
|
|
684
|
+
RED.editor.editConfig("#logger-selector", selectedLogger.type, selectedLogger.id);
|
|
685
|
+
});
|
|
686
|
+
loggerAddButton.on('click', function(event) {
|
|
687
|
+
event.stopPropagation();
|
|
688
|
+
let $btn = $(event.target);
|
|
689
|
+
// Remove any existing dropdown
|
|
690
|
+
$('.loggertype-dropdown').remove();
|
|
691
|
+
getLoggerTypes().then(loggerTypes => {
|
|
692
|
+
let $dropdown = $('<div class="loggertype-dropdown red-ui-editor"></div>');
|
|
693
|
+
loggerTypes.forEach(loggerType => {
|
|
694
|
+
$('<div class="dropdown-item"></div>').text(loggerType.name).data('value', loggerType.id).css({
|
|
695
|
+
padding: '4px 8px',
|
|
696
|
+
cursor: 'pointer'
|
|
697
|
+
}).appendTo($dropdown);
|
|
698
|
+
});
|
|
699
|
+
// Append first so outerWidth() is measurable, then position so the
|
|
700
|
+
// right edge of the dropdown aligns with the right edge of the button.
|
|
701
|
+
$('body').append($dropdown);
|
|
702
|
+
$dropdown.css({
|
|
703
|
+
top: $btn.offset().top + $btn.outerHeight(),
|
|
704
|
+
left: $btn.offset().left + $btn.outerWidth() - $dropdown.outerWidth()
|
|
705
|
+
});
|
|
706
|
+
$(document).on('mousedown.dropdown', function(e) {
|
|
707
|
+
if (!$(e.target).closest('.loggertype-dropdown').length) {
|
|
708
|
+
$dropdown.remove();
|
|
709
|
+
$(document).off('mousedown.dropdown'); // Clean up the listener
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
// Handle item selection
|
|
713
|
+
$dropdown.on('click', '.dropdown-item', function(e) {
|
|
714
|
+
e.stopPropagation();
|
|
715
|
+
const selectedValue = $(this).data('value');
|
|
716
|
+
const selectedText = $(this).text();
|
|
717
|
+
// Do whatever you need with the selection
|
|
718
|
+
console.log('Selected:', selectedValue, selectedText);
|
|
719
|
+
$dropdown.remove();
|
|
720
|
+
RED.editor.editConfig("#logger-selector", selectedValue, "_ADD_");
|
|
721
|
+
});
|
|
722
|
+
})
|
|
723
|
+
});
|
|
724
|
+
loggerSelector.on("focus", updateLoggerSelectorList);
|
|
725
|
+
loggerSelector.on('change', function(event) {
|
|
726
|
+
if ("_ADD_" === loggerSelector.val()) {
|
|
727
|
+
loggerEditButton.addClass("disabled")
|
|
728
|
+
} else {
|
|
729
|
+
loggerEditButton.removeClass("disabled")
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
// lastly, make sure that we set the correct value on the logger selector.
|
|
733
|
+
updateLoggerSelectorList().then(() => {
|
|
734
|
+
loggerSelector.val(node.logger);
|
|
735
|
+
loggerSelector.trigger('change');
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
// **** metrics **** //
|
|
739
|
+
{
|
|
740
|
+
let node = this;
|
|
741
|
+
let metricsEnabled = $("#node-input-metricsEnabled");
|
|
742
|
+
let metricsEnabledSection = $("#section_metricsEnabled");
|
|
743
|
+
metricsEnabled.on('change', function() {
|
|
744
|
+
if (metricsEnabled.prop("checked")) {
|
|
745
|
+
metricsEnabledSection.show();
|
|
746
|
+
node._def.defaults.metricsReference.required = true;
|
|
747
|
+
} else {
|
|
748
|
+
metricsEnabledSection.hide()
|
|
749
|
+
node._def.defaults.metricsReference.required = false;
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
oneditsave: function() {
|
|
755
|
+
// **** UpdateNginxHostNode **** //
|
|
756
|
+
{
|
|
757
|
+
let node = this;
|
|
758
|
+
}
|
|
759
|
+
// **** logger **** //
|
|
760
|
+
{
|
|
761
|
+
node = this;
|
|
762
|
+
var selectedLogger = $("#logger-selector").val();
|
|
763
|
+
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
764
|
+
node.logTemplateOverride = node.logTemplateOverrideEditor.getValue();
|
|
765
|
+
node.logTemplateOverrideEditor.destroy();
|
|
766
|
+
delete node.logTemplateOverrideEditor;
|
|
767
|
+
}
|
|
768
|
+
// **** metrics **** //
|
|
769
|
+
{}
|
|
770
|
+
},
|
|
771
|
+
oneditcancel: function() {},
|
|
772
|
+
oneditdelete: function() {},
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
</script>
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
<script type="text/html" data-template-name='UpdateNginxHostNode'>
|
|
779
|
+
<div id='section_UpdateNginxHostNode'>
|
|
780
|
+
<div class="form-row">
|
|
781
|
+
<label class="towb_editorlabel" for="node-input-name"><i class="fa fa-font"></i> Name</label>
|
|
782
|
+
<input type="text" id="node-input-name" />
|
|
783
|
+
</div>
|
|
784
|
+
|
|
785
|
+
<div class="form-row">
|
|
786
|
+
<label class="towb_editorlabel" for="node-input-proxymanagerconfig"><i class="fa fa-server"></i> Proxy Manager</label>
|
|
787
|
+
<input type="text" id="node-input-proxymanagerconfig" />
|
|
788
|
+
</div>
|
|
789
|
+
|
|
790
|
+
</div>
|
|
791
|
+
|
|
792
|
+
<div id='section_logger'>
|
|
793
|
+
<div id="section_loggerconfig">
|
|
794
|
+
<div class="form-row editorsectionheading">
|
|
795
|
+
<i class="w-16 fa fa-eye"></i> <span>Logging</span>
|
|
796
|
+
</div>
|
|
797
|
+
<div class="editorgroupborder">
|
|
798
|
+
<div class="form-row nomargin logEnableCheck">
|
|
799
|
+
<input type="checkbox" id="node-input-logEnabled" placeholder="logEnabled" value="false" style="margin:8px 0 10px 102px; width:20px;">
|
|
800
|
+
<label style="width:auto" for="node-input-logEnabled"><span>Enable Logging</span></label>
|
|
801
|
+
</div>
|
|
802
|
+
|
|
803
|
+
<div id="section_logEnabled">
|
|
804
|
+
<div class="form-row nomargin">
|
|
805
|
+
<label for="logger-selector">Logger</label>
|
|
806
|
+
<div style="width: 70%; display: inline-flex;">
|
|
807
|
+
<select id="logger-selector" style="flex-grow: 1;" class="">
|
|
808
|
+
<option value="_ADD_">none</option>
|
|
809
|
+
</select>
|
|
810
|
+
<a id="logger-edit-btn" class="red-ui-button disabled" style="margin-left: 10px;">
|
|
811
|
+
<i class="fa fa-pencil"></i>
|
|
812
|
+
</a>
|
|
813
|
+
<a id="logger-add-btn" class="red-ui-button" style="margin-left: 10px;">
|
|
814
|
+
<i class="fa fa-plus"></i>
|
|
815
|
+
</a>
|
|
816
|
+
</div>
|
|
817
|
+
</div>
|
|
818
|
+
|
|
819
|
+
<div class="form-row nomargin">
|
|
820
|
+
<input type="checkbox" id="node-input-logTemplateOverrideEnabled" placeholder="" value="false" style="margin:8px 0 10px 102px; width:20px;">
|
|
821
|
+
<label style="width:auto" for="node-input-logTemplateOverrideEnabled"><span>Override Template</span></label>
|
|
822
|
+
</div>
|
|
823
|
+
|
|
824
|
+
<div id="section_logTemplateOverrideEnabled">
|
|
825
|
+
<div class="form-row">
|
|
826
|
+
<label class="towb_editorlabel" for="node-input-logTemplateOverrideEditor"><i class="fa fa-code"></i> Template</label>
|
|
827
|
+
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-logTemplateOverrideEditor"></div>
|
|
828
|
+
</div>
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
</div>
|
|
833
|
+
|
|
834
|
+
</div>
|
|
835
|
+
|
|
836
|
+
<div id='section_metrics'>
|
|
837
|
+
<div id="section_metricsconfig">
|
|
838
|
+
<div class="form-row editorsectionheading">
|
|
839
|
+
<i class="w-16 fa fa-eye"></i> <span>Metrics</span>
|
|
840
|
+
</div>
|
|
841
|
+
<div class="editorgroupborder">
|
|
842
|
+
<div class="form-row nomargin metricsEnableCheck">
|
|
843
|
+
<input type="checkbox" id="node-input-metricsEnabled" value="false" style="margin:8px 0 10px 102px; width:20px;" />
|
|
844
|
+
<label style="width:auto" for="node-input-metricsEnabled"><span>Enable Metrics</span></label>
|
|
845
|
+
</div>
|
|
846
|
+
|
|
847
|
+
<div id="section_metricsEnabled">
|
|
848
|
+
<div class="form-row nomargin">
|
|
849
|
+
<label class="towb_editorlabel" for="node-input-metricsReference">Metric Collector</label>
|
|
850
|
+
<input id="node-input-metricsReference" placeholder="metrics" />
|
|
851
|
+
</div>
|
|
852
|
+
</div>
|
|
853
|
+
</div>
|
|
854
|
+
</div>
|
|
855
|
+
|
|
856
|
+
</div>
|
|
857
|
+
</script>
|
|
858
|
+
|
|
859
|
+
<script type="text/markdown" data-help-name='UpdateNginxHostNode'>
|
|
860
|
+
Creates or updates a proxy host entry in Nginx Proxy Manager.
|
|
861
|
+
|
|
862
|
+
The operation is determined by the presence of an `id` field in `msg.payload`:
|
|
863
|
+
- **No `id`** — creates a new proxy host.
|
|
864
|
+
- **With `id`** — updates the existing proxy host with that ID.
|
|
865
|
+
|
|
866
|
+
### Properties
|
|
867
|
+
|
|
868
|
+
: *name* (string) : A descriptive label for the node.
|
|
869
|
+
: *proxy manager* (npm config) : The Nginx Proxy Manager connection to use.
|
|
870
|
+
|
|
871
|
+
### Inputs
|
|
872
|
+
|
|
873
|
+
: *msg.payload* (object) : The host configuration to create or update.
|
|
874
|
+
|
|
875
|
+
**Create a new host:**
|
|
876
|
+
|
|
877
|
+
```javascript
|
|
878
|
+
{
|
|
879
|
+
domainNames: string[], // domain names to forward (must be unique and not yet registered)
|
|
880
|
+
scheme: "http" | "https", // forwarding scheme
|
|
881
|
+
forwardHost: string, // target host to forward requests to
|
|
882
|
+
forwardPort: number, // target port
|
|
883
|
+
accessList: string, // access list to bind to this host
|
|
884
|
+
cacheAssets: boolean, // optional: cache static assets
|
|
885
|
+
blockCommonExploits: boolean, // optional: block common exploit patterns
|
|
886
|
+
websocketSupport: boolean, // optional: enable WebSocket support
|
|
887
|
+
}
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
**Update an existing host:**
|
|
891
|
+
|
|
892
|
+
```javascript
|
|
893
|
+
{
|
|
894
|
+
id: number, // required: ID of the proxy host to update
|
|
895
|
+
domainNames: string[], // optional
|
|
896
|
+
scheme: "http" | "https", // optional
|
|
897
|
+
forwardHost: string, // optional
|
|
898
|
+
forwardPort: number, // optional
|
|
899
|
+
accessList: string, // optional
|
|
900
|
+
cacheAssets: boolean, // optional
|
|
901
|
+
blockCommonExploits: boolean, // optional
|
|
902
|
+
websocketSupport: boolean, // optional
|
|
903
|
+
}
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
### Output
|
|
907
|
+
|
|
908
|
+
The original message is forwarded unchanged after the operation completes.
|
|
909
|
+
|
|
910
|
+
### Example
|
|
911
|
+
|
|
912
|
+
```json
|
|
913
|
+
{
|
|
914
|
+
"domainNames": ["myservice.example.com"],
|
|
915
|
+
"scheme": "http",
|
|
916
|
+
"forwardHost": "192.168.1.30",
|
|
917
|
+
"forwardPort": 8080,
|
|
918
|
+
"blockCommonExploits": true,
|
|
919
|
+
"websocketSupport": true
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### Logging
|
|
924
|
+
: *enable logging* (boolean) : if checked, then the node will produce logging output to the specified logger.
|
|
925
|
+
: *logger* (logconfig) : the endppoint that the node will log events to.
|
|
926
|
+
: *override template* (logconfig) : if checked, a custom logging template can be provided for the log message.
|
|
927
|
+
: *logger template* (mustache) : a template in mustache format to generate a message for logging purposes (see LoggerConfig node for more information)
|
|
928
|
+
|
|
929
|
+
### Metrics
|
|
930
|
+
: *enable metrics* (boolean) : if checked, then the node will produce metrics output to the specified metrics provider.
|
|
931
|
+
: *metric collector* (metricconfig) : the collection point or grouping where this particular metric will be attached.
|
|
932
|
+
</script>
|
|
933
|
+
<!-- logger -->
|
|
934
|
+
<style>
|
|
935
|
+
.editorgroupborder {
|
|
936
|
+
border-width: 1px;
|
|
937
|
+
border-style: solid;
|
|
938
|
+
border-color: lightgray;
|
|
939
|
+
padding: 3px;
|
|
940
|
+
margin-top: 2px;
|
|
941
|
+
margin-bottom: 2px;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.editorsectionheading {
|
|
945
|
+
font-weight: 600;
|
|
946
|
+
margin-bottom: 1px !important;
|
|
947
|
+
margin-top: 6px !important;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.nomargin {
|
|
951
|
+
margin-bottom: 1px !important;
|
|
952
|
+
margin-top: 1px !important;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
.slidecontainer {
|
|
956
|
+
width: 100%;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.slider {
|
|
960
|
+
-webkit-appearance: none;
|
|
961
|
+
appearance: none;
|
|
962
|
+
width: 100%;
|
|
963
|
+
height: 25px;
|
|
964
|
+
background: #d3d3d3;
|
|
965
|
+
outline: none;
|
|
966
|
+
opacity: 0.7;
|
|
967
|
+
-webkit-transition: .2s;
|
|
968
|
+
transition: opacity .2s;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.slider:hover {
|
|
972
|
+
opacity: 1;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.slider::-webkit-slider-thumb {
|
|
976
|
+
-webkit-appearance: none;
|
|
977
|
+
appearance: none;
|
|
978
|
+
width: 25px;
|
|
979
|
+
height: 25px;
|
|
980
|
+
background: #04AA6D;
|
|
981
|
+
cursor: pointer;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.slider::-moz-range-thumb {
|
|
985
|
+
width: 25px;
|
|
986
|
+
height: 25px;
|
|
987
|
+
background: #04AA6D;
|
|
988
|
+
cursor: pointer;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
.towb_editorlabel {
|
|
992
|
+
width: 120px !important;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.towb_editorfield {
|
|
996
|
+
width: 70% !important;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.towb_editorfield_short {
|
|
1000
|
+
width: 30% !important;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
</style>
|
|
1004
|
+
<script type="text/javascript">
|
|
1005
|
+
// CSS
|
|
1006
|
+
dropdownStyles = `
|
|
1007
|
+
.loggertype-dropdown {
|
|
1008
|
+
position: absolute;
|
|
1009
|
+
background: white;
|
|
1010
|
+
border: 1px solid #ccc;
|
|
1011
|
+
border-radius: 4px;
|
|
1012
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
1013
|
+
max-height: 150px;
|
|
1014
|
+
overflow-y: auto;
|
|
1015
|
+
z-index: 1000;
|
|
1016
|
+
min-width: 150px;
|
|
1017
|
+
}
|
|
1018
|
+
.loggertype-dropdown .dropdown-item {
|
|
1019
|
+
padding: 6px 12px;
|
|
1020
|
+
cursor: pointer;
|
|
1021
|
+
}
|
|
1022
|
+
.loggertype-dropdown .dropdown-item:hover {
|
|
1023
|
+
background: #f0f0f0;
|
|
1024
|
+
}
|
|
1025
|
+
`;
|
|
1026
|
+
$('<style>').text(dropdownStyles).appendTo('head');
|
|
1027
|
+
|
|
1028
|
+
</script>
|
|
1029
|
+
<script type="text/javascript">
|
|
1030
|
+
RED.nodes.registerType('NginxGetHostsNode', {
|
|
1031
|
+
category: 'nginxproxymanager',
|
|
1032
|
+
icon: 'nginx.png',
|
|
1033
|
+
color: '#647791',
|
|
1034
|
+
labelStyle: 'node_label_white',
|
|
1035
|
+
label: function() {
|
|
1036
|
+
return this.name
|
|
1037
|
+
},
|
|
1038
|
+
paletteLabel: 'Nginx Get Hosts',
|
|
1039
|
+
inputs: 1,
|
|
1040
|
+
inputLabels: (i) => 'input',
|
|
1041
|
+
outputs: 1,
|
|
1042
|
+
outputLabels: (i) => ['event'][i],
|
|
1043
|
+
defaults: {
|
|
1044
|
+
name: {
|
|
1045
|
+
value: 'Nginx Get Hosts',
|
|
1046
|
+
required: true
|
|
1047
|
+
},
|
|
1048
|
+
proxymanagerconfig: {
|
|
1049
|
+
type: 'NginxProxyManagerConfigNode',
|
|
1050
|
+
required: true
|
|
1051
|
+
},
|
|
1052
|
+
outputpath: {
|
|
1053
|
+
value: 'payload'
|
|
1054
|
+
},
|
|
1055
|
+
logEnabled: {
|
|
1056
|
+
value: false,
|
|
1057
|
+
required: true
|
|
1058
|
+
},
|
|
1059
|
+
logger: {
|
|
1060
|
+
required: false,
|
|
1061
|
+
value: '',
|
|
1062
|
+
type: 'DelegatedConfigReferenceNode'
|
|
1063
|
+
},
|
|
1064
|
+
logTemplateOverrideEnabled: {
|
|
1065
|
+
value: false
|
|
1066
|
+
},
|
|
1067
|
+
logTemplateOverride: {
|
|
1068
|
+
value: 'message:{{msg}}'
|
|
1069
|
+
},
|
|
1070
|
+
metricsEnabled: {
|
|
1071
|
+
value: false,
|
|
1072
|
+
required: true
|
|
1073
|
+
},
|
|
1074
|
+
metricsReference: {
|
|
1075
|
+
type: 'MetricsConfigNode',
|
|
1076
|
+
required: false
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1079
|
+
oneditprepare: function() {
|
|
1080
|
+
// **** NginxGetHostsNode **** //
|
|
1081
|
+
{
|
|
1082
|
+
let node = this;
|
|
1083
|
+
$("#node-input-outputpath").typedInput({
|
|
1084
|
+
types: ["msg"]
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
// **** logger **** //
|
|
1088
|
+
{
|
|
1089
|
+
let node = this;
|
|
1090
|
+
// controls.
|
|
1091
|
+
let logEnabled = $("#node-input-logEnabled");
|
|
1092
|
+
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
1093
|
+
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
1094
|
+
let logEnabledSection = $("#section_logEnabled");
|
|
1095
|
+
// functions.
|
|
1096
|
+
let getLoggerTypes = async function() {
|
|
1097
|
+
let types = [];
|
|
1098
|
+
await $.ajax({
|
|
1099
|
+
url: "nodetypeservice/find?tag=LoggerType",
|
|
1100
|
+
type: "GET",
|
|
1101
|
+
contentType: "application/json; charset=utf-8",
|
|
1102
|
+
data: JSON.stringify({
|
|
1103
|
+
id: node.id
|
|
1104
|
+
}),
|
|
1105
|
+
success: (response) => {
|
|
1106
|
+
types = response;
|
|
1107
|
+
},
|
|
1108
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
1109
|
+
console.log(jqXHR, textStatus, errorThrown);
|
|
1110
|
+
},
|
|
1111
|
+
});
|
|
1112
|
+
return types;
|
|
1113
|
+
}
|
|
1114
|
+
let getLoggers = async () => {
|
|
1115
|
+
let loggers = [];
|
|
1116
|
+
// get the list of types again.
|
|
1117
|
+
let loggerTypes = (await getLoggerTypes()).map(type => type.id);
|
|
1118
|
+
// add all of the known loggers.
|
|
1119
|
+
RED.nodes.eachConfig(cfg => {
|
|
1120
|
+
if (loggerTypes.includes(cfg.type)) {
|
|
1121
|
+
loggers.push({
|
|
1122
|
+
id: cfg.id,
|
|
1123
|
+
name: cfg.label(),
|
|
1124
|
+
type: cfg.type
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
return loggers;
|
|
1129
|
+
};
|
|
1130
|
+
let updateLoggerSelectorList = async () => {
|
|
1131
|
+
loggerSelector.empty();
|
|
1132
|
+
(await getLoggers()).forEach(logger => {
|
|
1133
|
+
$('<option value="' + logger.id + '">' + logger.name + '</option>').appendTo(loggerSelector);
|
|
1134
|
+
});
|
|
1135
|
+
$('<option value="_ADD_">none</option>').appendTo(loggerSelector);
|
|
1136
|
+
}
|
|
1137
|
+
logEnabled.on('change', function() {
|
|
1138
|
+
if (logEnabled.prop("checked")) {
|
|
1139
|
+
logEnabledSection.show();
|
|
1140
|
+
node._def.defaults.logger.required = true;
|
|
1141
|
+
} else {
|
|
1142
|
+
logEnabledSection.hide()
|
|
1143
|
+
node._def.defaults.logger.required = false;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
logTemplateEnabled.on('change', function() {
|
|
1147
|
+
if (logTemplateEnabled.prop("checked")) {
|
|
1148
|
+
logTemplateEnabledSection.show();
|
|
1149
|
+
node._def.defaults.logTemplateOverride.required = true;
|
|
1150
|
+
} else {
|
|
1151
|
+
logTemplateEnabledSection.hide()
|
|
1152
|
+
node._def.defaults.logTemplateOverride.required = false;
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
node.logTemplateOverrideEditor = RED.editor.createEditor({
|
|
1156
|
+
id: 'node-input-logTemplateOverrideEditor',
|
|
1157
|
+
mode: 'ace/mode/handlebars',
|
|
1158
|
+
value: node.logTemplateOverride
|
|
1159
|
+
});
|
|
1160
|
+
let loggerSelector = $("#logger-selector");
|
|
1161
|
+
let loggerEditButton = $("#logger-edit-btn");
|
|
1162
|
+
let loggerAddButton = $("#logger-add-btn");
|
|
1163
|
+
loggerEditButton.on('click', async function(event) {
|
|
1164
|
+
event.stopPropagation();
|
|
1165
|
+
if (loggerEditButton.hasClass("disabled")) {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
// figure out what type of logger this is.
|
|
1169
|
+
let selectedLogger = (await getLoggers()).find(logger => logger.id === loggerSelector.val());
|
|
1170
|
+
RED.editor.editConfig("#logger-selector", selectedLogger.type, selectedLogger.id);
|
|
1171
|
+
});
|
|
1172
|
+
loggerAddButton.on('click', function(event) {
|
|
1173
|
+
event.stopPropagation();
|
|
1174
|
+
let $btn = $(event.target);
|
|
1175
|
+
// Remove any existing dropdown
|
|
1176
|
+
$('.loggertype-dropdown').remove();
|
|
1177
|
+
getLoggerTypes().then(loggerTypes => {
|
|
1178
|
+
let $dropdown = $('<div class="loggertype-dropdown red-ui-editor"></div>');
|
|
1179
|
+
loggerTypes.forEach(loggerType => {
|
|
1180
|
+
$('<div class="dropdown-item"></div>').text(loggerType.name).data('value', loggerType.id).css({
|
|
1181
|
+
padding: '4px 8px',
|
|
1182
|
+
cursor: 'pointer'
|
|
1183
|
+
}).appendTo($dropdown);
|
|
1184
|
+
});
|
|
1185
|
+
// Append first so outerWidth() is measurable, then position so the
|
|
1186
|
+
// right edge of the dropdown aligns with the right edge of the button.
|
|
1187
|
+
$('body').append($dropdown);
|
|
1188
|
+
$dropdown.css({
|
|
1189
|
+
top: $btn.offset().top + $btn.outerHeight(),
|
|
1190
|
+
left: $btn.offset().left + $btn.outerWidth() - $dropdown.outerWidth()
|
|
1191
|
+
});
|
|
1192
|
+
$(document).on('mousedown.dropdown', function(e) {
|
|
1193
|
+
if (!$(e.target).closest('.loggertype-dropdown').length) {
|
|
1194
|
+
$dropdown.remove();
|
|
1195
|
+
$(document).off('mousedown.dropdown'); // Clean up the listener
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
// Handle item selection
|
|
1199
|
+
$dropdown.on('click', '.dropdown-item', function(e) {
|
|
1200
|
+
e.stopPropagation();
|
|
1201
|
+
const selectedValue = $(this).data('value');
|
|
1202
|
+
const selectedText = $(this).text();
|
|
1203
|
+
// Do whatever you need with the selection
|
|
1204
|
+
console.log('Selected:', selectedValue, selectedText);
|
|
1205
|
+
$dropdown.remove();
|
|
1206
|
+
RED.editor.editConfig("#logger-selector", selectedValue, "_ADD_");
|
|
1207
|
+
});
|
|
1208
|
+
})
|
|
1209
|
+
});
|
|
1210
|
+
loggerSelector.on("focus", updateLoggerSelectorList);
|
|
1211
|
+
loggerSelector.on('change', function(event) {
|
|
1212
|
+
if ("_ADD_" === loggerSelector.val()) {
|
|
1213
|
+
loggerEditButton.addClass("disabled")
|
|
1214
|
+
} else {
|
|
1215
|
+
loggerEditButton.removeClass("disabled")
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
// lastly, make sure that we set the correct value on the logger selector.
|
|
1219
|
+
updateLoggerSelectorList().then(() => {
|
|
1220
|
+
loggerSelector.val(node.logger);
|
|
1221
|
+
loggerSelector.trigger('change');
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
// **** metrics **** //
|
|
1225
|
+
{
|
|
1226
|
+
let node = this;
|
|
1227
|
+
let metricsEnabled = $("#node-input-metricsEnabled");
|
|
1228
|
+
let metricsEnabledSection = $("#section_metricsEnabled");
|
|
1229
|
+
metricsEnabled.on('change', function() {
|
|
1230
|
+
if (metricsEnabled.prop("checked")) {
|
|
1231
|
+
metricsEnabledSection.show();
|
|
1232
|
+
node._def.defaults.metricsReference.required = true;
|
|
1233
|
+
} else {
|
|
1234
|
+
metricsEnabledSection.hide()
|
|
1235
|
+
node._def.defaults.metricsReference.required = false;
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
oneditsave: function() {
|
|
1241
|
+
// **** NginxGetHostsNode **** //
|
|
1242
|
+
{
|
|
1243
|
+
let node = this;
|
|
1244
|
+
}
|
|
1245
|
+
// **** logger **** //
|
|
1246
|
+
{
|
|
1247
|
+
node = this;
|
|
1248
|
+
var selectedLogger = $("#logger-selector").val();
|
|
1249
|
+
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
1250
|
+
node.logTemplateOverride = node.logTemplateOverrideEditor.getValue();
|
|
1251
|
+
node.logTemplateOverrideEditor.destroy();
|
|
1252
|
+
delete node.logTemplateOverrideEditor;
|
|
1253
|
+
}
|
|
1254
|
+
// **** metrics **** //
|
|
1255
|
+
{}
|
|
1256
|
+
},
|
|
1257
|
+
oneditcancel: function() {},
|
|
1258
|
+
oneditdelete: function() {},
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
</script>
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
<script type="text/html" data-template-name='NginxGetHostsNode'>
|
|
1265
|
+
<div id='section_NginxGetHostsNode'>
|
|
1266
|
+
<div class="form-row">
|
|
1267
|
+
<label class="towb_editorlabel" for="node-input-name"><i class="fa fa-font"></i> Name</label>
|
|
1268
|
+
<input type="text" id="node-input-name" />
|
|
1269
|
+
</div>
|
|
1270
|
+
|
|
1271
|
+
<div class="form-row">
|
|
1272
|
+
<label class="towb_editorlabel" for="node-input-proxymanagerconfig"><i class="fa fa-server"></i> Proxy Manager</label>
|
|
1273
|
+
<input type="text" id="node-input-proxymanagerconfig" />
|
|
1274
|
+
</div>
|
|
1275
|
+
|
|
1276
|
+
<div class="form-row">
|
|
1277
|
+
<label class="towb_editorlabel" for="node-input-outputpath"><i class="fa fa-sign-out"></i> Output</label>
|
|
1278
|
+
<input type="text" id="node-input-outputpath" />
|
|
1279
|
+
</div>
|
|
1280
|
+
|
|
1281
|
+
</div>
|
|
1282
|
+
|
|
1283
|
+
<div id='section_logger'>
|
|
1284
|
+
<div id="section_loggerconfig">
|
|
1285
|
+
<div class="form-row editorsectionheading">
|
|
1286
|
+
<i class="w-16 fa fa-eye"></i> <span>Logging</span>
|
|
1287
|
+
</div>
|
|
1288
|
+
<div class="editorgroupborder">
|
|
1289
|
+
<div class="form-row nomargin logEnableCheck">
|
|
1290
|
+
<input type="checkbox" id="node-input-logEnabled" placeholder="logEnabled" value="false" style="margin:8px 0 10px 102px; width:20px;">
|
|
1291
|
+
<label style="width:auto" for="node-input-logEnabled"><span>Enable Logging</span></label>
|
|
1292
|
+
</div>
|
|
1293
|
+
|
|
1294
|
+
<div id="section_logEnabled">
|
|
1295
|
+
<div class="form-row nomargin">
|
|
1296
|
+
<label for="logger-selector">Logger</label>
|
|
1297
|
+
<div style="width: 70%; display: inline-flex;">
|
|
1298
|
+
<select id="logger-selector" style="flex-grow: 1;" class="">
|
|
1299
|
+
<option value="_ADD_">none</option>
|
|
1300
|
+
</select>
|
|
1301
|
+
<a id="logger-edit-btn" class="red-ui-button disabled" style="margin-left: 10px;">
|
|
1302
|
+
<i class="fa fa-pencil"></i>
|
|
1303
|
+
</a>
|
|
1304
|
+
<a id="logger-add-btn" class="red-ui-button" style="margin-left: 10px;">
|
|
1305
|
+
<i class="fa fa-plus"></i>
|
|
1306
|
+
</a>
|
|
1307
|
+
</div>
|
|
1308
|
+
</div>
|
|
1309
|
+
|
|
1310
|
+
<div class="form-row nomargin">
|
|
1311
|
+
<input type="checkbox" id="node-input-logTemplateOverrideEnabled" placeholder="" value="false" style="margin:8px 0 10px 102px; width:20px;">
|
|
1312
|
+
<label style="width:auto" for="node-input-logTemplateOverrideEnabled"><span>Override Template</span></label>
|
|
1313
|
+
</div>
|
|
1314
|
+
|
|
1315
|
+
<div id="section_logTemplateOverrideEnabled">
|
|
1316
|
+
<div class="form-row">
|
|
1317
|
+
<label class="towb_editorlabel" for="node-input-logTemplateOverrideEditor"><i class="fa fa-code"></i> Template</label>
|
|
1318
|
+
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-logTemplateOverrideEditor"></div>
|
|
1319
|
+
</div>
|
|
1320
|
+
</div>
|
|
1321
|
+
</div>
|
|
1322
|
+
</div>
|
|
1323
|
+
</div>
|
|
1324
|
+
|
|
1325
|
+
</div>
|
|
1326
|
+
|
|
1327
|
+
<div id='section_metrics'>
|
|
1328
|
+
<div id="section_metricsconfig">
|
|
1329
|
+
<div class="form-row editorsectionheading">
|
|
1330
|
+
<i class="w-16 fa fa-eye"></i> <span>Metrics</span>
|
|
1331
|
+
</div>
|
|
1332
|
+
<div class="editorgroupborder">
|
|
1333
|
+
<div class="form-row nomargin metricsEnableCheck">
|
|
1334
|
+
<input type="checkbox" id="node-input-metricsEnabled" value="false" style="margin:8px 0 10px 102px; width:20px;" />
|
|
1335
|
+
<label style="width:auto" for="node-input-metricsEnabled"><span>Enable Metrics</span></label>
|
|
1336
|
+
</div>
|
|
1337
|
+
|
|
1338
|
+
<div id="section_metricsEnabled">
|
|
1339
|
+
<div class="form-row nomargin">
|
|
1340
|
+
<label class="towb_editorlabel" for="node-input-metricsReference">Metric Collector</label>
|
|
1341
|
+
<input id="node-input-metricsReference" placeholder="metrics" />
|
|
1342
|
+
</div>
|
|
1343
|
+
</div>
|
|
1344
|
+
</div>
|
|
1345
|
+
</div>
|
|
1346
|
+
|
|
1347
|
+
</div>
|
|
1348
|
+
</script>
|
|
1349
|
+
|
|
1350
|
+
<script type="text/markdown" data-help-name='NginxGetHostsNode'>
|
|
1351
|
+
Retrieves the list of proxy hosts from Nginx Proxy Manager and writes them to the specified output path on the message.
|
|
1352
|
+
|
|
1353
|
+
### Properties
|
|
1354
|
+
|
|
1355
|
+
: *name* (string) : A descriptive label for the node.
|
|
1356
|
+
: *proxy manager* (npm config) : The Nginx Proxy Manager connection to use.
|
|
1357
|
+
: *output* (msg) : The message property to write the hosts array to. Defaults to `msg.payload`.
|
|
1358
|
+
|
|
1359
|
+
### Output
|
|
1360
|
+
|
|
1361
|
+
The original message is forwarded with the specified property set to an array of proxy host objects:
|
|
1362
|
+
|
|
1363
|
+
```javascript
|
|
1364
|
+
[
|
|
1365
|
+
{
|
|
1366
|
+
id: number, // proxy host ID
|
|
1367
|
+
domainNames: string[], // domain names served by this host
|
|
1368
|
+
scheme: string, // forwarding scheme: "http" or "https"
|
|
1369
|
+
forwardHost: string, // target host
|
|
1370
|
+
forwardPort: number, // target port
|
|
1371
|
+
}
|
|
1372
|
+
]
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
### Logging
|
|
1376
|
+
: *enable logging* (boolean) : if checked, then the node will produce logging output to the specified logger.
|
|
1377
|
+
: *logger* (logconfig) : the endppoint that the node will log events to.
|
|
1378
|
+
: *override template* (logconfig) : if checked, a custom logging template can be provided for the log message.
|
|
1379
|
+
: *logger template* (mustache) : a template in mustache format to generate a message for logging purposes (see LoggerConfig node for more information)
|
|
1380
|
+
|
|
1381
|
+
### Metrics
|
|
1382
|
+
: *enable metrics* (boolean) : if checked, then the node will produce metrics output to the specified metrics provider.
|
|
1383
|
+
: *metric collector* (metricconfig) : the collection point or grouping where this particular metric will be attached.
|
|
1384
|
+
</script>
|
|
1385
|
+
|
|
1386
|
+
|