iobroker.rest-api 1.0.4 → 1.1.0

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 CHANGED
@@ -9,11 +9,11 @@
9
9
 
10
10
  **This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information how to disable the error reporting see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0.
11
11
 
12
- This is RESTFul interface to read the objects and states from ioBroker and to write/control the states over HTTP Get/Post requests.
12
+ This is a RESTFul interface to read the objects and states from ioBroker and to write/control the states over HTTP Get/Post requests.
13
13
 
14
14
  The purpose of this adapter is similar to simple-api. But this adapter supports long-polling and URL hooks for subscribes.
15
15
 
16
- It has very useful web interface to play with the requests:
16
+ It has a very useful web interface to play with the requests:
17
17
 
18
18
  ![Screenshot](img/screen.png)
19
19
 
@@ -24,22 +24,22 @@ Some request examples:
24
24
  - `http://ipaddress:8093/v1/state/system.adapter.rest-api.0.memHeapTotal` - read state as JSON
25
25
  - `http://ipaddress:8093/v1/state/system.adapter.rest-api.0.memHeapTotal/plain` - read state as string (only value)
26
26
  - `http://ipaddress:8093/v1/state/system.adapter.rest-api.0.memHeapTotal?value=5` - write state with GET (only for back compatibility with simple-api)
27
- - `http://ipaddress:8093/v1/sendto/javascript.0?message=toScript&data={"message":"MESSAGE","data":"FROM REST-API"}` - send message to javascript.0 in script `scriptName`
27
+ - `http://ipaddress:8093/v1/sendto/javascript.0?message=toScript&data={"message":"MESSAGE","data":"FROM REST-API"}` - send a message to javascript.0 in script `scriptName`
28
28
 
29
- ## Subscribe on state or object changes
29
+ ## Subscribe to the state's or object's changes
30
30
  Your application could get notifications by every change of the state or object.
31
31
 
32
- For that your application must provide an HTTP(S) end-point to accept the updates.
32
+ For that, your application must provide an HTTP(S) end-point to accept the updates.
33
33
 
34
34
  Example in node.js see here [demoNodeClient.js](examples/demoNodeClient.js)
35
35
 
36
36
  ## Long polling
37
- This adapter supports subscribe on data changes via long polling.
37
+ This adapter supports a subscribing on data changes via long polling.
38
38
 
39
39
  Example for browser could be found here: [demoNodeClient.js](examples/demoBrowserClient.html)
40
40
 
41
41
  ## Web extension
42
- This adapter can run as web-extension. In this case the path is available under http://iipaddress:8082/rest
42
+ This adapter can run as a web extension. In this case, the path is available under http://iipaddress:8082/rest
43
43
 
44
44
  ## Notice
45
45
  - `POST` is always for creating a resource (does not matter if it was duplicated)
@@ -73,6 +73,7 @@ You cannot send POST request to commands via GUI.
73
73
  <!-- START -->
74
74
  ### States
75
75
  - `getStates(pattern)` - get the list of states for pattern (e.g. for system.adapter.admin.0.*). GUI can have problems by visualization of answer.
76
+ - `getForeignStates(pattern)` - same as getStates
76
77
  - `getState(id)` - get state value by ID
77
78
  - `setState(id, state)` - set state value with JSON object (e.g. `{"val": 1, "ack": true}`)
78
79
  - `getBinaryState(id)` - get binary state by ID
@@ -126,7 +127,6 @@ You cannot send POST request to commands via GUI.
126
127
  - `getAllObjects()` - read all objects as list. GUI can have problems by visualization of answer.
127
128
  - `extendObject(id, obj)` - modify object by ID with JSON. (.e.g. `{"common":{"enabled": true}}`)
128
129
  - `getForeignObjects(pattern, type)` - same as getObjects
129
- - `getForeignStates(pattern)` - same as getStates
130
130
  - `delObjects(id, options)` - delete objects by pattern
131
131
 
132
132
  ### Others
@@ -142,15 +142,19 @@ You cannot send POST request to commands via GUI.
142
142
 
143
143
  <!-- END -->
144
144
 
145
- ## Todo
146
- - [ ] Implement GET,PATCH,POST,DELETE file operations
147
-
148
145
  <!--
149
146
  Placeholder for the next version (at the beginning of the line):
150
147
  ### **WORK IN PROGRESS**
151
148
  -->
152
149
 
153
150
  ## Changelog
151
+ ### 1.1.0 (2023-05-03)
152
+ * (bluefox) Converting of the setState values to the according type
153
+ * (bluefox) Implemented file operations
154
+
155
+ ### 1.0.5 (2023-03-27)
156
+ * (Apollon77) Prepare for future js-controller versions
157
+
154
158
  ### 1.0.4 (2022-08-31)
155
159
  * (bluefox) Check if the port is occupied only on defined interface
156
160
 
@@ -190,4 +194,4 @@ You cannot send POST request to commands via GUI.
190
194
  ## License
191
195
  Apache 2.0
192
196
 
193
- Copyright (c) 2017-2022 bluefox <dogafox@gmail.com>
197
+ Copyright (c) 2017-2023 bluefox <dogafox@gmail.com>
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "rest-api",
4
- "version": "1.0.4",
4
+ "version": "1.1.0",
5
5
  "news": {
6
+ "1.1.0": {
7
+ "en": "Converting of the setState values to the according type\nImplemented file operations",
8
+ "de": "Umrechnung der setState-Werte in den entsprechenden Typ\nImplementierung von Dateioperationen",
9
+ "ru": "Преобразование значений setState в согласно типу\nРеализованные файловые операции",
10
+ "pt": "Convertendo dos valores setState para o tipo de acordo\nOperações de arquivo implementadas",
11
+ "nl": "Omkeren van de setstate waarden naar het type\nGeïmplementeerde bestand operaties",
12
+ "fr": "Conversion des valeurs déterminées de l'état dans le type suivant\nOpérations de fichiers mises en œuvre",
13
+ "it": "Convertizione dei valori setState secondo il tipo\nOperazioni di file implementate",
14
+ "es": "Convertir los valores establecidos en el tipo\nOperaciones de archivo aplicadas",
15
+ "pl": "Przetłumaczył wartości zbioru zgodnie z typem\nImplementacja",
16
+ "uk": "Перетворення значень setState до за типом\nРеалізовані операції файлів",
17
+ "zh-cn": "避免按类型划分国家数值\n执行档案业务"
18
+ },
19
+ "1.0.5": {
20
+ "en": "Prepare for future js-controller versions",
21
+ "de": "Bereiten Sie sich auf zukünftige js-Controller-Versionen",
22
+ "ru": "Подготовьтесь к будущим версиям js-controller",
23
+ "pt": "Prepare-se para futuras versões js-controller",
24
+ "nl": "Bereid je voor op toekomstige Js-controller versie",
25
+ "fr": "Préparez-vous pour les versions futures de js-controller",
26
+ "it": "Prepararsi per le future versioni js-controller",
27
+ "es": "Prepararse para futuras versiones js-controller",
28
+ "pl": "Prepare for future js-controller version",
29
+ "uk": "Підготовка до майбутніх версій js-controller",
30
+ "zh-cn": "今后防污版本的编制"
31
+ },
6
32
  "1.0.4": {
7
33
  "en": "Check if the port is occupied only on defined interface",
8
34
  "de": "Überprüfen Sie, ob der Port nur auf definierter Schnittstelle besetzt ist",
@@ -62,30 +88,6 @@
62
88
  "es": "Ruta de envío agregada",
63
89
  "pl": "Dodano ścieżkę sendTo",
64
90
  "zh-cn": "添加了 sendTo 路径"
65
- },
66
- "0.5.0": {
67
- "en": "Some access errors were corrected",
68
- "de": "Einige Zugriffsfehler wurden behoben",
69
- "ru": "Исправлены некоторые ошибки доступа",
70
- "pt": "Alguns erros de acesso foram corrigidos",
71
- "nl": "Sommige toegangsfouten zijn gecorrigeerd",
72
- "fr": "Certaines erreurs d'accès ont été corrigées",
73
- "it": "Alcuni errori di accesso sono stati corretti",
74
- "es": "Se corrigieron algunos errores de acceso",
75
- "pl": "Poprawiono niektóre błędy dostępu",
76
- "zh-cn": "一些访问错误已得到纠正"
77
- },
78
- "0.4.0": {
79
- "en": "Added socket commands",
80
- "de": "Socket-Befehle hinzugefügt",
81
- "ru": "Добавлены команды сокета",
82
- "pt": "Comandos de soquete adicionados",
83
- "nl": "Socket-opdrachten toegevoegd",
84
- "fr": "Commandes de socket ajoutées",
85
- "it": "Aggiunti comandi socket",
86
- "es": "Comandos de socket agregados",
87
- "pl": "Dodano polecenia dotyczące gniazd",
88
- "zh-cn": "添加了套接字命令"
89
91
  }
90
92
  },
91
93
  "title": "REST API",
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+ const commonLib = require('./common.js');
3
+
4
+ module.exports = {
5
+ readFile: function (req, res) {
6
+ commonLib.checkPermissions(req._adapter, req._user, [{type: 'file', operation: 'read'}], async error => {
7
+ if (error) {
8
+ commonLib.errorResponse(req, res, error);
9
+ } else {
10
+ const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
11
+ try {
12
+ const data = await req._adapter.readFileAsync(params.objectId, params.fileName, {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
13
+ if (data && data.mimeType) {
14
+ res.set('Content-Type', data.mimeType);
15
+ res.send(data.file);
16
+ } else {
17
+ res.status(404).send(Buffer.from(''));
18
+ }
19
+ } catch (error) {
20
+ commonLib.errorResponse(req, res, error);
21
+ }
22
+ }
23
+ });
24
+ },
25
+ deleteFile: function (req, res) {
26
+ commonLib.checkPermissions(req._adapter, req._user, [{type: 'file', operation: 'delete'}], async error => {
27
+ if (error) {
28
+ commonLib.errorResponse(req, res, error);
29
+ } else {
30
+ const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
31
+ try {
32
+ await req._adapter.delFileAsync(params.objectId, params.fileName, {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
33
+ res.json({success: true});
34
+ } catch (err) {
35
+ if (err.toString().includes('Not exists')) {
36
+ res.status(404).json({error: err.toString()});
37
+ } else {
38
+ commonLib.errorResponse(req, res, err);
39
+ }
40
+ }
41
+ }
42
+ });
43
+ },
44
+ writeFile: function (req, res) {
45
+ commonLib.checkPermissions(req._adapter, req._user, [{type: 'file', operation: 'write'}], async error => {
46
+ if (error) {
47
+ commonLib.errorResponse(req, res, error);
48
+ } else {
49
+ const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
50
+ try {
51
+ await req._adapter.writeFileAsync(params.objectId, params.fileName, req.files.file[0].buffer, {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
52
+ res.json({success: true});
53
+ } catch (err) {
54
+ commonLib.errorResponse(req, res, err);
55
+ }
56
+ }
57
+ });
58
+ },
59
+ readDir: function (req, res) {
60
+ commonLib.checkPermissions(req._adapter, req._user, [{type: 'file', operation: 'list'}], async error => {
61
+ if (error) {
62
+ commonLib.errorResponse(req, res, error);
63
+ } else {
64
+ const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
65
+ try {
66
+ const response = await req._adapter.readDirAsync(params.objectId, params.dirName || '', {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
67
+ res.json(response);
68
+ } catch (err) {
69
+ commonLib.errorResponse(req, res, err);
70
+ }
71
+ }
72
+ });
73
+ },
74
+ };
@@ -2,7 +2,7 @@
2
2
  swagger: "2.0"
3
3
  info:
4
4
  description: "This is a REST server for ioBroker."
5
- version: "1.0.4"
5
+ version: "1.1.0"
6
6
  title: "ioBroker Swagger UI"
7
7
  contact:
8
8
  email: "admin@iobroker.net"
@@ -28,6 +28,8 @@ tags:
28
28
  description: "Socket commands"
29
29
  - name: "sendTo"
30
30
  description: "Send message to instance"
31
+ - name: "file"
32
+ description: "Read/Write files and directories"
31
33
  securityDefinitions:
32
34
  basicAuth:
33
35
  type: basic
@@ -625,9 +627,7 @@ paths:
625
627
  200:
626
628
  description: "successful operation"
627
629
  schema:
628
- type: array
629
- items:
630
- $ref: "#/definitions/Object"
630
+ $ref: "#/definitions/ObjectArray"
631
631
  404:
632
632
  description: "URL or session not found"
633
633
  422:
@@ -997,8 +997,136 @@ paths:
997
997
  description: "Invalid state ID supplied"
998
998
  500:
999
999
  description: "instance is offline"
1000
+ /file/{objectId}/{fileName}:
1001
+ x-swagger-router-controller: file
1002
+ get:
1003
+ tags:
1004
+ - "file"
1005
+ summary: "Reads file"
1006
+ operationId: "readFile"
1007
+ produces:
1008
+ - "application/octet-stream"
1009
+ parameters:
1010
+ - name: "objectId"
1011
+ in: "path"
1012
+ description: "Object ID, like vis.0"
1013
+ type: "string"
1014
+ required: true
1015
+ - name: "fileName"
1016
+ in: "path"
1017
+ description: "File name, like main/vis-views.json"
1018
+ type: "string"
1019
+ required: true
1020
+ responses:
1021
+ 200:
1022
+ description: "successful operation"
1023
+ 404:
1024
+ description: "File not found"
1025
+ post:
1026
+ tags:
1027
+ - "file"
1028
+ summary: "Writes file"
1029
+ operationId: "writeFile"
1030
+ produces:
1031
+ - "application/json"
1032
+ consumes:
1033
+ - "multipart/form-data"
1034
+ parameters:
1035
+ - name: "objectId"
1036
+ in: "path"
1037
+ description: "Object ID, like vis.0"
1038
+ type: "string"
1039
+ required: true
1040
+ - name: "fileName"
1041
+ in: "path"
1042
+ description: "File name, like main/vis-views.json"
1043
+ type: "string"
1044
+ required: true
1045
+ - name: "file"
1046
+ in: "formData"
1047
+ description: "File content"
1048
+ required: true
1049
+ type: "file"
1050
+ responses:
1051
+ 200:
1052
+ description: "successful operation"
1053
+ 404:
1054
+ description: "File not found"
1055
+ delete:
1056
+ tags:
1057
+ - "file"
1058
+ summary: "deletes file"
1059
+ operationId: "deleteFile"
1060
+ produces:
1061
+ - "application/json"
1062
+ parameters:
1063
+ - name: "objectId"
1064
+ in: "path"
1065
+ description: "Object ID, like vis.0"
1066
+ type: "string"
1067
+ required: true
1068
+ - name: "fileName"
1069
+ in: "path"
1070
+ description: "File name, like main/vis-views.json"
1071
+ type: "string"
1072
+ required: true
1073
+ responses:
1074
+ 200:
1075
+ description: "successful operation"
1076
+ 404:
1077
+ description: "File not found"
1078
+ /dir/{objectId}/{dirName}:
1079
+ x-swagger-router-controller: file
1080
+ get:
1081
+ tags:
1082
+ - "file"
1083
+ summary: "List directory"
1084
+ operationId: "readDir"
1085
+ produces:
1086
+ - "application/json"
1087
+ parameters:
1088
+ - name: "objectId"
1089
+ in: "path"
1090
+ description: "Object ID, like vis.0"
1091
+ type: "string"
1092
+ required: true
1093
+ - name: "dirName"
1094
+ in: "path"
1095
+ description: "File name, like main/vis-views.json"
1096
+ type: "string"
1097
+ required: true
1098
+ responses:
1099
+ 200:
1100
+ description: "successful operation"
1101
+ schema:
1102
+ $ref: "#/definitions/DirResponse"
1103
+ 404:
1104
+ description: "File not found"
1105
+ /dir/{objectId}:
1106
+ x-swagger-router-controller: file
1107
+ get:
1108
+ tags:
1109
+ - "file"
1110
+ summary: "List directory"
1111
+ operationId: "readDir"
1112
+ produces:
1113
+ - "application/json"
1114
+ parameters:
1115
+ - name: "objectId"
1116
+ in: "path"
1117
+ description: "Object ID, like vis.0"
1118
+ type: "string"
1119
+ required: true
1120
+ responses:
1121
+ 200:
1122
+ description: "successful operation"
1123
+ schema:
1124
+ $ref: "#/definitions/DirResponse"
1125
+ 404:
1126
+ description: "File not found"
1000
1127
 
1001
- # commands start
1128
+
1129
+ # commands start
1002
1130
  /command/getStates:
1003
1131
  get:
1004
1132
  tags:
@@ -1016,6 +1144,23 @@ paths:
1016
1144
  responses:
1017
1145
  200:
1018
1146
  description: "successful operation"
1147
+ /command/getForeignStates:
1148
+ get:
1149
+ tags:
1150
+ - "commands"
1151
+ summary: "same as getStates"
1152
+ produces:
1153
+ - "application/json"
1154
+ parameters:
1155
+
1156
+ - name: "pattern"
1157
+ in: "query"
1158
+ description: ""
1159
+ type: "string"
1160
+ required: true
1161
+ responses:
1162
+ 200:
1163
+ description: "successful operation"
1019
1164
  /command/getState:
1020
1165
  get:
1021
1166
  tags:
@@ -1959,23 +2104,6 @@ paths:
1959
2104
  responses:
1960
2105
  200:
1961
2106
  description: "successful operation"
1962
- /command/getForeignStates:
1963
- get:
1964
- tags:
1965
- - "commands"
1966
- summary: "same as getStates"
1967
- produces:
1968
- - "application/json"
1969
- parameters:
1970
-
1971
- - name: "pattern"
1972
- in: "query"
1973
- description: ""
1974
- type: "string"
1975
- required: true
1976
- responses:
1977
- 200:
1978
- description: "successful operation"
1979
2107
  /command/delObjects:
1980
2108
  get:
1981
2109
  tags:
@@ -2231,6 +2359,10 @@ definitions:
2231
2359
  native:
2232
2360
  type: "object"
2233
2361
  description: "Native state description"
2362
+ ObjectArray:
2363
+ type: "object"
2364
+ additionalProperties:
2365
+ $ref: "#/definitions/Object"
2234
2366
  UrlHook:
2235
2367
  type: "object"
2236
2368
  properties:
@@ -2408,6 +2540,33 @@ definitions:
2408
2540
  type: "array"
2409
2541
  items:
2410
2542
  $ref: "#/definitions/EnumEntry"
2543
+ FileEntry:
2544
+ type: "object"
2545
+ properties:
2546
+ file:
2547
+ type: "string"
2548
+ description: "File name"
2549
+ stats:
2550
+ type: "object"
2551
+ description: "File size"
2552
+ properties:
2553
+ size:
2554
+ type: "number"
2555
+ description: "File size in bytes"
2556
+ isDir:
2557
+ type: "boolean"
2558
+ description: "Is directory"
2559
+ modifiedAt:
2560
+ type: "number"
2561
+ description: "Modification time in ms"
2562
+ createdAt:
2563
+ type: "number"
2564
+ description: "Creation time in ms"
2565
+ DirResponse:
2566
+ type: "array"
2567
+ items:
2568
+ $ref: "#/definitions/FileEntry"
2569
+
2411
2570
  SendToData:
2412
2571
  type: "object"
2413
2572
  properties:
package/lib/rest-api.js CHANGED
@@ -13,8 +13,9 @@ const axios = require('axios');
13
13
  const cors = require('cors');
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
+ const multer = require('multer');
16
17
  const utils = require('@iobroker/adapter-core'); // Get common adapter utils
17
- const tools = require(utils.controllerDir + '/lib/tools');
18
+ const pattern2RegEx = utils.commonTools.pattern2RegEx;
18
19
  const CommandsAdmin = require('@iobroker/socket-classes').SocketCommandsAdmin;
19
20
  const CommandsCommon = require('@iobroker/socket-classes').SocketCommands;
20
21
  const common = require('./common');
@@ -23,6 +24,26 @@ process.env.SUPPRESS_NO_CONFIG_WARNING = 'true';
23
24
 
24
25
  const WEB_EXTENSION_PREFIX = 'rest-api/';
25
26
 
27
+ /*const memStore = { };
28
+
29
+ // Writable memory stream
30
+ class WMStrm extends Writable {
31
+ constructor(key, options) {
32
+ super(options); // init super
33
+ this.key = key; // save key
34
+ this.data = Buffer.from(''); // empty
35
+ }
36
+
37
+ _write(chunk, enc, cb) {
38
+ // our memory store stores things in buffers
39
+ const buffer = (Buffer.isBuffer(chunk)) ? chunk : Buffer.from(chunk, enc);
40
+
41
+ // concat to the buffer already there
42
+ this.data = Buffer.concat([this.data, buffer]);
43
+ cb();
44
+ }
45
+ }
46
+ */
26
47
  function parseQuery(_url) {
27
48
  let url = decodeURI(_url);
28
49
  const pos = url.indexOf('?');
@@ -286,17 +307,17 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
286
307
  };
287
308
 
288
309
  // prepare yaml
289
- _options.swaggerFile = __dirname + '/api/swagger/swagger.yaml';
310
+ _options.swaggerFile = `${__dirname}/api/swagger/swagger.yaml`;
290
311
  if (this.adapter.config.noCommands) {
291
312
  const newText = removeTextFromFile(_options.swaggerFile, '# commands start', '# commands stop');
292
313
 
293
- _options.swaggerFile = __dirname + '/api/swagger/swaggerEdited.yaml';
314
+ _options.swaggerFile = `${__dirname}/api/swagger/swaggerEdited.yaml`;
294
315
  if (!fs.existsSync(_options.swaggerFile) || fs.readFileSync(_options.swaggerFile).toString('utf8') !== newText) {
295
316
  fs.writeFileSync(_options.swaggerFile, newText);
296
317
  }
297
318
  } else if (this.adapter.config.noAdminCommands) {
298
319
  const newText = removeTextFromFile(_options.swaggerFile, '# admin commands start', '# admin commands end');
299
- _options.swaggerFile = __dirname + '/api/swagger/swaggerEdited.yaml';
320
+ _options.swaggerFile = `${__dirname}/api/swagger/swaggerEdited.yaml`;
300
321
  if (!fs.existsSync(_options.swaggerFile) || fs.readFileSync(_options.swaggerFile).toString('utf8') !== newText) {
301
322
  fs.writeFileSync(_options.swaggerFile, newText);
302
323
  }
@@ -307,7 +328,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
307
328
  let file = fs.readFileSync(_options.swaggerFile).toString('utf8')
308
329
  file = file.replace('basePath: "/v1"', `basePath: "/${WEB_EXTENSION_PREFIX}v1"`);
309
330
 
310
- _options.swaggerFile = __dirname + '/api/swagger/swagger_extension.yaml';
331
+ _options.swaggerFile = `${__dirname}/api/swagger/swagger_extension.yaml`;
311
332
 
312
333
  if (!fs.existsSync(_options.swaggerFile) || fs.readFileSync(_options.swaggerFile).toString('utf8') !== file) {
313
334
  fs.writeFileSync(_options.swaggerFile, file);
@@ -322,16 +343,16 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
322
343
 
323
344
 
324
345
  if (!this.config.noUI) {
325
- this.app.get(this.routerPrefix + 'api-docs/swagger.json', (req, res) =>
346
+ this.app.get(`${this.routerPrefix}api-docs/swagger.json`, (req, res) =>
326
347
  res.json(swaggerDocument));
327
348
 
328
349
  const options = {
329
350
  customCss: '.swagger-ui .topbar { background-color: #4dabf5; }',
330
351
  };
331
352
  // show WEB CSS and so on
332
- this.app.use(this.routerPrefix + 'api-doc/', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
353
+ this.app.use(`${this.routerPrefix}api-doc/`, swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
333
354
  this.app.get(this.routerPrefix, (req, res) =>
334
- res.redirect(this.routerPrefix + 'api-doc/'));
355
+ res.redirect(`${this.routerPrefix}api-doc/`));
335
356
  }
336
357
 
337
358
  function isAuthenticated(req, res, callback) {
@@ -354,7 +375,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
354
375
  }
355
376
  }
356
377
  if (!values.user.match(/^system\.user\./)) {
357
- values.user = 'system.user.' + values.user;
378
+ values.user = `system.user.${values.user}`;
358
379
  }
359
380
 
360
381
  that.adapter.checkPassword(values.user, values.pass, result => {
@@ -533,7 +554,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
533
554
  const item = {id, delta: options.delta, onchange: options.onchange};
534
555
  this.subscribes[urlHash][type].push(item);
535
556
  if (item.id.includes('*')) {
536
- item.regEx = new RegExp(tools.pattern2RegEx(item.id));
557
+ item.regEx = new RegExp(pattern2RegEx(item.id));
537
558
  }
538
559
 
539
560
  if (type === 'state') {
@@ -686,7 +707,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
686
707
  }
687
708
 
688
709
  task.timer = setTimeout(_task => {
689
- // remove this task from list
710
+ // remove this task from the list
690
711
  const pos = this._waitFor.indexOf(_task);
691
712
  if (pos !== -1) {
692
713
  this._waitFor.splice(pos, 1);
@@ -702,11 +723,11 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
702
723
 
703
724
  this.app.get('/favicon.ico', (req, res) => {
704
725
  res.set('Content-Type', 'image/x-icon');
705
- res.send(fs.readFileSync(__dirname + '/../img/favicon.ico'));
726
+ res.send(fs.readFileSync(`${__dirname}/../img/favicon.ico`));
706
727
  });
707
728
 
708
729
  // authenticate
709
- this.app.use(this.routerPrefix + 'v1/*', (req, res, next) => {
730
+ this.app.use(`${this.routerPrefix}v1/*`, (req, res, next) => {
710
731
  isAuthenticated(req, res, () => {
711
732
  req._adapter = this.adapter;
712
733
  req._swaggerObject = this;
@@ -714,7 +735,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
714
735
  });
715
736
  });
716
737
 
717
- this.app.get(this.routerPrefix + 'v1/polling', (req, res, next) => {
738
+ this.app.get(`${this.routerPrefix}v1/polling`, (req, res, next) => {
718
739
  res.writeHead(200, {'Content-Type': 'text/plain'});
719
740
  const ip = req.query.sid || req.headers['x-forwarded-for'] || req.socket.remoteAddress;
720
741
  const urlHash = crypto.createHash('md5').update(ip).digest('hex');
@@ -799,13 +820,13 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
799
820
  });
800
821
  });
801
822
 
802
- this.app.use(this.routerPrefix + 'v1/command/*', (req, res) => {
823
+ this.app.use(`${this.routerPrefix}v1/command/*`, async (req, res) => {
803
824
  if (this.adapter.config.noCommands) {
804
825
  res.status(404).json({error: `Commands are disabled`});
805
826
  return;
806
827
  }
807
828
 
808
- let command = req.originalUrl.startsWith('/' + WEB_EXTENSION_PREFIX) ? req.originalUrl.split('/')[4] : req.originalUrl.split('/')[3];
829
+ let command = req.originalUrl.startsWith(`/${WEB_EXTENSION_PREFIX}`) ? req.originalUrl.split('/')[4] : req.originalUrl.split('/')[3];
809
830
  command = command.split('?')[0];
810
831
 
811
832
  const handler = this.commands.getCommandHandler(command);
@@ -813,7 +834,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
813
834
  const args = common.getParamNames(handler).map(item => item[0] === '_' ? item.substring(1) : item);
814
835
 
815
836
  args.shift(); // remove socket
816
- // try to parse query or body
837
+ // try to parse a query or body
817
838
  let params = parseQuery(req.originalUrl);
818
839
  if (req.body) {
819
840
  Object.assign(params, req.body);
@@ -837,7 +858,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
837
858
  }
838
859
 
839
860
  const _acl = {
840
- user: req._user
861
+ user: req._user,
841
862
  };
842
863
 
843
864
  const _arguments = [{_acl}];
@@ -846,7 +867,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
846
867
  if (name !== 'callback') {
847
868
  if (name === 'options' || name === 'params' || name === 'obj' || name === 'message') {
848
869
  // try to convert
849
- if (typeof params[name] == 'string' && params[name].startsWith('{') && params[name].endsWith('}')) {
870
+ if (typeof params[name] === 'string' && params[name].startsWith('{') && params[name].endsWith('}')) {
850
871
  try {
851
872
  params[name] = JSON.parse(params[name]);
852
873
  } catch (_error) {
@@ -869,6 +890,26 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
869
890
  }
870
891
  });
871
892
 
893
+ // try to convert arguments for setState and setForeignState
894
+ if (command === 'setState' || command === 'setForeignState') {
895
+ // read object
896
+ try {
897
+ const obj = await adapter.getForeignObjectAsync(_arguments[1], {user: req._user});
898
+ if (obj && obj.common && obj.common.type) {
899
+ if (obj.common.type === 'number') {
900
+ _arguments[2] = parseFloat(_arguments[2]);
901
+ } else if (obj.common.type === 'boolean') {
902
+ _arguments[2] = _arguments[2] === 'true' || _arguments[2] === true || _arguments[2] === 1 || _arguments[2] === '1' || _arguments[2] === 'on' || _arguments[2] === 'ON';
903
+ } else if (obj.common.type === 'string') {
904
+ _arguments[2] = _arguments[2].toString();
905
+ }
906
+ }
907
+ } catch (error) {
908
+ res.status(501).json({error});
909
+ return;
910
+ }
911
+ }
912
+
872
913
  if (!error) {
873
914
  if (args[args.length - 1] === 'callback') {
874
915
  _arguments.push((error, ...args) => {
@@ -910,9 +951,9 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
910
951
  }
911
952
  });
912
953
 
913
- this.app.get(this.routerPrefix + 'log/*',(req, res) => {
954
+ this.app.get(`${this.routerPrefix}log/*`,(req, res) => {
914
955
  let parts = decodeURIComponent(req.url).split('/');
915
- if (req.originalUrl.startsWith('/' + WEB_EXTENSION_PREFIX)) {
956
+ if (req.originalUrl.startsWith(`/${WEB_EXTENSION_PREFIX}`)) {
916
957
  parts.shift();
917
958
  }
918
959
 
@@ -922,7 +963,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
922
963
  const [host, transport] = parts;
923
964
  parts = parts.splice(2);
924
965
  let filename = parts.join('/');
925
- this.adapter.sendToHost('system.host.' + host, 'getLogFile', {filename, transport}, result => {
966
+ this.adapter.sendToHost(`system.host.${host}`, 'getLogFile', {filename, transport}, result => {
926
967
  if (!result || result.error) {
927
968
  res.status(404).send(`File ${escapeHtml(filename)} not found`);
928
969
  } else {
@@ -969,9 +1010,9 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
969
1010
  }
970
1011
 
971
1012
  if (logFolder[0] !== '/' && logFolder[0] !== '\\' && !logFolder.match(/^[a-zA-Z]:/)) {
972
- const _logFolder = path.normalize(path.join(__dirname + '/../../../', logFolder).replace(/\\/g, '/')).replace(/\\/g, '/');
1013
+ const _logFolder = path.normalize(path.join(`${__dirname}/../../../`, logFolder).replace(/\\/g, '/')).replace(/\\/g, '/');
973
1014
  if (!fs.existsSync(_logFolder)) {
974
- logFolder = path.normalize(path.join(__dirname + '/../../', logFolder).replace(/\\/g, '/')).replace(/\\/g, '/');
1015
+ logFolder = path.normalize(path.join(`${__dirname}/../../`, logFolder).replace(/\\/g, '/')).replace(/\\/g, '/');
975
1016
  } else {
976
1017
  logFolder = _logFolder;
977
1018
  }
@@ -1013,9 +1054,12 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
1013
1054
  }
1014
1055
  });
1015
1056
 
1057
+ // parse binary files
1058
+ this.app.post(`${this.routerPrefix}v1/file/*`, multer().fields([{ name: 'file', maxCount: 1 }]), (req, res, next) => next());
1059
+
1016
1060
  this.unload = async () => {
1017
1061
  if (this.config.webInstance) {
1018
- await this.adapter.setForeignStateAsync(this.namespace + '.info.extension', false, true);
1062
+ await this.adapter.setForeignStateAsync(`${this.namespace}.info.extension`, false, true);
1019
1063
  }
1020
1064
  this.checkInterval && clearInterval(this.checkInterval);
1021
1065
  this.checkInterval = null;
package/main.js CHANGED
@@ -4,7 +4,7 @@
4
4
  'use strict';
5
5
 
6
6
  const utils = require('@iobroker/adapter-core'); // Get common adapter utils
7
- const LE = require(utils.controllerDir + '/lib/letsencrypt.js');
7
+ const LE = utils.commonTools.letsEncrypt;
8
8
  const RestAPI = require('./lib/rest-api.js');
9
9
  const adapterName = require('./package.json').name.split('.').pop();
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.rest-api",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "RESTful interface for ioBroker with GUI.",
5
5
  "author": {
6
6
  "name": "bluefox",
@@ -19,27 +19,29 @@
19
19
  "url": "https://github.com/ioBroker/ioBroker.rest-api"
20
20
  },
21
21
  "dependencies": {
22
- "@iobroker/adapter-core": "^2.6.0",
23
- "@iobroker/socket-classes": "^0.5.3",
24
- "axios": "^0.27.2",
25
- "body-parser": "^1.20.0",
22
+ "@iobroker/adapter-core": "^2.6.8",
23
+ "@iobroker/socket-classes": "^1.1.5",
24
+ "axios": "^1.4.0",
25
+ "body-parser": "^1.20.2",
26
26
  "cors": "^2.8.5",
27
- "express": "^4.18.1",
27
+ "express": "^4.18.2",
28
28
  "swagger-node-runner-fork": "^0.8.0",
29
- "swagger-ui-express": "^4.5.0",
30
- "yamljs": "^0.3.0"
29
+ "swagger-ui-express": "^4.6.2",
30
+ "yamljs": "^0.3.0",
31
+ "multer": "^1.4.5-lts.1"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@alcalzone/release-script": "^3.5.9",
34
35
  "@alcalzone/release-script-plugin-iobroker": "^3.5.9",
35
36
  "@alcalzone/release-script-plugin-license": "^3.5.9",
37
+ "@iobroker/adapter-dev": "^1.2.0",
36
38
  "@iobroker/testing": "^4.1.0",
37
- "chai": "^4.3.6",
39
+ "chai": "^4.3.7",
38
40
  "eslint-plugin-eqeqeq-fix": "^1.0.3",
39
- "eslint-plugin-only-warn": "^1.0.3",
40
- "eslint-plugin-react": "^7.31.1",
41
+ "eslint-plugin-only-warn": "^1.1.0",
42
+ "eslint-plugin-react": "^7.32.2",
41
43
  "gulp": "^4.0.2",
42
- "mocha": "^9.2.2"
44
+ "mocha": "^10.2.0"
43
45
  },
44
46
  "bugs": {
45
47
  "url": "https://github.com/ioBroker/ioBroker.rest-api/issues"
@@ -60,7 +62,9 @@
60
62
  "release": "release-script",
61
63
  "release-patch": "release-script patch --yes --no-update-lockfile",
62
64
  "release-minor": "release-script minor --yes --no-update-lockfile",
63
- "release-major": "release-script major --yes --no-update-lockfile"
65
+ "release-major": "release-script major --yes --no-update-lockfile",
66
+ "translate": "translate-adapter",
67
+ "update-packages": "ncu --upgrade"
64
68
  },
65
69
  "license": "Apache-2.0"
66
70
  }