@frangoteam/fuxa-min 1.2.11 → 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/auth/index.js +141 -3
- package/api/command/index.js +1 -1
- package/api/index.js +21 -2
- package/api/jwt-helper.js +3 -1
- package/api/resources/index.js +19 -2
- package/api/scripts/index.js +7 -3
- package/dist/3rdpartylicenses.txt +139 -7
- package/dist/assets/i18n/de.json +10 -0
- package/dist/assets/i18n/en.json +21 -3
- package/dist/assets/i18n/es.json +12 -0
- package/dist/assets/i18n/fr.json +11 -0
- package/dist/assets/i18n/ja.json +16 -6
- package/dist/assets/i18n/ko.json +12 -0
- package/dist/assets/i18n/pt.json +9 -2
- package/dist/assets/i18n/ru.json +11 -0
- package/dist/assets/i18n/sv.json +11 -1
- package/dist/assets/i18n/tr.json +8 -1
- package/dist/assets/i18n/ua.json +9 -2
- package/dist/assets/i18n/zh-cn.json +11 -0
- package/dist/assets/i18n/zh-tw.json +12 -1
- package/dist/assets/lib/svgeditor/fuxa-editor.min.js +17 -23
- package/dist/index.html +2 -2
- package/dist/main.72bdfed42c527918.js +329 -0
- package/dist/polyfills.d7de05f9af2fb559.js +1 -0
- package/dist/{runtime.8ef63094e52a66ba.js → runtime.9136a61a9b98f987.js} +1 -1
- package/dist/{scripts.40b60f02658462e4.js → scripts.d9e6ee984bf6f3b7.js} +1 -1
- package/dist/styles.545e37beb3e671ba.css +1 -0
- 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-daq.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-daq.js +8 -2
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-change.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-change.js +12 -12
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-daq-settings.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-daq-settings.js +14 -10
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.html +69 -14
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.js +44 -12
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag-daq-settings.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag-daq-settings.js +24 -20
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.html +72 -13
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.js +33 -7
- package/main.js +38 -17
- package/package.json +10 -5
- package/runtime/alarms/alarmstorage.js +45 -30
- package/runtime/apikeys/apiKeysStorage.js +34 -22
- package/runtime/devices/adsclient/index.js +1 -1
- package/runtime/devices/ethernetip/index.js +1 -1
- package/runtime/devices/gpio/index.js +1 -1
- package/runtime/devices/odbc/index.js +14 -9
- package/runtime/devices/s7/index.js +2 -2
- package/runtime/devices/template/index.js +14 -14
- 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/daqstorage.js +28 -2
- package/runtime/storage/influxdb/index.js +8 -3
- package/runtime/storage/questdb/index.js +224 -0
- package/runtime/storage/sqlite/index.js +11 -8
- package/runtime/storage/tdengine/index.js +24 -9
- package/runtime/users/usrstorage.js +65 -61
- package/runtime/utils.js +5 -0
- package/settings.default.js +8 -2
- package/dist/main.92522279642ef880.js +0 -329
- package/dist/polyfills.c8e7db9850a3ad8b.js +0 -1
- package/dist/styles.03cc550382689976.css +0 -1
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
6
|
name: {value:""},
|
|
7
|
-
tag: {value:"",
|
|
7
|
+
tag: {value:""}, // Keep: tag name for display and backward compatibility
|
|
8
|
+
tagId: {value:""}, // New: unique identifier (optional, for backward compatibility)
|
|
8
9
|
enabled: {value:true},
|
|
9
10
|
interval: {value:1000},
|
|
10
11
|
deadband: {value:0}
|
|
@@ -13,17 +14,65 @@
|
|
|
13
14
|
outputs:1,
|
|
14
15
|
icon: "white-globe.png",
|
|
15
16
|
label: function() {
|
|
16
|
-
|
|
17
|
+
if (this.name) {
|
|
18
|
+
return this.name;
|
|
19
|
+
}
|
|
20
|
+
// Display tag name or tagId
|
|
21
|
+
return this.tag || this.tagId || "set tag daq settings";
|
|
17
22
|
},
|
|
18
23
|
oneditprepare: function() {
|
|
24
|
+
var node = this;
|
|
25
|
+
var tagMap = {}; // Use tag.id as key to avoid tag.name duplication issues
|
|
26
|
+
|
|
19
27
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
20
28
|
var datalist = $('#fuxa-tags-set-daq-settings');
|
|
21
29
|
datalist.empty();
|
|
30
|
+
|
|
22
31
|
data.forEach(function(device) {
|
|
23
32
|
device.tags.forEach(function(tag) {
|
|
24
|
-
|
|
33
|
+
var fullName = device.name + ' - ' + tag.name;
|
|
34
|
+
// datalist option value format: tag.name(tag.id)
|
|
35
|
+
var optionValue = tag.name + '(' + tag.id + ')';
|
|
36
|
+
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
37
|
+
tagMap[tag.id] = {
|
|
38
|
+
name: tag.name,
|
|
39
|
+
deviceName: device.name,
|
|
40
|
+
fullName: fullName
|
|
41
|
+
};
|
|
25
42
|
});
|
|
26
43
|
});
|
|
44
|
+
|
|
45
|
+
// Listen for tagSelected input changes
|
|
46
|
+
$('#node-input-tagSelected').on('change', function() {
|
|
47
|
+
var selectedValue = $(this).val();
|
|
48
|
+
// Parse format: tag.name(tag.id)
|
|
49
|
+
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
50
|
+
if (match) {
|
|
51
|
+
var tagName = match[1];
|
|
52
|
+
var tagId = match[2];
|
|
53
|
+
|
|
54
|
+
// Set the actual stored fields
|
|
55
|
+
$('#node-input-tag').val(tagName);
|
|
56
|
+
$('#node-input-tagId').val(tagId);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Initialize display (when editing existing node)
|
|
61
|
+
if (node.tagId && tagMap[node.tagId]) {
|
|
62
|
+
// Has tagId, construct tagSelected value
|
|
63
|
+
var tagName = node.tag || tagMap[node.tagId].name;
|
|
64
|
+
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
65
|
+
} else if (node.tag && !node.tagId) {
|
|
66
|
+
// Old node: only has tag (tag.name), need to find corresponding tagId
|
|
67
|
+
// Note: if there are duplicate names, only the first match will be found
|
|
68
|
+
for (var tagId in tagMap) {
|
|
69
|
+
if (tagMap[tagId].name === node.tag) {
|
|
70
|
+
$('#node-input-tagSelected').val(node.tag + '(' + tagId + ')');
|
|
71
|
+
$('#node-input-tagId').val(tagId);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
27
76
|
});
|
|
28
77
|
}
|
|
29
78
|
});
|
|
@@ -35,10 +84,12 @@
|
|
|
35
84
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
36
85
|
</div>
|
|
37
86
|
<div class="form-row">
|
|
38
|
-
<label for="node-input-
|
|
39
|
-
<input type="text" id="node-input-
|
|
87
|
+
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
88
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags-set-daq-settings" placeholder="Select Tag">
|
|
40
89
|
<datalist id="fuxa-tags-set-daq-settings"></datalist>
|
|
41
90
|
</div>
|
|
91
|
+
<input type="hidden" id="node-input-tag">
|
|
92
|
+
<input type="hidden" id="node-input-tagId">
|
|
42
93
|
<div class="form-row">
|
|
43
94
|
<label for="node-input-enabled"> </label>
|
|
44
95
|
<input type="checkbox" id="node-input-enabled" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
@@ -6,29 +6,33 @@ module.exports = function(RED) {
|
|
|
6
6
|
|
|
7
7
|
this.on('input', async function(msg) {
|
|
8
8
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
// Prefer config.tagId, fallback to config.tag or msg.tag for backward compatibility
|
|
10
|
+
var tagId = config.tagId;
|
|
11
|
+
if (!tagId) {
|
|
12
|
+
var tagName = config.tag || msg.tag;
|
|
13
|
+
if (tagName) {
|
|
14
|
+
// Backward compatibility: old nodes use tag.name, need to convert to tagId
|
|
15
|
+
tagId = fuxa.getTagId(tagName, null);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (tagId) {
|
|
20
|
+
var settings = {
|
|
21
|
+
enabled: config.enabled,
|
|
22
|
+
interval: config.interval,
|
|
23
|
+
deadband: config.deadband
|
|
24
|
+
};
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
// Override with message properties if provided
|
|
27
|
+
if (msg.enabled !== undefined) settings.enabled = msg.enabled;
|
|
28
|
+
if (msg.interval !== undefined) settings.interval = msg.interval;
|
|
29
|
+
if (msg.deadband !== undefined) settings.deadband = msg.deadband;
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} else {
|
|
28
|
-
node.error('Tag not found: ' + tagName, msg);
|
|
29
|
-
}
|
|
31
|
+
var result = await fuxa.setTagDaqSettings(tagId, settings);
|
|
32
|
+
msg.payload = result;
|
|
33
|
+
node.send(msg);
|
|
30
34
|
} else {
|
|
31
|
-
node.error('Tag
|
|
35
|
+
node.error('Tag not found: ' + (config.tag || msg.tag || config.tagId), msg);
|
|
32
36
|
}
|
|
33
37
|
} catch (err) {
|
|
34
38
|
node.error(err, msg);
|
|
@@ -1,27 +1,69 @@
|
|
|
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:"",
|
|
6
|
+
name: {value: ""},
|
|
7
|
+
tag: {value: ""},
|
|
8
|
+
tagId: {value: ""}
|
|
8
9
|
},
|
|
9
|
-
inputs:1,
|
|
10
|
-
outputs:1,
|
|
10
|
+
inputs: 1,
|
|
11
|
+
outputs: 1,
|
|
11
12
|
icon: "white-globe.png",
|
|
12
13
|
label: function() {
|
|
13
|
-
|
|
14
|
+
if (this.name) {
|
|
15
|
+
return this.name;
|
|
16
|
+
}
|
|
17
|
+
return this.tag || this.tagId || "set tag";
|
|
14
18
|
},
|
|
15
19
|
oneditprepare: function() {
|
|
20
|
+
var node = this;
|
|
21
|
+
var tagMap = {};
|
|
22
|
+
|
|
16
23
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
17
24
|
var datalist = $('#fuxa-tags-set');
|
|
18
25
|
datalist.empty();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
|
|
27
|
+
(data || []).forEach(function(device) {
|
|
28
|
+
(device.tags || []).forEach(function(tag) {
|
|
29
|
+
var fullName = device.name + ' - ' + tag.name;
|
|
30
|
+
var optionValue = tag.name + '(' + tag.id + ')';
|
|
31
|
+
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
32
|
+
tagMap[tag.id] = {
|
|
33
|
+
name: tag.name,
|
|
34
|
+
deviceName: device.name,
|
|
35
|
+
fullName: fullName
|
|
36
|
+
};
|
|
22
37
|
});
|
|
23
38
|
});
|
|
39
|
+
|
|
40
|
+
$('#node-input-tagSelected').on('change', function() {
|
|
41
|
+
var selectedValue = $(this).val();
|
|
42
|
+
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
43
|
+
if (match) {
|
|
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('');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (node.tagId && tagMap[node.tagId]) {
|
|
53
|
+
var tagName = node.tag || tagMap[node.tagId].name;
|
|
54
|
+
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
55
|
+
} else if (node.tag && !node.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);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
24
64
|
});
|
|
65
|
+
|
|
66
|
+
$('#node-input-tagSelected').attr('placeholder', 'Optional - leave empty to use msg.topic');
|
|
25
67
|
}
|
|
26
68
|
});
|
|
27
69
|
</script>
|
|
@@ -31,13 +73,30 @@
|
|
|
31
73
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
32
74
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
33
75
|
</div>
|
|
76
|
+
|
|
34
77
|
<div class="form-row">
|
|
35
|
-
<label for="node-input-
|
|
36
|
-
<input type="text" id="node-input-
|
|
78
|
+
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
79
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags-set" placeholder="Optional - leave empty to use msg.topic">
|
|
37
80
|
<datalist id="fuxa-tags-set"></datalist>
|
|
38
81
|
</div>
|
|
82
|
+
<input type="hidden" id="node-input-tag">
|
|
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>
|
|
39
90
|
</script>
|
|
40
91
|
|
|
41
92
|
<script type="text/x-red" data-help-name="set-tag">
|
|
42
|
-
<p>
|
|
43
|
-
|
|
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,23 +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
|
-
|
|
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();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!tagId) {
|
|
30
|
+
tagId = fuxa.getTagId(tagRef, null);
|
|
31
|
+
}
|
|
32
|
+
|
|
11
33
|
if (tagId) {
|
|
12
34
|
await fuxa.setTag(tagId, msg.payload);
|
|
13
|
-
|
|
35
|
+
send(msg);
|
|
36
|
+
return done && done();
|
|
14
37
|
} else {
|
|
15
|
-
node.error(
|
|
38
|
+
node.error("Tag not found: " + tagRef, msg);
|
|
39
|
+
return done && done();
|
|
16
40
|
}
|
|
17
41
|
} catch (err) {
|
|
18
42
|
node.error(err, msg);
|
|
43
|
+
return done && done(err);
|
|
19
44
|
}
|
|
20
45
|
});
|
|
21
46
|
}
|
|
47
|
+
|
|
22
48
|
RED.nodes.registerType("set-tag", FuxaSetTagNode);
|
|
23
|
-
}
|
|
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
|
}
|
|
@@ -148,6 +151,12 @@ try {
|
|
|
148
151
|
if (mysettings.tokenExpiresIn) {
|
|
149
152
|
settings.tokenExpiresIn = mysettings.tokenExpiresIn;
|
|
150
153
|
}
|
|
154
|
+
if (!utils.isNullOrUndefined(mysettings.enableRefreshCookieAuth)) {
|
|
155
|
+
settings.enableRefreshCookieAuth = mysettings.enableRefreshCookieAuth;
|
|
156
|
+
}
|
|
157
|
+
if (mysettings.refreshTokenExpiresIn) {
|
|
158
|
+
settings.refreshTokenExpiresIn = mysettings.refreshTokenExpiresIn;
|
|
159
|
+
}
|
|
151
160
|
if (mysettings.secretCode) {
|
|
152
161
|
settings.secretCode = mysettings.secretCode;
|
|
153
162
|
}
|
|
@@ -189,6 +198,12 @@ try {
|
|
|
189
198
|
logger.error('Error loading user settings file: ' + userSettingsFile)
|
|
190
199
|
}
|
|
191
200
|
|
|
201
|
+
// Ensure secure mode never runs with an empty/static-known JWT secret.
|
|
202
|
+
if (settings.secureEnabled && !settings.secretCode) {
|
|
203
|
+
settings.secretCode = utils.generateSecretCode();
|
|
204
|
+
logger.warn('Generated a random JWT secret in memory because secureEnabled=true and secretCode was missing. Persist it in settings for stable sessions across restarts.');
|
|
205
|
+
}
|
|
206
|
+
|
|
192
207
|
// Check logger
|
|
193
208
|
if (!settings.logDir) {
|
|
194
209
|
settings.logDir = path.resolve(rootDir, '_logs');
|
|
@@ -260,6 +275,7 @@ var www = path.resolve(__dirname, '../client/dist');
|
|
|
260
275
|
if (!fs.existsSync(www)) { // compatibility with docker/npm/electron
|
|
261
276
|
www = path.resolve(__dirname, './dist');
|
|
262
277
|
}
|
|
278
|
+
|
|
263
279
|
settings.httpStatic = settings.httpStatic || www;
|
|
264
280
|
|
|
265
281
|
if (parsedArgs.port !== undefined) {
|
|
@@ -321,10 +337,13 @@ const allowCrossDomain = function (req, res, next) {
|
|
|
321
337
|
|
|
322
338
|
if (isOriginAllowed(origin)) {
|
|
323
339
|
res.header('Access-Control-Allow-Origin', origin || '*');
|
|
340
|
+
if (settings.enableRefreshCookieAuth) {
|
|
341
|
+
res.header('Access-Control-Allow-Credentials', 'true');
|
|
342
|
+
}
|
|
324
343
|
}
|
|
325
344
|
|
|
326
345
|
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
|
327
|
-
res.header('Access-Control-Allow-Headers', 'x-access-token, x-auth-user, Origin, Content-Type, Accept');
|
|
346
|
+
res.header('Access-Control-Allow-Headers', 'x-access-token, x-auth-user, Origin, Content-Type, Accept, Skip-Auth, Skip-Error');
|
|
328
347
|
|
|
329
348
|
|
|
330
349
|
if (req.method === 'OPTIONS') {
|
|
@@ -348,22 +367,24 @@ app.use('/_widgets', express.static(settings.widgetsFileDir));
|
|
|
348
367
|
app.use('/snapshots', express.static(settings.webcamSnapShotsDir))
|
|
349
368
|
|
|
350
369
|
var accessLogStream = fs.createWriteStream(settings.logDir + '/api.log', { flags: 'a' });
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
370
|
+
if (runtime.settings.logApiLevel !== 'none') {
|
|
371
|
+
app.use(morgan('combined', {
|
|
372
|
+
stream: accessLogStream,
|
|
373
|
+
skip: function (req, res) { return res.statusCode < 400 }
|
|
374
|
+
}));
|
|
375
|
+
|
|
376
|
+
app.use(morgan('dev', {
|
|
377
|
+
skip: function (req, res) {
|
|
378
|
+
return res.statusCode < 400
|
|
379
|
+
}, stream: process.stderr
|
|
380
|
+
}));
|
|
381
|
+
|
|
382
|
+
app.use(morgan('dev', {
|
|
383
|
+
skip: function (req, res) {
|
|
384
|
+
return res.statusCode >= 400
|
|
385
|
+
}, stream: process.stdout
|
|
386
|
+
}));
|
|
387
|
+
}
|
|
367
388
|
|
|
368
389
|
function mountSwaggerIfEnabled() {
|
|
369
390
|
const swaggerEnabled = settings.swagger || settings.swaggerEnabled;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frangoteam/fuxa-min",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Web-based Process Visualization (SCADA/HMI/Dashboard) software",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,12 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@influxdata/influxdb-client": "1.25.0",
|
|
39
|
+
"@questdb/nodejs-client": "3.0.0",
|
|
39
40
|
"@tdengine/rest": "3.0.2",
|
|
40
41
|
"ads-client": "2.1.0",
|
|
41
42
|
"app-root-path": "2.2.1",
|
|
42
43
|
"async": "3.2.3",
|
|
43
44
|
"async-mutex": "0.5.0",
|
|
44
|
-
"axios": "
|
|
45
|
+
"axios": "1.13.5",
|
|
45
46
|
"bcryptjs": "2.4.3",
|
|
46
47
|
"bluebird": "3.7.2",
|
|
47
48
|
"body-parser": "1.20.3",
|
|
@@ -52,19 +53,20 @@
|
|
|
52
53
|
"fontkit": "^2.0.2",
|
|
53
54
|
"fs-extra": "7.0.1",
|
|
54
55
|
"influx": "5.9.3",
|
|
55
|
-
"ip": "2.0.1",
|
|
56
|
+
"ip": "~2.0.1",
|
|
56
57
|
"jsonwebtoken": "9.0.0",
|
|
57
58
|
"live-plugin-manager": "0.15.1",
|
|
58
59
|
"modbus-serial": "8.0.19",
|
|
59
|
-
"morgan": "1.10.
|
|
60
|
+
"morgan": "1.10.1",
|
|
60
61
|
"mqtt": "4.3.7",
|
|
61
62
|
"node-bacnet": "0.2.4",
|
|
62
63
|
"node-opcua": "2.149.0",
|
|
63
64
|
"node-red": "^4.1.0",
|
|
64
65
|
"node-schedule": "2.1.1",
|
|
65
|
-
"nodemailer": "7.0.
|
|
66
|
+
"nodemailer": "7.0.13",
|
|
66
67
|
"nopt": "5.0.0",
|
|
67
68
|
"pdfmake": "0.2.5",
|
|
69
|
+
"pg": "8.18.0",
|
|
68
70
|
"socket.io": "4.8.1",
|
|
69
71
|
"sqlite3": "5.1.5",
|
|
70
72
|
"swagger-ui-express": "^5.0.1",
|
|
@@ -72,6 +74,9 @@
|
|
|
72
74
|
"ws": "8.18.0",
|
|
73
75
|
"yamljs": "^0.3.0"
|
|
74
76
|
},
|
|
77
|
+
"overrides": {
|
|
78
|
+
"tar": "^7.4.3"
|
|
79
|
+
},
|
|
75
80
|
"devDependencies": {
|
|
76
81
|
"@mapbox/node-pre-gyp": "^1.0.0",
|
|
77
82
|
"@types/express": "5.0.0",
|
|
@@ -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
|
}
|