@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.
Files changed (35) hide show
  1. package/api/command/index.js +1 -1
  2. package/api/index.js +3 -0
  3. package/api/scripts/index.js +7 -3
  4. package/dist/assets/i18n/en.json +10 -3
  5. package/dist/assets/i18n/fr.json +2 -1
  6. package/dist/assets/i18n/ja.json +2 -1
  7. package/dist/assets/i18n/sv.json +2 -1
  8. package/dist/assets/i18n/zh-cn.json +2 -1
  9. package/dist/assets/i18n/zh-tw.json +2 -1
  10. package/dist/assets/lib/svgeditor/fuxa-editor.min.js +17 -23
  11. package/dist/index.html +1 -1
  12. package/dist/{main.bafae830903d548e.js → main.72bdfed42c527918.js} +6 -6
  13. package/docs/openapi.yaml +3 -0
  14. package/integrations/node-red/index.js +4 -5
  15. package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.html +39 -35
  16. package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.js +43 -17
  17. package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.html +42 -34
  18. package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.js +32 -12
  19. package/main.js +3 -0
  20. package/package.json +1 -1
  21. package/runtime/alarms/alarmstorage.js +45 -30
  22. package/runtime/apikeys/apiKeysStorage.js +34 -22
  23. package/runtime/devices/odbc/index.js +9 -4
  24. package/runtime/devices/s7/index.js +2 -2
  25. package/runtime/notificator/notifystorage.js +34 -23
  26. package/runtime/project/index.js +16 -0
  27. package/runtime/project/prjstorage.js +78 -40
  28. package/runtime/scripts/index.js +6 -2
  29. package/runtime/storage/calculator.js +17 -13
  30. package/runtime/storage/influxdb/index.js +8 -3
  31. package/runtime/storage/questdb/index.js +1 -1
  32. package/runtime/storage/sqlite/index.js +11 -8
  33. package/runtime/storage/tdengine/index.js +24 -9
  34. package/runtime/users/usrstorage.js +65 -61
  35. package/settings.default.js +3 -0
@@ -263,12 +263,12 @@ function ODBCclient(_data, _logger, _events) {
263
263
  * Return tables list
264
264
  */
265
265
  function getTables(endpoint, fncGetProperty, packagerManager) {
266
- return new Promise( async function (resolve, reject) {
267
- if (loadOdbcLib(packagerManager)) {
266
+ return new Promise(async function (resolve, reject) {
267
+ if (loadOdbcLib(packagerManager)) {
268
268
  var connection;
269
269
  try {
270
270
  var security
271
- await fncGetProperty({query: 'security', name: endpoint.id}).then((result, error) => {
271
+ await fncGetProperty({ query: 'security', name: endpoint.id }).then((result, error) => {
272
272
  if (result) {
273
273
  security = utils.JsonTryToParse(result.value);
274
274
  }
@@ -286,7 +286,12 @@ function getTables(endpoint, fncGetProperty, packagerManager) {
286
286
  loginTimeout: 10,
287
287
  }
288
288
  connection = await odbc.connect(connectionConfig)
289
- var tables = await connection.tables(null, 'dbo', null, null);
289
+ var tables = [];
290
+
291
+ try {
292
+ tables = await connection.tables(null, 'dbo', null, null);
293
+ } catch (err) { }
294
+
290
295
  if (tables.length <= 0) {
291
296
  tables = await connection.tables(null, null, null, null);
292
297
  }
@@ -348,7 +348,7 @@ function S7client(_data, _logger, _events, _runtime) {
348
348
  type: items[itemidx].type,
349
349
  daq: items[itemidx].daq,
350
350
  changed: changed,
351
- tagref: items[itemidx]
351
+ tagref: data.tags[items[itemidx].id] // <--- Passes full tag config including scaling
352
352
  };
353
353
  someval = true;
354
354
  }
@@ -701,4 +701,4 @@ function DbItems(dbnum) {
701
701
  this.DBNumber = dbnum;
702
702
  this.MaxSize = 0;
703
703
  this.Items = {};
704
- }
704
+ }
@@ -13,6 +13,18 @@ var settings // Application settings
13
13
  var logger; // Application logger
14
14
  var db_notifications; // Database of notifications
15
15
 
16
+ function _run(sql, params = []) {
17
+ return new Promise((resolve, reject) => {
18
+ db_notifications.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
@@ -120,23 +132,24 @@ function getNotifications() {
120
132
  */
121
133
  function setNotification(notification) {
122
134
  return new Promise(function (resolve, reject) {
123
- // prepare query
124
135
  if (notification) {
125
- var sql = "";
126
- //is notification is changed insert or update record
127
- // sql += "INSERT OR REPLACE INTO notifications (id, name, type, ontime, notifytime, notifytype) VALUES('" + notification.id + "','" +
128
- // notification.type + "','" + notification.name + "','" + notification.ontime + "','" + notification.notifytime + "','" + notification.notifytype + "');";
129
- sql += "INSERT OR REPLACE INTO chronicle (id, name, type, receiver, text, notifytime, notifytype) VALUES('" +
130
- notification.id + "','" + notification.name + "','" + notification.type + "','" + notification.receiver + "','" +
131
- notification.text + "','" + notification.notifytime + "','" + notification.notifytype + "');";
132
- db_notifications.exec(sql, function (err) {
133
- if (err) {
134
- logger.error('notifystorage.failed-to-set: ' + err);
135
- reject();
136
- } else {
137
- resolve();
138
- }
136
+ const sql = "INSERT OR REPLACE INTO chronicle (id, name, type, receiver, text, notifytime, notifytype) VALUES(?, ?, ?, ?, ?, ?, ?)";
137
+ _run(sql, [
138
+ notification.id,
139
+ notification.name,
140
+ notification.type,
141
+ notification.receiver,
142
+ notification.text,
143
+ notification.notifytime,
144
+ notification.notifytype
145
+ ]).then(function () {
146
+ resolve();
147
+ }).catch(function (err) {
148
+ logger.error('notifystorage.failed-to-set: ' + err);
149
+ reject();
139
150
  });
151
+ } else {
152
+ resolve();
140
153
  }
141
154
  });
142
155
  }
@@ -156,14 +169,12 @@ function close() {
156
169
  function removeNotification(notification) {
157
170
  return new Promise(function (resolve, reject) {
158
171
  // prepare query
159
- var sql = "DELETE FROM notifications WHERE id = '" + notification.id + "'";
160
- db_notifications.exec(sql, function (err) {
161
- if (err) {
162
- logger.error('notificationsstorage.failed-to-remove: ' + err);
163
- reject();
164
- } else {
165
- resolve();
166
- }
172
+ var sql = "DELETE FROM notifications WHERE id = ?";
173
+ _run(sql, [notification.id]).then(function () {
174
+ resolve();
175
+ }).catch(function (err) {
176
+ logger.error('notificationsstorage.failed-to-remove: ' + err);
177
+ reject();
167
178
  });
168
179
  });
169
180
  }
@@ -928,6 +928,22 @@ function _filterProjectPermission(userPermission) {
928
928
  // from device remove the not used (no permission)
929
929
  // delete result.devices;
930
930
  delete result.server;
931
+ if (Array.isArray(result.scripts)) {
932
+ // Keep only scripts authorised for the current user. For authorised
933
+ // server-side scripts, retain just the metadata needed for event
934
+ // bindings and execution requests; do not expose source code.
935
+ result.scripts = result.scripts.filter(script => {
936
+ return script && runtime.scriptsMgr.isAuthorised(script, userPermission);
937
+ }).map(script => {
938
+ delete script.permission;
939
+ delete script.permissionRoles;
940
+ if (script.mode === 'CLIENT') {
941
+ return script;
942
+ }
943
+ delete script.code;
944
+ return script;
945
+ });
946
+ }
931
947
  // check navigation permission
932
948
  if (result.hmi.layout && result.hmi.layout.navigation.items) {
933
949
  for (var i = result.hmi.layout.navigation.items.length - 1; i >= 0; i--) {
@@ -13,6 +13,38 @@ var settings // Application settings
13
13
  var logger; // Application logger
14
14
  var db_prj; // Database of project
15
15
 
16
+ function _run(sql, params = []) {
17
+ return new Promise((resolve, reject) => {
18
+ db_prj.run(sql, params, function (err) {
19
+ if (err) {
20
+ reject(err);
21
+ } else {
22
+ resolve(this);
23
+ }
24
+ });
25
+ });
26
+ }
27
+
28
+ function _all(sql, params = []) {
29
+ return new Promise((resolve, reject) => {
30
+ db_prj.all(sql, params, function (err, rows) {
31
+ if (err) {
32
+ reject(err);
33
+ } else {
34
+ resolve(rows);
35
+ }
36
+ });
37
+ });
38
+ }
39
+
40
+ function _ensureValidTable(table) {
41
+ const tables = Object.values(TableType);
42
+ if (!tables.includes(table)) {
43
+ throw new Error(`invalid table '${table}'`);
44
+ }
45
+ return table;
46
+ }
47
+
16
48
  /**
17
49
  * Init and bind the database resource
18
50
  * @param {*} _settings
@@ -83,21 +115,23 @@ function setDefault() {
83
115
  * @param {*} sections
84
116
  */
85
117
  function setSections(sections) {
86
- return new Promise(function (resolve, reject) {
87
- // prepare query
88
- var sql = "";
89
- for(var i = 0; i < sections.length; i++) {
90
- var value = JSON.stringify(sections[i].value).replace(/\'/g,"''");
91
- sql += "INSERT OR REPLACE INTO " + sections[i].table + " (name, value) VALUES('" + sections[i].name + "','"+ value + "');";
92
- }
93
- db_prj.exec(sql, function (err) {
94
- if (err) {
95
- logger.error(`prjstorage.set failed! ${err}`);
96
- reject();
97
- } else {
98
- resolve();
118
+ return new Promise(async function (resolve, reject) {
119
+ try {
120
+ await _run('BEGIN TRANSACTION');
121
+ for (var i = 0; i < sections.length; i++) {
122
+ var table = _ensureValidTable(sections[i].table);
123
+ var value = JSON.stringify(sections[i].value);
124
+ await _run(`INSERT OR REPLACE INTO ${table} (name, value) VALUES(?, ?)`, [sections[i].name, value]);
99
125
  }
100
- });
126
+ await _run('COMMIT');
127
+ resolve();
128
+ } catch (err) {
129
+ try {
130
+ await _run('ROLLBACK');
131
+ } catch (_) {}
132
+ logger.error(`prjstorage.set failed! ${err}`);
133
+ reject();
134
+ }
101
135
  });
102
136
  }
103
137
 
@@ -108,16 +142,19 @@ function setSections(sections) {
108
142
  */
109
143
  function setSection(section) {
110
144
  return new Promise(function (resolve, reject) {
111
- var value = JSON.stringify(section.value).replace(/\'/g,"''");
112
- var sql = "INSERT OR REPLACE INTO " + section.table + " (name, value) VALUES('" + section.name + "','"+ value + "');";
113
- db_prj.exec(sql, function (err) {
114
- if (err) {
145
+ try {
146
+ var table = _ensureValidTable(section.table);
147
+ var value = JSON.stringify(section.value);
148
+ _run(`INSERT OR REPLACE INTO ${table} (name, value) VALUES(?, ?)`, [section.name, value]).then(function () {
149
+ resolve();
150
+ }).catch(function (err) {
115
151
  logger.error(`prjstorage.set failed! ${err}`);
116
152
  reject();
117
- } else {
118
- resolve();
119
- }
120
- });
153
+ });
154
+ } catch (err) {
155
+ logger.error(`prjstorage.set failed! ${err}`);
156
+ reject();
157
+ }
121
158
  });
122
159
  }
123
160
 
@@ -129,17 +166,18 @@ function setSection(section) {
129
166
  */
130
167
  function getSection(table, name) {
131
168
  return new Promise(function (resolve, reject) {
132
- var sql = "SELECT name, value FROM " + table;
133
- if (name) {
134
- sql += " WHERE name = '" + name + "'";
135
- }
136
- db_prj.all(sql, function (err, rows) {
137
- if (err) {
138
- reject(err);
139
- } else {
140
- resolve(rows);
169
+ try {
170
+ var safeTable = _ensureValidTable(table);
171
+ var sql = `SELECT name, value FROM ${safeTable}`;
172
+ var params = [];
173
+ if (name) {
174
+ sql += " WHERE name = ?";
175
+ params.push(name);
141
176
  }
142
- });
177
+ _all(sql, params).then(resolve).catch(reject);
178
+ } catch (err) {
179
+ reject(err);
180
+ }
143
181
  });
144
182
  }
145
183
 
@@ -150,14 +188,14 @@ function getSection(table, name) {
150
188
  */
151
189
  function deleteSection(section) {
152
190
  return new Promise(function (resolve, reject) {
153
- var sql = "DELETE FROM " + section.table + " WHERE name = '" + section.name + "'";
154
- db_prj.run(sql, function (err, rows) {
155
- if (err) {
156
- reject(err);
157
- } else {
191
+ try {
192
+ var table = _ensureValidTable(section.table);
193
+ _run(`DELETE FROM ${table} WHERE name = ?`, [section.name]).then(function () {
158
194
  resolve();
159
- }
160
- });
195
+ }).catch(reject);
196
+ } catch (err) {
197
+ reject(err);
198
+ }
161
199
  });
162
200
  }
163
201
 
@@ -222,4 +260,4 @@ module.exports = {
222
260
  deleteSection: deleteSection,
223
261
  setDefault: setDefault,
224
262
  TableType: TableType,
225
- };
263
+ };
@@ -88,6 +88,10 @@ function ScriptsManager(_runtime) {
88
88
  const st = scriptModule.getScript(_script);
89
89
  var admin = (permission === -1 || permission === 255) ? true : false;
90
90
  if (runtime.settings.userRole) {
91
+ admin = admin || permission?.groups === -1 || permission?.groups === 255;
92
+ if (admin) {
93
+ return true;
94
+ }
91
95
  if (!st.permissionRoles || !st.permissionRoles.enabled) {
92
96
  return true;
93
97
  }
@@ -106,7 +110,7 @@ function ScriptsManager(_runtime) {
106
110
  this.isAuthorisedByScriptName = function (scriptName, permission) {
107
111
  const script = scriptModule.getScriptByName(scriptName);
108
112
  if (!script) {
109
- return true;
113
+ return false;
110
114
  }
111
115
  return this.isAuthorised(script, permission);
112
116
  }
@@ -366,4 +370,4 @@ const ScriptSchedulingMode = {
366
370
  const SchedulerType = {
367
371
  weekly: 0,
368
372
  date: 1,
369
- }
373
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Calculator for DAQ value array (integrale, max, min, average...)
2
+ * Calculator for DAQ value array (integrale, max, min, average...)
3
3
  */
4
4
 
5
5
  'use strict';
@@ -19,11 +19,11 @@ function getFunctionValues(values, fromts, tots, fnc, interval, format) {
19
19
  }
20
20
 
21
21
  /**
22
- *
23
- * @param {*} values
24
- * @param {*} fnc
25
- * @param {*} intervalType hour / day
26
- * @returns
22
+ *
23
+ * @param {*} values
24
+ * @param {*} fnc
25
+ * @param {*} intervalType hour / day
26
+ * @returns
27
27
  */
28
28
  function getMin(timeserie, fromts, tots, intervalType) {
29
29
  let result = getInterval(fromts, tots, intervalType, Number.MAX_VALUE);
@@ -39,7 +39,7 @@ function getMin(timeserie, fromts, tots, intervalType) {
39
39
  intervals[intervalIndex] = value;
40
40
  }
41
41
  }
42
-
42
+
43
43
  for (let i = 0; i < sorted.length; i++) {
44
44
  let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
45
45
  addToInterval(result, intervalIndex, utils.parseFloat(sorted[i].value, 5));
@@ -61,7 +61,7 @@ function getMax(timeserie, fromts, tots, intervalType) {
61
61
  intervals[intervalIndex] = value;
62
62
  }
63
63
  }
64
-
64
+
65
65
  for (let i = 0; i < sorted.length; i++) {
66
66
  let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
67
67
  addToInterval(result, intervalIndex, utils.parseFloat(sorted[i].value, 5));
@@ -85,7 +85,7 @@ function getAverage(timeserie, fromts, tots, intervalType, format) {
85
85
  }
86
86
  counters[intervalIndex]++;
87
87
  }
88
-
88
+
89
89
  for (let i = 0; i < sorted.length; i++) {
90
90
  let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
91
91
  addToInterval(result, counts, intervalIndex, parseFloat(sorted[i].value));
@@ -96,7 +96,7 @@ function getAverage(timeserie, fromts, tots, intervalType, format) {
96
96
  result[k] = utils.parseFloat(result[k] / counts[k], format || 5);
97
97
  }
98
98
  });
99
- return result;
99
+ return result;
100
100
  }
101
101
 
102
102
  function getSum(timeserie, fromts, tots, intervalType) {
@@ -114,12 +114,12 @@ function getSum(timeserie, fromts, tots, intervalType) {
114
114
  intervals[intervalIndex] = utils.parseFloat(intervals[intervalIndex], 5)
115
115
  }
116
116
  }
117
-
117
+
118
118
  for (let i = 0; i < sorted.length; i++) {
119
119
  let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
120
120
  addToInterval(result, intervalIndex, parseFloat(sorted[i].value));
121
121
  }
122
- return result;
122
+ return result;
123
123
  }
124
124
 
125
125
 
@@ -137,7 +137,7 @@ function getIntegral(timeserie, fromts, tots, intervalType) {
137
137
  intervals[intervalIndex] += value;
138
138
  }
139
139
  }
140
-
140
+
141
141
  let lastRecord = null;// : TimeValue { dt: number, value: number };
142
142
  let lastIntervalIndex = null;
143
143
  for (let i = 0; i < sorted.length; i++) {
@@ -191,6 +191,10 @@ function getStepDate(ts, type, toadd) {
191
191
  dtRange = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), 0, 0);
192
192
  var minutesRange = parseInt(dt.getMinutes() / 30);
193
193
  dtRange.setMinutes((minutesRange + toadd) * 30);
194
+ } else if (type === ReportIntervalType.min15) {
195
+ dtRange = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), 0, 0);
196
+ var minutesRange = parseInt(dt.getMinutes() / 15);
197
+ dtRange.setMinutes((minutesRange + toadd) * 15);
194
198
  } else if (type === ReportIntervalType.min10) {
195
199
  dtRange = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), 0, 0);
196
200
  var minutesRange = parseInt(dt.getMinutes() / 10);
@@ -12,6 +12,10 @@ var { InfluxDB, Point, flux } = require('@influxdata/influxdb-client');
12
12
  const VERSION_18_FLUX = '1.8-flux';
13
13
  const VERSION_20 = '2.0';
14
14
 
15
+ function escapeInfluxIdentifier(value) {
16
+ return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
17
+ }
18
+
15
19
  function Influx(_settings, _log, _currentStorate) {
16
20
 
17
21
  var settings = _settings; // Application settings
@@ -157,7 +161,8 @@ function Influx(_settings, _log, _currentStorate) {
157
161
  if (influxdbVersion === VERSION_18_FLUX) {
158
162
  fromts *= 1000000;
159
163
  tots *= 1000000;
160
- const query = `SELECT * FROM "${tagid}" WHERE time >= ${fromts} AND time <= ${tots}`;
164
+ const measurement = escapeInfluxIdentifier(tagid);
165
+ const query = `SELECT * FROM "${measurement}" WHERE time >= ${Number(fromts)} AND time <= ${Number(tots)}`;
161
166
  client.query(query).then((result) => {
162
167
  resolve(result.map(row => {
163
168
  return {
@@ -171,7 +176,7 @@ function Influx(_settings, _log, _currentStorate) {
171
176
  reject(error);
172
177
  });
173
178
  } else {
174
- const query = flux`from(bucket: "${settings.daqstore.bucket}") |> range(start: ${new Date(fromts)}, stop: ${new Date(tots)}) |> filter(fn: (r) => r.id == "${tagid}")`;
179
+ const query = flux`from(bucket: ${settings.daqstore.bucket}) |> range(start: ${new Date(fromts)}, stop: ${new Date(tots)}) |> filter(fn: (r) => r.id == ${String(tagid)})`;
175
180
  var result = [];
176
181
  queryApi.queryRows(query, {
177
182
  next(row, tableMeta) {
@@ -234,4 +239,4 @@ module.exports = {
234
239
  var InfluxDBStatusEnum = {
235
240
  OPEN: 'open',
236
241
  CLOSE: 'close',
237
- }
242
+ }
@@ -71,7 +71,7 @@ function QuestDB(_settings, _log, _currentStorage) {
71
71
  .symbol('device_id', row.deviceId)
72
72
  .stringColumn('device_name', row.deviceName);
73
73
  if (!utils.isNullOrUndefined(row.unsPath)) {
74
- line = line.stringColumn('uns_path', row.unsPath);
74
+ line = line.symbol('uns_path', row.unsPath);
75
75
  }
76
76
 
77
77
  if (!utils.isNullOrUndefined(parsedValue.numberValue)) {
@@ -227,14 +227,14 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
227
227
  _checkDataWorking(false);
228
228
  });
229
229
  }
230
- // check function to add daq data
230
+ // check function to add daq data
231
231
  if (addDaqfnc.length > 0) {
232
232
  if (_checkDataWorking(true)) {
233
233
  Promise.all(addDaqfnc).then(result => {
234
234
  // check db tokenizer after inserted
235
235
  var lastts = 0 || result[0];
236
236
  if (daqNextToken && daqNextToken < lastts) {
237
- // close data DB, open and bind the new db, rename and move the closed db
237
+ // close data DB, open and bind the new db, rename and move the closed db
238
238
  db_daqdata.close(function () {
239
239
  var suffix = _getDateTimeSuffix(new Date());
240
240
  var oldfile = db_daqdata_file;
@@ -262,6 +262,9 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
262
262
  }
263
263
  _checkDataWorking(false);
264
264
  });
265
+ } else {
266
+ // overload: attach no-op catch handlers to prevent UnhandledPromiseRejection
267
+ addDaqfnc.forEach(function (p) { p.catch(function () {}); });
265
268
  }
266
269
  }
267
270
  if (dataToRestore.length && currentStorage) {
@@ -554,8 +557,8 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
554
557
 
555
558
  function _insertTagToMap(tagid, name, type) {
556
559
  return new Promise(function (resolve, reject) {
557
- var sqlRequest = "INSERT INTO data (id, name, type) ";
558
- db_daqmap.run(sqlRequest + "VALUES('" + tagid + "','" + name + "','" + type + "'); SELECT last_insert_rowid() FROM data", function (err) {
560
+ var sqlRequest = "INSERT INTO data (id, name, type) VALUES (?, ?, ?)";
561
+ db_daqmap.run(sqlRequest, [tagid, name, type], function (err) {
559
562
  if (err !== null) {
560
563
  reject(err);
561
564
  } else {
@@ -615,10 +618,10 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
615
618
  function _insertTagValue(mapid, value) {
616
619
  return new Promise(function (resolve, reject) {
617
620
  var ts = new Date().getTime();
618
- var sqlRequest = "INSERT INTO data (dt, id, value) ";
619
- db_daqdata.run(sqlRequest + "VALUES('" + ts + "','" + mapid + "','" + value + "')", function (err) {
621
+ var sqlRequest = "INSERT INTO data (dt, id, value) VALUES (?, ?, ?)";
622
+ db_daqdata.run(sqlRequest, [ts, mapid, value], function (err) {
620
623
  if (err !== null) {
621
- reject();
624
+ reject(err);
622
625
  }
623
626
  else {
624
627
  resolve(ts);
@@ -692,4 +695,4 @@ module.exports = {
692
695
  return new DaqNode(data, logger, id, currentStorate);
693
696
  },
694
697
  checkRetention: checkRetention
695
- };
698
+ };
@@ -3,11 +3,20 @@
3
3
  let { options, connect } = require("@tdengine/rest");
4
4
  let utils = require('../../utils');
5
5
 
6
+ function quoteTdIdentifier(value) {
7
+ return '`' + String(value).replace(/`/g, '``') + '`';
8
+ }
9
+
10
+ function escapeTdString(value) {
11
+ return String(value).replace(/'/g, "''");
12
+ }
13
+
6
14
  function TDengine(_settings, _log, _currentStorage) {
7
15
  let settings = _settings; // Application settings
8
16
  const logger = _log; // Application logger
9
17
  const currentStorage = _currentStorage; // Database to set the last value (current)
10
18
  const database = settings.daqstore.database || 'fuxa';
19
+ const databaseRef = quoteTdIdentifier(database);
11
20
  let conn;
12
21
 
13
22
  this.setCall = function (_fncGetProp) {
@@ -25,8 +34,8 @@ function TDengine(_settings, _log, _currentStorage) {
25
34
  const cursor = conn.cursor();
26
35
  try {
27
36
  //TODO add retention
28
- let res = await cursor.query(`CREATE DATABASE IF NOT EXISTS ${database} `);
29
- res = await cursor.query(`CREATE STABLE IF NOT EXISTS ${database}.meters (dt TIMESTAMP,tag_id VARCHAR(200), tag_value BINARY(20)) TAGS (device_id VARCHAR(200),device_name BINARY(256) )`);
37
+ let res = await cursor.query(`CREATE DATABASE IF NOT EXISTS ${databaseRef} `);
38
+ res = await cursor.query(`CREATE STABLE IF NOT EXISTS ${databaseRef}.meters (dt TIMESTAMP,tag_id VARCHAR(200), tag_value BINARY(20)) TAGS (device_id VARCHAR(200),device_name BINARY(256) )`);
30
39
 
31
40
  } catch (error) {
32
41
  console.error(error);
@@ -47,8 +56,13 @@ function TDengine(_settings, _log, _currentStorage) {
47
56
  continue;
48
57
  }
49
58
  }
50
- let insertSql = `INSERT INTO ${database}.\`${deviceId}\` USING ${database}.meters TAGS('${deviceId}','${deviceName}')
51
- VALUES (NOW, '${tagid}', '${tag.value}')`;
59
+ const safeDeviceId = quoteTdIdentifier(deviceId);
60
+ const safeDeviceTag = escapeTdString(deviceId);
61
+ const safeDeviceName = escapeTdString(deviceName);
62
+ const safeTagId = escapeTdString(tagid);
63
+ const safeTagValue = escapeTdString(tag.value);
64
+ let insertSql = `INSERT INTO ${databaseRef}.${safeDeviceId} USING ${databaseRef}.meters TAGS('${safeDeviceTag}','${safeDeviceName}')
65
+ VALUES (NOW, '${safeTagId}', '${safeTagValue}')`;
52
66
  //async
53
67
 
54
68
  cursor.query(insertSql).then((rst) => {
@@ -71,11 +85,12 @@ function TDengine(_settings, _log, _currentStorage) {
71
85
  const cursor = conn.cursor()
72
86
  let data = []
73
87
  //add by J, the tagid is missed in the sql, should be one of the filter condition
88
+ const safeTagId = escapeTdString(tagid);
74
89
  cursor.query(`SELECT CAST(dt as BIGINT) as dt, tag_value
75
- FROM ${database}.meters
76
- WHERE tag_id = '${tagid}'
77
- and dt >= ${fromts}
78
- and dt < ${tots} `).then((result) => {
90
+ FROM ${databaseRef}.meters
91
+ WHERE tag_id = '${safeTagId}'
92
+ and dt >= ${Number(fromts)}
93
+ and dt < ${Number(tots)} `).then((result) => {
79
94
  // logger.debug(result)
80
95
  result.getData().forEach((row) => {
81
96
  data.push({ dt: row[0], value: row[1] })
@@ -105,4 +120,4 @@ module.exports = {
105
120
  create: function (data, logger, currentStorage) {
106
121
  return new TDengine(data, logger, currentStorage);
107
122
  }
108
- };
123
+ };