@frangoteam/fuxa-min 1.3.0 → 1.3.1
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/api/command/index.js +1 -1
- package/api/index.js +3 -0
- package/api/scripts/index.js +7 -3
- package/dist/assets/i18n/en.json +10 -3
- package/dist/assets/i18n/fr.json +2 -1
- package/dist/assets/i18n/ja.json +2 -1
- package/dist/assets/i18n/sv.json +2 -1
- package/dist/assets/i18n/zh-cn.json +2 -1
- package/dist/assets/i18n/zh-tw.json +2 -1
- package/dist/assets/lib/svgeditor/fuxa-editor.min.js +17 -23
- package/dist/index.html +1 -1
- package/dist/{main.bafae830903d548e.js → main.72bdfed42c527918.js} +6 -6
- package/docs/openapi.yaml +3 -0
- package/integrations/node-red/index.js +4 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.html +39 -35
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.js +43 -17
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.html +42 -34
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.js +32 -12
- package/main.js +3 -0
- package/package.json +1 -1
- package/runtime/alarms/alarmstorage.js +45 -30
- package/runtime/apikeys/apiKeysStorage.js +34 -22
- package/runtime/devices/odbc/index.js +9 -4
- package/runtime/devices/s7/index.js +2 -2
- package/runtime/notificator/notifystorage.js +34 -23
- package/runtime/project/index.js +16 -0
- package/runtime/project/prjstorage.js +78 -40
- package/runtime/scripts/index.js +6 -2
- package/runtime/storage/calculator.js +17 -13
- package/runtime/storage/influxdb/index.js +8 -3
- package/runtime/storage/questdb/index.js +1 -1
- package/runtime/storage/sqlite/index.js +11 -8
- package/runtime/storage/tdengine/index.js +24 -9
- package/runtime/users/usrstorage.js +65 -61
- package/settings.default.js +3 -0
package/docs/openapi.yaml
CHANGED
|
@@ -652,6 +652,9 @@ paths:
|
|
|
652
652
|
tags: [Scripts]
|
|
653
653
|
summary: Run script
|
|
654
654
|
security: [{ AccessToken: [] }, { ApiKey: [] }]
|
|
655
|
+
description: >
|
|
656
|
+
Runs a stored server-side script. Requests that set `params.script.test=true`
|
|
657
|
+
are limited to authenticated administrators.
|
|
655
658
|
requestBody:
|
|
656
659
|
required: true
|
|
657
660
|
content:
|
|
@@ -152,12 +152,11 @@ async function mountNodeRedIfInstalled({ app, server, settings, runtime, logger,
|
|
|
152
152
|
});
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
-
// Allow
|
|
155
|
+
// Allow only dashboard routes as public; require JWT or API key for admin/editor/flows when security is enabled
|
|
156
156
|
const allowDashboard = (req, res, next) => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (url.includes('/dashboard') || url.includes('/socket.io')) return next();
|
|
157
|
+
// Public dashboard UI and its HTTP APIs (served from httpNodeRoot/ui.path).
|
|
158
|
+
// baseUrl comes from Express mount point and is not affected by query/path tricks.
|
|
159
|
+
if (req.baseUrl === '/dashboard') return next();
|
|
161
160
|
|
|
162
161
|
if (!settings.secureEnabled || settings.nodeRedAuthMode === 'legacy-open') {
|
|
163
162
|
return next();
|
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
|
-
RED.nodes.registerType('get-tag',{
|
|
2
|
+
RED.nodes.registerType('get-tag', {
|
|
3
3
|
category: 'FUXA',
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
|
-
name: {value:""},
|
|
7
|
-
tag: {value:""},
|
|
8
|
-
tagId: {value:""}
|
|
6
|
+
name: {value: ""},
|
|
7
|
+
tag: {value: ""},
|
|
8
|
+
tagId: {value: ""}
|
|
9
9
|
},
|
|
10
|
-
inputs:1,
|
|
11
|
-
outputs:1,
|
|
10
|
+
inputs: 1,
|
|
11
|
+
outputs: 1,
|
|
12
12
|
icon: "white-globe.png",
|
|
13
13
|
label: function() {
|
|
14
14
|
if (this.name) {
|
|
15
15
|
return this.name;
|
|
16
16
|
}
|
|
17
|
-
// Display tag name or tagId
|
|
18
17
|
return this.tag || this.tagId || "get tag";
|
|
19
18
|
},
|
|
20
19
|
oneditprepare: function() {
|
|
21
20
|
var node = this;
|
|
22
|
-
var tagMap = {};
|
|
23
|
-
|
|
21
|
+
var tagMap = {};
|
|
22
|
+
|
|
24
23
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
25
24
|
var datalist = $('#fuxa-tags');
|
|
26
25
|
datalist.empty();
|
|
27
|
-
|
|
28
|
-
data.forEach(function(device) {
|
|
29
|
-
device.tags.forEach(function(tag) {
|
|
26
|
+
|
|
27
|
+
(data || []).forEach(function(device) {
|
|
28
|
+
(device.tags || []).forEach(function(tag) {
|
|
30
29
|
var fullName = device.name + ' - ' + tag.name;
|
|
31
|
-
// datalist option value format: tag.name(tag.id)
|
|
32
30
|
var optionValue = tag.name + '(' + tag.id + ')';
|
|
33
31
|
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
34
32
|
tagMap[tag.id] = {
|
|
@@ -38,39 +36,34 @@
|
|
|
38
36
|
};
|
|
39
37
|
});
|
|
40
38
|
});
|
|
41
|
-
|
|
42
|
-
// Listen for tagSelected input changes
|
|
39
|
+
|
|
43
40
|
$('#node-input-tagSelected').on('change', function() {
|
|
44
41
|
var selectedValue = $(this).val();
|
|
45
|
-
// Parse format: tag.name(tag.id)
|
|
46
42
|
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
47
43
|
if (match) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
$('#node-input-
|
|
53
|
-
$('#node-input-tagId').val(tagId);
|
|
44
|
+
$('#node-input-tag').val(match[1]);
|
|
45
|
+
$('#node-input-tagId').val(match[2]);
|
|
46
|
+
} else if (!selectedValue || !selectedValue.trim()) {
|
|
47
|
+
$('#node-input-tag').val('');
|
|
48
|
+
$('#node-input-tagId').val('');
|
|
54
49
|
}
|
|
55
50
|
});
|
|
56
|
-
|
|
57
|
-
// Initialize display (when editing existing node)
|
|
51
|
+
|
|
58
52
|
if (node.tagId && tagMap[node.tagId]) {
|
|
59
|
-
// Has tagId, construct tagSelected value
|
|
60
53
|
var tagName = node.tag || tagMap[node.tagId].name;
|
|
61
54
|
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
62
55
|
} else if (node.tag && !node.tagId) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
$('#node-input-tagSelected').val(node.tag + '(' + tagId + ')');
|
|
68
|
-
$('#node-input-tagId').val(tagId);
|
|
56
|
+
for (var id in tagMap) {
|
|
57
|
+
if (tagMap[id].name === node.tag) {
|
|
58
|
+
$('#node-input-tagSelected').val(node.tag + '(' + id + ')');
|
|
59
|
+
$('#node-input-tagId').val(id);
|
|
69
60
|
break;
|
|
70
61
|
}
|
|
71
62
|
}
|
|
72
63
|
}
|
|
73
64
|
});
|
|
65
|
+
|
|
66
|
+
$('#node-input-tagSelected').attr('placeholder', 'Optional - leave empty to use msg.topic');
|
|
74
67
|
}
|
|
75
68
|
});
|
|
76
69
|
</script>
|
|
@@ -82,14 +75,25 @@
|
|
|
82
75
|
</div>
|
|
83
76
|
<div class="form-row">
|
|
84
77
|
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
85
|
-
<input type="text" id="node-input-tagSelected" list="fuxa-tags" placeholder="
|
|
78
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags" placeholder="Optional - leave empty to use msg.topic">
|
|
86
79
|
<datalist id="fuxa-tags"></datalist>
|
|
87
80
|
</div>
|
|
88
81
|
<input type="hidden" id="node-input-tag">
|
|
89
82
|
<input type="hidden" id="node-input-tagId">
|
|
83
|
+
<div class="form-row">
|
|
84
|
+
<div style="margin-left:105px;font-size:12px;color:#666;">
|
|
85
|
+
If <b>Tag</b> is empty, the node uses <code>msg.topic</code>.
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
90
88
|
</script>
|
|
91
89
|
|
|
92
90
|
<script type="text/x-red" data-help-name="get-tag">
|
|
93
|
-
<p>
|
|
94
|
-
<
|
|
95
|
-
</
|
|
91
|
+
<p>Gets the value of a FUXA tag.</p>
|
|
92
|
+
<ul>
|
|
93
|
+
<li>If <b>Tag</b> is set in the node, that tag is used.</li>
|
|
94
|
+
<li>If <b>Tag</b> is empty, <code>msg.topic</code> is used.</li>
|
|
95
|
+
</ul>
|
|
96
|
+
<p>The tag value is returned in <code>msg.payload</code>.</p>
|
|
97
|
+
<p>The resolved tag name is always set on <code>msg.topic</code>.</p>
|
|
98
|
+
<pre>msg.topic = "V2";</pre>
|
|
99
|
+
</script>
|
|
@@ -1,31 +1,57 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
2
|
function FuxaGetTagNode(config) {
|
|
3
3
|
RED.nodes.createNode(this, config);
|
|
4
|
-
|
|
4
|
+
const node = this;
|
|
5
|
+
|
|
5
6
|
// Access FUXA functions from global context
|
|
6
|
-
|
|
7
|
+
const fuxa = RED.settings?.functionGlobalContext?.fuxa;
|
|
8
|
+
|
|
9
|
+
node.on("input", function(msg, send, done) {
|
|
10
|
+
send = send || node.send.bind(node);
|
|
7
11
|
|
|
8
|
-
this.on('input', function(msg) {
|
|
9
12
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// Backward compatibility: old nodes use tag.name, need to convert to tagId
|
|
14
|
-
tagId = fuxa.getTagId(config.tag, null);
|
|
13
|
+
if (!fuxa) {
|
|
14
|
+
node.error("FUXA not available in functionGlobalContext", msg);
|
|
15
|
+
return done && done();
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
|
|
18
|
+
const uiTag = (typeof config.tag === "string") ? config.tag.trim() : "";
|
|
19
|
+
const topicTag = (typeof msg.topic === "string") ? msg.topic.trim() : "";
|
|
20
|
+
const tagRef = uiTag || topicTag;
|
|
21
|
+
let tagId = config.tagId || null;
|
|
22
|
+
|
|
23
|
+
// Keep master behavior (prefer configured tagId), then fallback to UI tag/topic lookup.
|
|
24
|
+
if (!tagId && !tagRef) {
|
|
25
|
+
node.error("No tag provided: set Tag in the node OR provide msg.topic", msg);
|
|
26
|
+
return done && done();
|
|
24
27
|
}
|
|
28
|
+
|
|
29
|
+
if (!tagId) {
|
|
30
|
+
tagId = fuxa.getTagId(tagRef, null);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!tagId) {
|
|
34
|
+
node.error("Tag not found: " + tagRef, msg);
|
|
35
|
+
return done && done();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const value = fuxa.getTag(tagId);
|
|
39
|
+
|
|
40
|
+
msg.payload = value;
|
|
41
|
+
if (tagRef) {
|
|
42
|
+
msg.topic = tagRef;
|
|
43
|
+
} else if (typeof config.tag === "string" && config.tag.trim()) {
|
|
44
|
+
msg.topic = config.tag.trim();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
send(msg);
|
|
48
|
+
return done && done();
|
|
25
49
|
} catch (err) {
|
|
26
50
|
node.error(err, msg);
|
|
51
|
+
return done && done(err);
|
|
27
52
|
}
|
|
28
53
|
});
|
|
29
54
|
}
|
|
55
|
+
|
|
30
56
|
RED.nodes.registerType("get-tag", FuxaGetTagNode);
|
|
31
|
-
}
|
|
57
|
+
};
|
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
|
-
RED.nodes.registerType('set-tag',{
|
|
2
|
+
RED.nodes.registerType('set-tag', {
|
|
3
3
|
category: 'FUXA',
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
|
-
name: {value:""},
|
|
7
|
-
tag: {value:""},
|
|
8
|
-
tagId: {value:""}
|
|
6
|
+
name: {value: ""},
|
|
7
|
+
tag: {value: ""},
|
|
8
|
+
tagId: {value: ""}
|
|
9
9
|
},
|
|
10
|
-
inputs:1,
|
|
11
|
-
outputs:1,
|
|
10
|
+
inputs: 1,
|
|
11
|
+
outputs: 1,
|
|
12
12
|
icon: "white-globe.png",
|
|
13
13
|
label: function() {
|
|
14
14
|
if (this.name) {
|
|
15
15
|
return this.name;
|
|
16
16
|
}
|
|
17
|
-
// Display tag name or tagId
|
|
18
17
|
return this.tag || this.tagId || "set tag";
|
|
19
18
|
},
|
|
20
19
|
oneditprepare: function() {
|
|
21
20
|
var node = this;
|
|
22
|
-
var tagMap = {};
|
|
23
|
-
|
|
21
|
+
var tagMap = {};
|
|
22
|
+
|
|
24
23
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
25
24
|
var datalist = $('#fuxa-tags-set');
|
|
26
25
|
datalist.empty();
|
|
27
|
-
|
|
28
|
-
data.forEach(function(device) {
|
|
29
|
-
device.tags.forEach(function(tag) {
|
|
26
|
+
|
|
27
|
+
(data || []).forEach(function(device) {
|
|
28
|
+
(device.tags || []).forEach(function(tag) {
|
|
30
29
|
var fullName = device.name + ' - ' + tag.name;
|
|
31
|
-
// datalist option value format: tag.name(tag.id)
|
|
32
30
|
var optionValue = tag.name + '(' + tag.id + ')';
|
|
33
31
|
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
34
32
|
tagMap[tag.id] = {
|
|
@@ -38,39 +36,34 @@
|
|
|
38
36
|
};
|
|
39
37
|
});
|
|
40
38
|
});
|
|
41
|
-
|
|
42
|
-
// Listen for tagSelected input changes
|
|
39
|
+
|
|
43
40
|
$('#node-input-tagSelected').on('change', function() {
|
|
44
41
|
var selectedValue = $(this).val();
|
|
45
|
-
// Parse format: tag.name(tag.id)
|
|
46
42
|
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
47
43
|
if (match) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
$('#node-input-
|
|
53
|
-
$('#node-input-tagId').val(tagId);
|
|
44
|
+
$('#node-input-tag').val(match[1]);
|
|
45
|
+
$('#node-input-tagId').val(match[2]);
|
|
46
|
+
} else if (!selectedValue || !selectedValue.trim()) {
|
|
47
|
+
$('#node-input-tag').val('');
|
|
48
|
+
$('#node-input-tagId').val('');
|
|
54
49
|
}
|
|
55
50
|
});
|
|
56
|
-
|
|
57
|
-
// Initialize display (when editing existing node)
|
|
51
|
+
|
|
58
52
|
if (node.tagId && tagMap[node.tagId]) {
|
|
59
|
-
// Has tagId, construct tagSelected value
|
|
60
53
|
var tagName = node.tag || tagMap[node.tagId].name;
|
|
61
54
|
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
62
55
|
} else if (node.tag && !node.tagId) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
$('#node-input-tagSelected').val(node.tag + '(' + tagId + ')');
|
|
68
|
-
$('#node-input-tagId').val(tagId);
|
|
56
|
+
for (var id in tagMap) {
|
|
57
|
+
if (tagMap[id].name === node.tag) {
|
|
58
|
+
$('#node-input-tagSelected').val(node.tag + '(' + id + ')');
|
|
59
|
+
$('#node-input-tagId').val(id);
|
|
69
60
|
break;
|
|
70
61
|
}
|
|
71
62
|
}
|
|
72
63
|
}
|
|
73
64
|
});
|
|
65
|
+
|
|
66
|
+
$('#node-input-tagSelected').attr('placeholder', 'Optional - leave empty to use msg.topic');
|
|
74
67
|
}
|
|
75
68
|
});
|
|
76
69
|
</script>
|
|
@@ -80,15 +73,30 @@
|
|
|
80
73
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
81
74
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
82
75
|
</div>
|
|
76
|
+
|
|
83
77
|
<div class="form-row">
|
|
84
78
|
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
85
|
-
<input type="text" id="node-input-tagSelected" list="fuxa-tags-set" placeholder="
|
|
79
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags-set" placeholder="Optional - leave empty to use msg.topic">
|
|
86
80
|
<datalist id="fuxa-tags-set"></datalist>
|
|
87
81
|
</div>
|
|
88
82
|
<input type="hidden" id="node-input-tag">
|
|
89
83
|
<input type="hidden" id="node-input-tagId">
|
|
84
|
+
|
|
85
|
+
<div class="form-row">
|
|
86
|
+
<div style="margin-left: 105px; font-size: 12px; color: #666;">
|
|
87
|
+
If <b>Tag</b> is empty, the node uses <code>msg.topic</code> as the tag name/id.
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
90
|
</script>
|
|
91
91
|
|
|
92
92
|
<script type="text/x-red" data-help-name="set-tag">
|
|
93
|
-
<p>
|
|
94
|
-
|
|
93
|
+
<p>Sets the value of a FUXA tag to <code>msg.payload</code>.</p>
|
|
94
|
+
<p><b>Tag selection:</b></p>
|
|
95
|
+
<ul>
|
|
96
|
+
<li>If the node's <b>Tag</b> field is set, that tag is used.</li>
|
|
97
|
+
<li>If the node's <b>Tag</b> field is empty, the node uses <code>msg.topic</code> as the tag name/id.</li>
|
|
98
|
+
</ul>
|
|
99
|
+
<p><b>Example:</b></p>
|
|
100
|
+
<pre>msg.topic = "V2";
|
|
101
|
+
msg.payload = true;</pre>
|
|
102
|
+
</script>
|
|
@@ -1,29 +1,49 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
2
|
function FuxaSetTagNode(config) {
|
|
3
3
|
RED.nodes.createNode(this, config);
|
|
4
|
-
|
|
4
|
+
const node = this;
|
|
5
|
+
|
|
5
6
|
// Access FUXA functions from global context
|
|
6
|
-
|
|
7
|
+
const fuxa = RED.settings?.functionGlobalContext?.fuxa;
|
|
8
|
+
|
|
9
|
+
node.on("input", async function(msg, send, done) {
|
|
10
|
+
send = send || node.send.bind(node);
|
|
7
11
|
|
|
8
|
-
this.on('input', async function(msg) {
|
|
9
12
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
if (!fuxa) {
|
|
14
|
+
node.error("FUXA not available in functionGlobalContext", msg);
|
|
15
|
+
return done && done();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const uiTag = (typeof config.tag === "string") ? config.tag.trim() : "";
|
|
19
|
+
const topicTag = (typeof msg.topic === "string") ? msg.topic.trim() : "";
|
|
20
|
+
const tagRef = uiTag || topicTag;
|
|
21
|
+
let tagId = config.tagId || null;
|
|
22
|
+
|
|
23
|
+
// Keep master behavior (prefer configured tagId), then fallback to UI tag/topic lookup.
|
|
24
|
+
if (!tagId && !tagRef) {
|
|
25
|
+
node.error("No tag provided: set Tag in the node OR provide msg.topic", msg);
|
|
26
|
+
return done && done();
|
|
15
27
|
}
|
|
16
|
-
|
|
28
|
+
|
|
29
|
+
if (!tagId) {
|
|
30
|
+
tagId = fuxa.getTagId(tagRef, null);
|
|
31
|
+
}
|
|
32
|
+
|
|
17
33
|
if (tagId) {
|
|
18
34
|
await fuxa.setTag(tagId, msg.payload);
|
|
19
|
-
|
|
35
|
+
send(msg);
|
|
36
|
+
return done && done();
|
|
20
37
|
} else {
|
|
21
|
-
node.error(
|
|
38
|
+
node.error("Tag not found: " + tagRef, msg);
|
|
39
|
+
return done && done();
|
|
22
40
|
}
|
|
23
41
|
} catch (err) {
|
|
24
42
|
node.error(err, msg);
|
|
43
|
+
return done && done(err);
|
|
25
44
|
}
|
|
26
45
|
});
|
|
27
46
|
}
|
|
47
|
+
|
|
28
48
|
RED.nodes.registerType("set-tag", FuxaSetTagNode);
|
|
29
|
-
}
|
|
49
|
+
};
|
package/main.js
CHANGED
|
@@ -133,6 +133,9 @@ try {
|
|
|
133
133
|
if (mysettings.language) {
|
|
134
134
|
settings.language = mysettings.language;
|
|
135
135
|
}
|
|
136
|
+
if (!utils.isNullOrUndefined(mysettings.hideEditorOnboarding)) {
|
|
137
|
+
settings.hideEditorOnboarding = mysettings.hideEditorOnboarding;
|
|
138
|
+
}
|
|
136
139
|
if (mysettings.uiPort) {
|
|
137
140
|
settings.uiPort = mysettings.uiPort;
|
|
138
141
|
}
|
package/package.json
CHANGED
|
@@ -13,6 +13,18 @@ var settings // Application settings
|
|
|
13
13
|
var logger; // Application logger
|
|
14
14
|
var db_alarms; // Database of alarms
|
|
15
15
|
|
|
16
|
+
function _run(sql, params = []) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
db_alarms.run(sql, params, function (err) {
|
|
19
|
+
if (err) {
|
|
20
|
+
reject(err);
|
|
21
|
+
} else {
|
|
22
|
+
resolve(this);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
/**
|
|
17
29
|
* Init and bind the database resource
|
|
18
30
|
* @param {*} _settings
|
|
@@ -135,33 +147,38 @@ function getAlarms() {
|
|
|
135
147
|
* Set alarm value in database
|
|
136
148
|
*/
|
|
137
149
|
function setAlarms(alarms) {
|
|
138
|
-
return new Promise(function (resolve, reject) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
150
|
+
return new Promise(async function (resolve, reject) {
|
|
151
|
+
if (!alarms || !alarms.length) {
|
|
152
|
+
resolve();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
await _run('BEGIN TRANSACTION');
|
|
157
|
+
for (const alr of alarms) {
|
|
143
158
|
let grp = alr.subproperty.group || '';
|
|
144
159
|
let status = alr.status || '';
|
|
145
160
|
let userack = alr.userack || '';
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
const alarmId = alr.getId();
|
|
162
|
+
await _run(
|
|
163
|
+
"INSERT OR REPLACE INTO alarms (nametype, type, status, ontime, offtime, acktime) VALUES(?, ?, ?, ?, ?, ?)",
|
|
164
|
+
[alarmId, alr.type, status, alr.ontime, alr.offtime, alr.acktime]
|
|
165
|
+
);
|
|
166
|
+
await _run(
|
|
167
|
+
"INSERT OR REPLACE INTO chronicle (Sn, nametype, type, status, text, grp, ontime, offtime, acktime, userack) VALUES ((SELECT Sn from chronicle WHERE ontime = ? AND nametype = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
168
|
+
[alr.ontime, alarmId, alarmId, alr.type, status, alr.subproperty.text, grp, alr.ontime, alr.offtime, alr.acktime, userack]
|
|
169
|
+
);
|
|
152
170
|
if (alr.toremove) {
|
|
153
|
-
|
|
154
|
-
sql += "DELETE FROM alarms WHERE nametype = '" + alr.getId() + "';";
|
|
171
|
+
await _run("DELETE FROM alarms WHERE nametype = ?", [alarmId]);
|
|
155
172
|
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
}
|
|
174
|
+
await _run('COMMIT');
|
|
175
|
+
resolve();
|
|
176
|
+
} catch (err) {
|
|
177
|
+
try {
|
|
178
|
+
await _run('ROLLBACK');
|
|
179
|
+
} catch (_) {}
|
|
180
|
+
logger.error('alarmsstorage.failed-to-set: ' + err);
|
|
181
|
+
reject();
|
|
165
182
|
}
|
|
166
183
|
});
|
|
167
184
|
}
|
|
@@ -172,14 +189,12 @@ function setAlarms(alarms) {
|
|
|
172
189
|
function removeAlarm(alarm) {
|
|
173
190
|
return new Promise(function (resolve, reject) {
|
|
174
191
|
// prepare query
|
|
175
|
-
var sql = "DELETE FROM alarms WHERE nametype =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
resolve();
|
|
182
|
-
}
|
|
192
|
+
var sql = "DELETE FROM alarms WHERE nametype = ?";
|
|
193
|
+
_run(sql, [alarm.getId()]).then(function () {
|
|
194
|
+
resolve();
|
|
195
|
+
}).catch(function (err) {
|
|
196
|
+
logger.error('alarmsstorage.failed-to-remove: ' + err);
|
|
197
|
+
reject();
|
|
183
198
|
});
|
|
184
199
|
});
|
|
185
200
|
}
|
|
@@ -13,6 +13,18 @@ var settings // Application settings
|
|
|
13
13
|
var logger; // Application logger
|
|
14
14
|
var db_apikeys; // Database of apikeys
|
|
15
15
|
|
|
16
|
+
function _run(sql, params = []) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
db_apikeys.run(sql, params, function (err) {
|
|
19
|
+
if (err) {
|
|
20
|
+
reject(err);
|
|
21
|
+
} else {
|
|
22
|
+
resolve(this);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
/**
|
|
17
29
|
* Init and bind the database resource
|
|
18
30
|
* @param {*} _settings
|
|
@@ -73,18 +85,18 @@ function getApiKeys() {
|
|
|
73
85
|
*/
|
|
74
86
|
function setApiKeys(apiKeys) {
|
|
75
87
|
return new Promise(async function (resolve, reject) {
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
var
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
});
|
|
88
|
+
try {
|
|
89
|
+
const sql = "INSERT OR REPLACE INTO apikeys (name, value) VALUES(?, ?)";
|
|
90
|
+
for (var i = 0; i < apiKeys.length; i++) {
|
|
91
|
+
const apiKey = apiKeys[i];
|
|
92
|
+
var value = JSON.stringify(apiKey);
|
|
93
|
+
await _run(sql, [apiKey.id, value]);
|
|
94
|
+
}
|
|
95
|
+
resolve();
|
|
96
|
+
} catch (err) {
|
|
97
|
+
logger.error(`apiKeysStorage.set apikeys failed! ${err}`);
|
|
98
|
+
reject();
|
|
86
99
|
}
|
|
87
|
-
resolve();
|
|
88
100
|
});
|
|
89
101
|
}
|
|
90
102
|
|
|
@@ -93,17 +105,17 @@ function setApiKeys(apiKeys) {
|
|
|
93
105
|
*/
|
|
94
106
|
function removeApiKeys(apiKeys) {
|
|
95
107
|
return new Promise(async function (resolve, reject) {
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
});
|
|
108
|
+
try {
|
|
109
|
+
const sql = "DELETE FROM apikeys WHERE name = ?";
|
|
110
|
+
for (var i = 0; i < apiKeys.length; i++) {
|
|
111
|
+
const apiKey = apiKeys[i];
|
|
112
|
+
await _run(sql, [apiKey.id]);
|
|
113
|
+
}
|
|
114
|
+
resolve();
|
|
115
|
+
} catch (err) {
|
|
116
|
+
logger.error(`apiKeysStorage.remove apikeys failed! ${err}`);
|
|
117
|
+
reject();
|
|
105
118
|
}
|
|
106
|
-
resolve();
|
|
107
119
|
});
|
|
108
120
|
}
|
|
109
121
|
|
|
@@ -122,4 +134,4 @@ module.exports = {
|
|
|
122
134
|
getApiKeys: getApiKeys,
|
|
123
135
|
setApiKeys: setApiKeys,
|
|
124
136
|
removeApiKeys: removeApiKeys
|
|
125
|
-
};
|
|
137
|
+
};
|