@inteli.city/node-red-contrib-exec-collection 1.0.3 → 1.0.5
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/README.md +836 -5
- package/async.pg.js +1 -0
- package/exec.queue.html +228 -38
- package/exec.queue.js +554 -538
- package/exec.service.html +342 -229
- package/exec.service.js +325 -487
- package/node.queue.html +359 -0
- package/node.queue.js +569 -0
- package/package.json +19 -19
- package/python.config.html +55 -0
- package/python.config.js +24 -0
- package/python.queue.html +360 -0
- package/python.queue.js +555 -0
- package/utils/context.js +54 -0
- package/async.gpt.html +0 -327
- package/async.gpt.js +0 -615
- package/async.latex.html +0 -319
- package/async.latex.js +0 -618
- package/module.njk.html +0 -45
- package/module.njk.js +0 -12
- package/template.njk.html +0 -201
- package/template.njk.js +0 -138
- package/thrd.function.html +0 -312
- package/thrd.function.js +0 -586
- package/thread.queue.html +0 -311
- package/thread.queue.js +0 -586
package/exec.service.html
CHANGED
|
@@ -1,311 +1,424 @@
|
|
|
1
1
|
<!--* header -->
|
|
2
2
|
<script type="text/markdown" data-help-name="exec.service">
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
3
|
+
## exec.service
|
|
4
|
+
|
|
5
|
+
Runs a shell command as a persistent service. stdout is streamed continuously as output messages. The process is restarted automatically on failure.
|
|
6
|
+
|
|
7
|
+
**Mental model:** you are managing a daemon — not a one-shot command. The process stays alive and keeps emitting output.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Command
|
|
12
|
+
|
|
13
|
+
Any shell command that runs continuously:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
tail -f /var/log/syslog
|
|
17
|
+
python3 /opt/services/listener.py
|
|
18
|
+
node /opt/services/worker.js
|
|
19
|
+
ffmpeg -i rtsp://... -f ...
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The command is executed via `/bin/bash -c` (Linux) or `cmd.exe /c` (Windows).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Template and `$file`
|
|
27
|
+
|
|
28
|
+
Write code in the **Template** editor and reference it with `$file` in the command:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
python3 $file
|
|
32
|
+
node $file
|
|
33
|
+
bash $file
|
|
34
|
+
Rscript $file
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The template is rendered with Nunjucks before the process starts and written to a temporary file. Use `{{ flow.get('key') }}`, `{{ global.get('key') }}`, or `{{ env.MY_VAR }}` to inject dynamic values. The template re-renders on every restart.
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
# Example: Python service using a flow variable
|
|
41
|
+
import time, json
|
|
42
|
+
|
|
43
|
+
interval = {{ flow.get('pollInterval') or 5 }}
|
|
44
|
+
|
|
45
|
+
while True:
|
|
46
|
+
print(json.dumps({"tick": True}))
|
|
47
|
+
time.sleep(interval)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Restart behavior
|
|
53
|
+
|
|
54
|
+
When the process exits unexpectedly, the node automatically restarts it after the configured delay.
|
|
55
|
+
|
|
56
|
+
| Field | Default | Description |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| Restart delay | 1000 ms | Wait before restarting |
|
|
59
|
+
| Max retries | 0 | Maximum restart attempts (0 = infinite) |
|
|
60
|
+
|
|
61
|
+
If the process runs stably for 10 seconds, the retry counter resets.
|
|
62
|
+
|
|
63
|
+
Set **Max retries > 0** to stop after N failures (e.g. to avoid looping on a bad command).
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Status
|
|
68
|
+
|
|
69
|
+
| Badge | Meaning |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Blue ring `running` | Process is alive and streaming |
|
|
72
|
+
| Yellow ring `restarting (retry N)` | Waiting to restart after crash |
|
|
73
|
+
| Grey dot `stopped` | Manually killed or max retries exceeded |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Stop / Kill
|
|
78
|
+
|
|
79
|
+
Send `msg.stop = true` to stop the service without redeploying.
|
|
80
|
+
|
|
81
|
+
The **⏹ button** in the node editor header kills the process immediately. A confirmation dialog appears when the service is running or restarting.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Output
|
|
86
|
+
|
|
87
|
+
stdout is streamed using the same parsing system as `python.queue` and `node.queue`.
|
|
88
|
+
|
|
89
|
+
**Parsing: Delimited** (default) — buffers output and splits on the delimiter (`\n` by default). Each line produces one message.
|
|
90
|
+
|
|
91
|
+
**Parsing: Raw** — emits each stdout chunk immediately with no buffering. Chunks may not align with line boundaries.
|
|
92
|
+
|
|
93
|
+
**Output format** — how each segment is parsed: Plain text, JSON, YAML, or XML.
|
|
94
|
+
|
|
95
|
+
stderr lines become node warnings — they do not appear in `msg.payload`.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
[Full documentation](https://www.npmjs.com/package/@inteli.city/node-red-contrib-exec-collection)
|
|
26
100
|
</script>
|
|
101
|
+
|
|
102
|
+
<!--* styles -->
|
|
103
|
+
<style>
|
|
104
|
+
#node-es-restart-btn:hover { color: #337ab7 !important; }
|
|
105
|
+
#node-es-kill-btn:hover { color: #d9534f !important; }
|
|
106
|
+
#node-es-kill-btn.is-running { color: #d9534f !important; }
|
|
107
|
+
</style>
|
|
108
|
+
|
|
27
109
|
<!--* node-design -->
|
|
28
110
|
<script type="text/html" data-template-name="exec.service">
|
|
29
|
-
<div class="form-row">
|
|
111
|
+
<div class="form-row" style="position:relative;">
|
|
30
112
|
<label for="node-input-name">
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
113
|
+
<i class="fa fa-tag"></i>
|
|
114
|
+
<span data-i18n="node-red:common.label.name"></span>
|
|
115
|
+
</label>
|
|
116
|
+
<div style="display: inline-block; width: calc(100% - 165px)">
|
|
117
|
+
<input type="text" id="node-input-name" style="width:100%;" data-i18n="[placeholder]node-red:common.label.name">
|
|
118
|
+
</div>
|
|
119
|
+
<button id="node-es-restart-btn"
|
|
120
|
+
title="Restart service"
|
|
121
|
+
style="position:absolute; right:32px; top:50%; transform:translateY(-50%); z-index:5;
|
|
122
|
+
background:transparent; border:none; padding:6px 8px; cursor:pointer;
|
|
123
|
+
color:#888; font-size:13px; line-height:1;">
|
|
124
|
+
<i class="fa fa-refresh"></i>
|
|
125
|
+
</button>
|
|
126
|
+
<button id="node-es-kill-btn"
|
|
127
|
+
title="Kill service"
|
|
128
|
+
style="position:absolute; right:5px; top:50%; transform:translateY(-50%); z-index:5;
|
|
129
|
+
background:transparent; border:none; padding:6px 8px; cursor:pointer;
|
|
130
|
+
color:#888; font-size:13px; line-height:1;">
|
|
131
|
+
<i class="fa fa-stop"></i>
|
|
132
|
+
</button>
|
|
39
133
|
</div>
|
|
40
134
|
|
|
41
135
|
<div class="form-row">
|
|
42
136
|
<label for="node-input-command">
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<input style="display:
|
|
137
|
+
<i class="fa fa-terminal"></i>
|
|
138
|
+
<span>Command</span>
|
|
139
|
+
</label>
|
|
140
|
+
<input type="text" id="node-input-command" style="display:inline-block; width:calc(100% - 108px);" placeholder="e.g. tail -f /var/log/syslog" list="exec-service-command-suggestions">
|
|
141
|
+
<datalist id="exec-service-command-suggestions"></datalist>
|
|
142
|
+
</div>
|
|
47
143
|
|
|
144
|
+
<div class="form-row">
|
|
145
|
+
<label><i class="fa fa-clock-o"></i> <span>Restart</span></label>
|
|
146
|
+
<input type="number" id="node-input-restartDelay" style="width:65px;">
|
|
147
|
+
<span style="font-size:0.85em; color:#888; margin-left:3px;">ms</span>
|
|
148
|
+
<label style="margin-left:15px;"><i class="fa fa-repeat"></i> <span>Max retries</span></label>
|
|
149
|
+
<input type="number" id="node-input-maxRetries" style="width:55px; margin-left:0;">
|
|
150
|
+
<span style="font-size:0.75em; color:#888; margin-left:4px;">(0 = ∞)</span>
|
|
48
151
|
</div>
|
|
49
152
|
|
|
50
|
-
<div class="form-row" style="position:
|
|
153
|
+
<div class="form-row" style="position:relative;">
|
|
51
154
|
<label>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<span style="margin-right:10px;font-size:9px;">No Input</span>
|
|
60
|
-
<input type="checkbox" id="node-input-inputEmpty" style="display:inline-block; width:auto;margin-right:30px;">
|
|
61
|
-
|
|
62
|
-
<span style="margin-right:10px;font-size:9px;">Cmd Template</span>
|
|
63
|
-
<!-- <input type="checkbox" id="node-input-addpayCB" style="display:inline-block; width:auto;margin-right:30px;"> -->
|
|
64
|
-
<input type="checkbox" id="node-input-cmdTemplate" style="display:inline-block; width:auto;margin-right:30px;">
|
|
65
|
-
|
|
66
|
-
<input type="hidden" id="node-input-template" autofocus="autofocus">
|
|
67
|
-
|
|
68
|
-
<span style="font-size:9px;" data-i18n="node-red:template.label.format"></span>:
|
|
69
|
-
<select id="node-input-format" style="width:110px; font-size: 10px !important; height: 24px; padding:0;">
|
|
70
|
-
<option value="handlebars">Mustache</option>
|
|
71
|
-
<option value="javascript">Javascript</option>
|
|
155
|
+
<i class="fa fa-code"></i>
|
|
156
|
+
<span>Template</span>
|
|
157
|
+
</label>
|
|
158
|
+
<div style="position:absolute; right:0; display:inline-block; text-align:right; font-size:0.8em;">
|
|
159
|
+
<input type="hidden" id="node-input-template">
|
|
160
|
+
<select id="node-input-format" style="width:90px; font-size:10px !important; height:24px; padding:0;">
|
|
72
161
|
<option value="python">Python</option>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<option value="nginx">NGINX</option>
|
|
77
|
-
<option value="apache_conf">Apache</option>
|
|
78
|
-
<option value="dockerfile">Dockerfile</option>
|
|
79
|
-
<option value="terraform">Terraform</option>
|
|
80
|
-
<option value="text" data-i18n="node-red:template.label.none"></option>
|
|
162
|
+
<option value="javascript">JavaScript</option>
|
|
163
|
+
<option value="sh">Shell</option>
|
|
164
|
+
<option value="text">Text</option>
|
|
81
165
|
</select>
|
|
82
|
-
<button id="node-template-expand-editor" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button>
|
|
166
|
+
<button id="node-es-template-expand-editor" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button>
|
|
83
167
|
</div>
|
|
84
168
|
</div>
|
|
85
169
|
<div class="form-row node-text-editor-row">
|
|
86
|
-
<div style="height:
|
|
170
|
+
<div style="height:250px; min-height:150px;" class="node-text-editor" id="node-input-template-editor"></div>
|
|
87
171
|
</div>
|
|
88
172
|
|
|
89
|
-
<div class="form-row" style="margin-bottom:0px;">
|
|
173
|
+
<div class="form-row" style="margin-bottom:0px; white-space:nowrap;">
|
|
90
174
|
<label for="node-input-output"><i class="fa fa-long-arrow-right"></i> <span data-i18n="node-red:template.label.output"></span></label>
|
|
91
|
-
<select id="node-input-output" style="width:
|
|
175
|
+
<select id="node-input-output" style="width:90px;">
|
|
92
176
|
<option value="str">Plain text</option>
|
|
93
177
|
<option value="parsedJSON">Parsed JSON</option>
|
|
94
178
|
<option value="parsedYAML">Parsed YAML</option>
|
|
95
179
|
<option value="parsedXML">Parsed XML</option>
|
|
96
180
|
</select>
|
|
97
181
|
|
|
182
|
+
<label style="margin-left:14px;" for="node-input-field"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
|
|
183
|
+
<input style="margin-left:-14px; width:118px;" type="text" id="node-input-field" placeholder="payload">
|
|
184
|
+
<input style="margin-left:-14px; width:118px;" type="hidden" id="node-input-fieldType">
|
|
98
185
|
|
|
99
|
-
<label style="margin-left:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<select
|
|
104
|
-
<option value="
|
|
105
|
-
<option value="
|
|
186
|
+
<label style="margin-left:14px;">
|
|
187
|
+
<i class="fa fa-random"></i>
|
|
188
|
+
<span>Parsing</span>
|
|
189
|
+
</label>
|
|
190
|
+
<select id="node-input-streamMode" style="width:90px; margin-left:-20px;">
|
|
191
|
+
<option value="delimited">Delimited</option>
|
|
192
|
+
<option value="raw">Raw</option>
|
|
106
193
|
</select>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<span style="margin-left:20px;font-size:11px;">Split \n</span>
|
|
113
|
-
<input type="checkbox" id="node-input-splitLine" style="display:inline-block; width:auto;margin-left:10px;">
|
|
194
|
+
<input type="text" id="node-input-delimiter" style="margin-left:4px; width:46px;" placeholder="\n">
|
|
195
|
+
</div>
|
|
196
|
+
<div id="node-es-raw-hint" style="display:none; text-align:right; margin-top:3px; font-size:0.75em; color:#aaa;">
|
|
197
|
+
Raw output may produce incomplete messages.
|
|
114
198
|
</div>
|
|
115
|
-
|
|
116
199
|
</script>
|
|
200
|
+
|
|
117
201
|
<!--* javascript -->
|
|
118
202
|
<script type="text/javascript">
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//color:"rgb(180, 100, 100)",
|
|
122
|
-
color:"rgb(222, 204, 173)",
|
|
203
|
+
RED.nodes.registerType('exec.service', {
|
|
204
|
+
color: "#DEB887",
|
|
123
205
|
category: 'function',
|
|
124
206
|
defaults: {
|
|
125
|
-
name:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
output:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
cmdTemplate: {value:true},
|
|
138
|
-
splitLine: {value:false},
|
|
139
|
-
cleanQueue: {value:false}
|
|
207
|
+
name: { value: "" },
|
|
208
|
+
command: { value: "" },
|
|
209
|
+
template: { value: "" },
|
|
210
|
+
format: { value: "sh" },
|
|
211
|
+
currentLine: { value: { row: 0, column: 0 } },
|
|
212
|
+
restartDelay: { value: 3000 },
|
|
213
|
+
maxRetries: { value: 0 },
|
|
214
|
+
streamMode: { value: "delimited" },
|
|
215
|
+
delimiter: { value: "\\n", validate: function(v) { return typeof v === 'string' && v.length > 0; } },
|
|
216
|
+
output: { value: "str" },
|
|
217
|
+
field: { value: "payload", validate: RED.validators.typedInput("fieldType") },
|
|
218
|
+
fieldType: { value: "msg" }
|
|
140
219
|
},
|
|
141
|
-
inputs:0,
|
|
220
|
+
inputs: 0,
|
|
142
221
|
outputs: 1,
|
|
143
|
-
icon: "
|
|
144
|
-
//** function: label
|
|
222
|
+
icon: "cog.png",
|
|
145
223
|
label: function() {
|
|
146
|
-
return this.name||
|
|
224
|
+
return this.name || "exec.service";
|
|
147
225
|
},
|
|
148
|
-
//** function: labelStyle
|
|
149
226
|
labelStyle: function() {
|
|
150
|
-
return this.name?"node_label_italic":"";
|
|
227
|
+
return this.name ? "node_label_italic" : "";
|
|
151
228
|
},
|
|
152
|
-
//** function: oneditprepare
|
|
153
229
|
oneditprepare: function() {
|
|
154
230
|
var that = this;
|
|
155
231
|
|
|
156
|
-
if (!this.field)
|
|
157
|
-
|
|
158
|
-
$("#node-input-field").val("payload");
|
|
159
|
-
}
|
|
160
|
-
if (!this.fieldType) {
|
|
161
|
-
this.fieldType = 'msg';
|
|
162
|
-
}
|
|
163
|
-
$("#node-input-field").typedInput({
|
|
164
|
-
default: 'msg',
|
|
165
|
-
types: ['msg','flow','global'],
|
|
166
|
-
typeField: $("#node-input-fieldType")
|
|
167
|
-
});
|
|
232
|
+
if (!this.field) { this.field = 'payload'; }
|
|
233
|
+
if (!this.fieldType) { this.fieldType = 'msg'; }
|
|
168
234
|
|
|
169
235
|
$("#node-input-field").typedInput({
|
|
170
236
|
default: 'msg',
|
|
171
|
-
types: ['msg','flow','global'],
|
|
237
|
+
types: ['msg', 'flow', 'global'],
|
|
172
238
|
typeField: $("#node-input-fieldType")
|
|
173
239
|
});
|
|
174
240
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
$("#node-input-
|
|
178
|
-
|
|
179
|
-
|
|
241
|
+
function updateStreamUI() {
|
|
242
|
+
var mode = $("#node-input-streamMode").val();
|
|
243
|
+
var $sel = $("#node-input-streamMode");
|
|
244
|
+
var $rawOpt = $sel.find("option[value='raw']");
|
|
245
|
+
if (mode === 'raw') {
|
|
246
|
+
$("#node-input-delimiter").hide();
|
|
247
|
+
$("#node-es-raw-hint").show();
|
|
248
|
+
$sel.css("color", "#b8860b");
|
|
249
|
+
$rawOpt.text("⚠ Raw");
|
|
250
|
+
$sel.attr("title", "Raw mode emits unprocessed chunks — messages may be incomplete.");
|
|
251
|
+
} else {
|
|
252
|
+
$("#node-input-delimiter").show();
|
|
253
|
+
$("#node-es-raw-hint").hide();
|
|
254
|
+
$sel.css("color", "");
|
|
255
|
+
$rawOpt.text("Raw");
|
|
256
|
+
$sel.removeAttr("title");
|
|
257
|
+
}
|
|
180
258
|
}
|
|
259
|
+
$("#node-input-streamMode").on("change", updateStreamUI);
|
|
260
|
+
updateStreamUI();
|
|
181
261
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
262
|
+
// ── command suggestions ───────────────────────────────────────────
|
|
263
|
+
var commandSuggestions = [
|
|
264
|
+
"bash $file",
|
|
265
|
+
"node $file",
|
|
266
|
+
"python3 -u $file",
|
|
267
|
+
"inotifywait -m -e create --format '%f' ~/Downloads/",
|
|
268
|
+
"inotifywait -m -e create,modify,delete,move,close_write --format '{\"file\":\"%w%f\",\"event\":\"%e\"}' ~/Downloads/",
|
|
269
|
+
'psql postgresql://user:pass@host:port/db -c "LISTEN my_channel;" -c "SELECT 1" --no-align --tuples-only',
|
|
270
|
+
"tail -F /var/log/syslog | grep ERROR",
|
|
271
|
+
"while true; do df -h | jc --df; sleep 5; done",
|
|
272
|
+
"while true; do top -b -n1 | jc --top 2>/dev/null; sleep 5; done"
|
|
273
|
+
];
|
|
274
|
+
var $dl = $("#exec-service-command-suggestions");
|
|
275
|
+
$dl.empty();
|
|
276
|
+
commandSuggestions.forEach(function(cmd) {
|
|
277
|
+
$dl.append($("<option>").val(cmd));
|
|
186
278
|
});
|
|
187
279
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
280
|
+
// ── template editor ───────────────────────────────────────────────
|
|
281
|
+
var fmt = this.format || 'python';
|
|
282
|
+
this.editor = RED.editor.createEditor({
|
|
283
|
+
id: 'node-input-template-editor',
|
|
284
|
+
mode: 'ace/mode/' + fmt,
|
|
285
|
+
value: $("#node-input-template").val()
|
|
194
286
|
});
|
|
195
|
-
this.editor.focus();
|
|
196
287
|
|
|
288
|
+
$("#node-input-format").val(fmt);
|
|
197
289
|
$("#node-input-format").on("change", function() {
|
|
198
|
-
var
|
|
199
|
-
that.editor.getSession().setMode({
|
|
200
|
-
path: mod,
|
|
201
|
-
v: Date.now()
|
|
202
|
-
});
|
|
290
|
+
var mode = "ace/mode/" + $(this).val();
|
|
291
|
+
that.editor.getSession().setMode({ path: mode, v: Date.now() });
|
|
203
292
|
});
|
|
204
293
|
|
|
294
|
+
if (this.currentLine === undefined) { this.currentLine = { row: 0, column: 0 }; }
|
|
295
|
+
this.editor.resize(true);
|
|
296
|
+
this.editor.scrollToLine(this.currentLine.row + 1, true, true, function() {});
|
|
297
|
+
this.editor.gotoLine(this.currentLine.row + 1, this.currentLine.column + 1, true);
|
|
205
298
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
* try {
|
|
211
|
-
* if ( that.vimMode === "true" || that.vimMode === true ){
|
|
212
|
-
* if ( firstTime_vimMode ){
|
|
213
|
-
* that.editor.setKeyboardHandler("ace/keyboard/vim")
|
|
214
|
-
* } else {
|
|
215
|
-
* that.vimMode = false
|
|
216
|
-
* that.editor.setKeyboardHandler(null)
|
|
217
|
-
* }
|
|
218
|
-
* } else {
|
|
219
|
-
* if ( firstTime_vimMode ){
|
|
220
|
-
* that.editor.setKeyboardHandler(null)
|
|
221
|
-
* } else {
|
|
222
|
-
* that.vimMode = true
|
|
223
|
-
* that.editor.setKeyboardHandler("ace/keyboard/vim")
|
|
224
|
-
* }
|
|
225
|
-
* }
|
|
226
|
-
* } catch (e) {
|
|
227
|
-
* console.log("Vim Mode only works with the Ace editor")
|
|
228
|
-
* }
|
|
229
|
-
setTimeout(function() {
|
|
230
|
-
let panel = $(".ace_text-input");
|
|
231
|
-
panel.focus();
|
|
232
|
-
firstTime_vimMode = false
|
|
233
|
-
}, 600);
|
|
234
|
-
* });
|
|
235
|
-
*/
|
|
236
|
-
let firstTime_inputEmpty = true
|
|
237
|
-
$("#node-input-inputEmpty").on("change", function() {
|
|
238
|
-
if ( that.inputEmpty === "true" || that.inputEmpty === true ){
|
|
239
|
-
if ( firstTime_inputEmpty ){
|
|
240
|
-
that.inputs = 0
|
|
241
|
-
} else {
|
|
242
|
-
that.inputEmpty = false
|
|
243
|
-
that.inputs = 1
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
if ( firstTime_inputEmpty ){
|
|
247
|
-
that.inputs = 1
|
|
248
|
-
} else {
|
|
249
|
-
that.inputEmpty = true
|
|
250
|
-
that.inputs = 0
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
setTimeout(function() {
|
|
254
|
-
firstTime_inputEmpty = false
|
|
255
|
-
}, 600);
|
|
256
|
-
});
|
|
299
|
+
RED.popover.tooltip(
|
|
300
|
+
$("#node-es-template-expand-editor"),
|
|
301
|
+
RED._("node-red:common.label.expand")
|
|
302
|
+
);
|
|
257
303
|
|
|
258
|
-
|
|
259
|
-
this.editor.resize(true);
|
|
260
|
-
this.editor.scrollToLine(this.currentLine.row+1, true, true, function () {});
|
|
261
|
-
this.editor.gotoLine(this.currentLine.row+1,this.currentLine.column+1,true);
|
|
262
|
-
RED.popover.tooltip($("#node-template-expand-editor"), RED._("node-red:common.label.expand"));
|
|
263
|
-
$("#node-template-expand-editor").on("click", function(e) {
|
|
304
|
+
$("#node-es-template-expand-editor").on("click", function(e) {
|
|
264
305
|
e.preventDefault();
|
|
265
306
|
var value = that.editor.getValue();
|
|
266
307
|
RED.editor.editText({
|
|
267
|
-
mode:
|
|
268
|
-
value:
|
|
269
|
-
width:
|
|
308
|
+
mode: $("#node-input-format").val(),
|
|
309
|
+
value: value,
|
|
310
|
+
width: "Infinity",
|
|
270
311
|
cursor: that.editor.getCursorPosition(),
|
|
271
|
-
complete: function(v,cursor) {
|
|
312
|
+
complete: function(v, cursor) {
|
|
272
313
|
that.editor.setValue(v, -1);
|
|
273
|
-
that.editor.gotoLine(cursor.row+1,cursor.column,false);
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
314
|
+
that.editor.gotoLine(cursor.row + 1, cursor.column, false);
|
|
315
|
+
setTimeout(function() { that.editor.focus(); }, 300);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// ── kill button ───────────────────────────────────────────────────
|
|
321
|
+
setTimeout(function() {
|
|
322
|
+
var nodeId = that.id;
|
|
323
|
+
|
|
324
|
+
function refreshKillBtn() {
|
|
325
|
+
$.get("exec-service/" + nodeId + "/status", function(data) {
|
|
326
|
+
var active = data.running || data.restarting;
|
|
327
|
+
$("#node-es-kill-btn").toggleClass("is-running", active);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
refreshKillBtn();
|
|
331
|
+
var killBtnInterval = setInterval(refreshKillBtn, 1000);
|
|
332
|
+
|
|
333
|
+
$("#node-es-restart-btn").on("click", function(e) {
|
|
334
|
+
e.preventDefault();
|
|
335
|
+
e.stopPropagation();
|
|
336
|
+
|
|
337
|
+
function doRestart() {
|
|
338
|
+
$.post("exec-service/" + nodeId + "/restart");
|
|
339
|
+
setTimeout(refreshKillBtn, 300);
|
|
282
340
|
}
|
|
283
|
-
|
|
284
|
-
|
|
341
|
+
|
|
342
|
+
$.get("exec-service/" + nodeId + "/status", function(data) {
|
|
343
|
+
console.log("[exec.service restart] click — nodeId:", nodeId, "running:", data.running, "restarting:", data.restarting);
|
|
344
|
+
if (data.running || data.restarting) {
|
|
345
|
+
var $dlg = $("<div>")
|
|
346
|
+
.html("Restart running service?")
|
|
347
|
+
.dialog({
|
|
348
|
+
modal: true,
|
|
349
|
+
title: "Restart service",
|
|
350
|
+
closeOnEscape: true,
|
|
351
|
+
width: 320,
|
|
352
|
+
buttons: {
|
|
353
|
+
"Cancel": function() { $(this).dialog("close"); },
|
|
354
|
+
"Restart": function() { $(this).dialog("close"); doRestart(); }
|
|
355
|
+
},
|
|
356
|
+
close: function() { $(this).dialog("destroy").remove(); }
|
|
357
|
+
});
|
|
358
|
+
$dlg.closest(".ui-dialog").css("z-index", 9999);
|
|
359
|
+
} else {
|
|
360
|
+
doRestart();
|
|
361
|
+
}
|
|
362
|
+
}).fail(function() { doRestart(); });
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
$("#node-es-kill-btn").on("click", function(e) {
|
|
366
|
+
e.preventDefault();
|
|
367
|
+
e.stopPropagation();
|
|
368
|
+
|
|
369
|
+
function doKill() {
|
|
370
|
+
$.post("exec-service/" + nodeId + "/kill");
|
|
371
|
+
setTimeout(refreshKillBtn, 300);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
$.get("exec-service/" + nodeId + "/status", function(data) {
|
|
375
|
+
console.log("[exec.service kill] click — nodeId:", nodeId, "running:", data.running, "restarting:", data.restarting);
|
|
376
|
+
if (data.running || data.restarting) {
|
|
377
|
+
var msg = data.running ? "Kill running service?" : "Stop auto-restart?";
|
|
378
|
+
var $dlg = $("<div>")
|
|
379
|
+
.html(msg)
|
|
380
|
+
.dialog({
|
|
381
|
+
modal: true,
|
|
382
|
+
title: "Kill service",
|
|
383
|
+
closeOnEscape: true,
|
|
384
|
+
width: 320,
|
|
385
|
+
buttons: {
|
|
386
|
+
"Cancel": function() { $(this).dialog("close"); },
|
|
387
|
+
"Kill": function() { $(this).dialog("close"); doKill(); }
|
|
388
|
+
},
|
|
389
|
+
close: function() { $(this).dialog("destroy").remove(); }
|
|
390
|
+
});
|
|
391
|
+
$dlg.closest(".ui-dialog").css("z-index", 9999);
|
|
392
|
+
} else {
|
|
393
|
+
doKill();
|
|
394
|
+
}
|
|
395
|
+
}).fail(function() { doKill(); });
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
that._killBtnInterval = killBtnInterval;
|
|
399
|
+
}, 0);
|
|
285
400
|
},
|
|
286
|
-
//** function: oneditsave
|
|
287
401
|
oneditsave: function() {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
402
|
+
this.currentLine = this.editor.getCursorPosition();
|
|
403
|
+
$("#node-input-template").val(this.editor.getValue());
|
|
404
|
+
this.editor.destroy();
|
|
405
|
+
delete this.editor;
|
|
406
|
+
if (this._killBtnInterval) { clearInterval(this._killBtnInterval); delete this._killBtnInterval; }
|
|
293
407
|
},
|
|
294
|
-
//** function: oneditcancel
|
|
295
408
|
oneditcancel: function() {
|
|
296
409
|
this.editor.destroy();
|
|
297
410
|
delete this.editor;
|
|
411
|
+
if (this._killBtnInterval) { clearInterval(this._killBtnInterval); delete this._killBtnInterval; }
|
|
298
412
|
},
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
|
413
|
+
oneditresize: function() {
|
|
414
|
+
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
|
302
415
|
var height = $("#dialog-form").height();
|
|
303
|
-
for (var i=0; i<rows.length; i++) {
|
|
416
|
+
for (var i = 0; i < rows.length; i++) {
|
|
304
417
|
height -= $(rows[i]).outerHeight(true);
|
|
305
418
|
}
|
|
306
419
|
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
|
307
|
-
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
|
308
|
-
$(".node-text-editor").css("height",height+"px");
|
|
420
|
+
height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom")));
|
|
421
|
+
$(".node-text-editor").css("height", height + "px");
|
|
309
422
|
this.editor.resize();
|
|
310
423
|
}
|
|
311
424
|
});
|