iobroker.rest-api 1.0.1 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/io-package.json +38 -37
- package/lib/api/controllers/common.js +39 -2
- package/lib/api/controllers/sendTo.js +104 -101
- package/lib/api/controllers/state.js +95 -0
- package/lib/api/swagger/swagger.yaml +124 -26
- package/lib/rest-api.js +22 -3
- package/main.js +3 -3
- package/package.json +15 -13
package/README.md
CHANGED
|
@@ -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
|
|
@@ -115,6 +116,7 @@ You cannot send POST request to commands via GUI.
|
|
|
115
116
|
- `getCompactAdapters()` - read list of installed adapters with short information
|
|
116
117
|
- `getCompactInstalled(host)` - read short information about installed adapters
|
|
117
118
|
- `getCompactSystemConfig()` - read short system config
|
|
119
|
+
- `getCompactSystemRepositories()`
|
|
118
120
|
- `getCompactRepository(host)` - read short repository
|
|
119
121
|
- `getCompactHosts()` - get short information about hosts
|
|
120
122
|
- `addUser(user, pass)` - add new user
|
|
@@ -125,7 +127,6 @@ You cannot send POST request to commands via GUI.
|
|
|
125
127
|
- `getAllObjects()` - read all objects as list. GUI can have problems by visualization of answer.
|
|
126
128
|
- `extendObject(id, obj)` - modify object by ID with JSON. (.e.g. `{"common":{"enabled": true}}`)
|
|
127
129
|
- `getForeignObjects(pattern, type)` - same as getObjects
|
|
128
|
-
- `getForeignStates(pattern)` - same as getStates
|
|
129
130
|
- `delObjects(id, options)` - delete objects by pattern
|
|
130
131
|
|
|
131
132
|
### Others
|
|
@@ -140,12 +141,25 @@ You cannot send POST request to commands via GUI.
|
|
|
140
141
|
- `getAdapterInstances(adapterName)` - get objects of type "instance". You can define optionally adapterName
|
|
141
142
|
|
|
142
143
|
<!-- END -->
|
|
144
|
+
|
|
145
|
+
## Todo
|
|
146
|
+
- [ ] Implement GET,PATCH,POST,DELETE file operations
|
|
147
|
+
|
|
143
148
|
<!--
|
|
144
149
|
Placeholder for the next version (at the beginning of the line):
|
|
145
150
|
### **WORK IN PROGRESS**
|
|
146
151
|
-->
|
|
147
152
|
|
|
148
153
|
## Changelog
|
|
154
|
+
### 1.0.5 (2023-03-27)
|
|
155
|
+
* (Apollon77) Prepare for future js-controller versions
|
|
156
|
+
|
|
157
|
+
### 1.0.4 (2022-08-31)
|
|
158
|
+
* (bluefox) Check if the port is occupied only on defined interface
|
|
159
|
+
|
|
160
|
+
### 1.0.2 (2022-07-27)
|
|
161
|
+
* (bluefox) Implemented binary read/write operations
|
|
162
|
+
|
|
149
163
|
### 1.0.1 (2022-07-27)
|
|
150
164
|
* (bluefox) Increased the max size of body to 100Mb
|
|
151
165
|
|
|
@@ -179,4 +193,4 @@ You cannot send POST request to commands via GUI.
|
|
|
179
193
|
## License
|
|
180
194
|
Apache 2.0
|
|
181
195
|
|
|
182
|
-
Copyright (c) 2017-
|
|
196
|
+
Copyright (c) 2017-2023 bluefox <dogafox@gmail.com>
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "rest-api",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.5",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.0.5": {
|
|
7
|
+
"en": "Prepare for future js-controller versions",
|
|
8
|
+
"de": "Bereiten Sie sich auf zukünftige js-Controller-Versionen",
|
|
9
|
+
"ru": "Подготовьтесь к будущим версиям js-controller",
|
|
10
|
+
"pt": "Prepare-se para futuras versões js-controller",
|
|
11
|
+
"nl": "Bereid je voor op toekomstige Js-controller versie",
|
|
12
|
+
"fr": "Préparez-vous pour les versions futures de js-controller",
|
|
13
|
+
"it": "Prepararsi per le future versioni js-controller",
|
|
14
|
+
"es": "Prepararse para futuras versiones js-controller",
|
|
15
|
+
"pl": "Prepare for future js-controller version",
|
|
16
|
+
"uk": "Підготовка до майбутніх версій js-controller",
|
|
17
|
+
"zh-cn": "今后防污版本的编制"
|
|
18
|
+
},
|
|
19
|
+
"1.0.4": {
|
|
20
|
+
"en": "Check if the port is occupied only on defined interface",
|
|
21
|
+
"de": "Überprüfen Sie, ob der Port nur auf definierter Schnittstelle besetzt ist",
|
|
22
|
+
"ru": "Проверьте, занят ли порт только на определенном интерфейсе",
|
|
23
|
+
"pt": "Verifique se a porta está ocupada apenas na interface definida",
|
|
24
|
+
"nl": "Controleer of de haven alleen bezet is op definitieve interface",
|
|
25
|
+
"fr": "Vérifiez si le port n'est occupé que sur l'interface définie",
|
|
26
|
+
"it": "Controllare se la porta è occupata solo su interfaccia definita",
|
|
27
|
+
"es": "Compruebe si el puerto está ocupado sólo en la interfaz definida",
|
|
28
|
+
"pl": "Jeśli port jest zajęty tylko na określonym interfejsie",
|
|
29
|
+
"zh-cn": "如果港口只在界定的界线上被占有,则该港口将被扣押。"
|
|
30
|
+
},
|
|
31
|
+
"1.0.2": {
|
|
32
|
+
"en": "Implemented binary read/write operations",
|
|
33
|
+
"de": "Implementierung binärer Schreib-/Lesevorgänge",
|
|
34
|
+
"ru": "Внедренные бинарные чтения / записи операции",
|
|
35
|
+
"pt": "Operações de leitura/escrita binárias implementadas",
|
|
36
|
+
"nl": "Geïmplementeerde binaire las/schrijf operaties",
|
|
37
|
+
"fr": "Opérations de lecture/écriture binaires mises en œuvre",
|
|
38
|
+
"it": "Operazioni di lettura/scrittura binarie implementate",
|
|
39
|
+
"es": "Operaciones de lectura y escritura binarias aplicadas",
|
|
40
|
+
"pl": "Poprawianie binarnego odczytu/pisania operacji",
|
|
41
|
+
"zh-cn": "执行本文的文字/仪式作业"
|
|
42
|
+
},
|
|
6
43
|
"1.0.1": {
|
|
7
44
|
"en": "Increased the max size of body to 100Mb",
|
|
8
45
|
"de": "Erhöht die maximale Körpergröße auf 100Mb",
|
|
@@ -50,42 +87,6 @@
|
|
|
50
87
|
"es": "Se corrigieron algunos errores de acceso",
|
|
51
88
|
"pl": "Poprawiono niektóre błędy dostępu",
|
|
52
89
|
"zh-cn": "一些访问错误已得到纠正"
|
|
53
|
-
},
|
|
54
|
-
"0.4.0": {
|
|
55
|
-
"en": "Added socket commands",
|
|
56
|
-
"de": "Socket-Befehle hinzugefügt",
|
|
57
|
-
"ru": "Добавлены команды сокета",
|
|
58
|
-
"pt": "Comandos de soquete adicionados",
|
|
59
|
-
"nl": "Socket-opdrachten toegevoegd",
|
|
60
|
-
"fr": "Commandes de socket ajoutées",
|
|
61
|
-
"it": "Aggiunti comandi socket",
|
|
62
|
-
"es": "Comandos de socket agregados",
|
|
63
|
-
"pl": "Dodano polecenia dotyczące gniazd",
|
|
64
|
-
"zh-cn": "添加了套接字命令"
|
|
65
|
-
},
|
|
66
|
-
"0.3.6": {
|
|
67
|
-
"en": "Added object creation and enumerations reading",
|
|
68
|
-
"de": "Objekterstellung und Lesen von Aufzählungen hinzugefügt",
|
|
69
|
-
"ru": "Добавлено создание объектов и чтение перечислений",
|
|
70
|
-
"pt": "Adicionado criação de objetos e leitura de enumerações",
|
|
71
|
-
"nl": "Toegevoegd objectcreatie en opsommingen lezen",
|
|
72
|
-
"fr": "Ajout de la création d'objets et de la lecture des énumérations",
|
|
73
|
-
"it": "Aggiunta creazione di oggetti e lettura di enumerazioni",
|
|
74
|
-
"es": "Añadida creación de objetos y lectura de enumeraciones.",
|
|
75
|
-
"pl": "Dodano tworzenie obiektów i odczytywanie wyliczeń",
|
|
76
|
-
"zh-cn": "添加了对象创建和枚举读取"
|
|
77
|
-
},
|
|
78
|
-
"0.3.5": {
|
|
79
|
-
"en": "Allowed the reading of current subscriptions",
|
|
80
|
-
"de": "Erlaubt das Lesen aktueller Abonnements",
|
|
81
|
-
"ru": "Разрешено чтение текущих подписок",
|
|
82
|
-
"pt": "Permitiu a leitura de assinaturas atuais",
|
|
83
|
-
"nl": "Lezen van huidige abonnementen toegestaan",
|
|
84
|
-
"fr": "Autorisé la lecture des abonnements en cours",
|
|
85
|
-
"it": "Consentita la lettura degli abbonamenti in corso",
|
|
86
|
-
"es": "Permitida la lectura de suscripciones vigentes",
|
|
87
|
-
"pl": "Umożliwił czytanie aktualnych subskrypcji",
|
|
88
|
-
"zh-cn": "允许阅读当前订阅"
|
|
89
90
|
}
|
|
90
91
|
},
|
|
91
92
|
"title": "REST API",
|
|
@@ -31,7 +31,7 @@ function findState(adapter, idOrName, user, type, callback) {
|
|
|
31
31
|
callback = type;
|
|
32
32
|
type = null;
|
|
33
33
|
}
|
|
34
|
-
adapter.findForeignObject(idOrName, type, {user
|
|
34
|
+
adapter.findForeignObject(idOrName, type, {user, checked: true, limitToOwnerRights: adapter.config.onlyAllowWhenUserIsOwner}, callback);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function getState(adapter, idOrName, user, type, callback) {
|
|
@@ -48,7 +48,7 @@ function getState(adapter, idOrName, user, type, callback) {
|
|
|
48
48
|
id = idOrName;
|
|
49
49
|
}
|
|
50
50
|
if (id) {
|
|
51
|
-
adapter.getForeignState(id, {user
|
|
51
|
+
adapter.getForeignState(id, {user, limitToOwnerRights: adapter.config.onlyAllowWhenUserIsOwner}, (err, state) => {
|
|
52
52
|
if (err || !state) {
|
|
53
53
|
state = undefined;
|
|
54
54
|
}
|
|
@@ -61,6 +61,42 @@ function getState(adapter, idOrName, user, type, callback) {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function getBinaryState(adapter, idOrName, user, type, callback) {
|
|
65
|
+
if (typeof type === 'function') {
|
|
66
|
+
callback = type;
|
|
67
|
+
type = null;
|
|
68
|
+
}
|
|
69
|
+
findState(adapter, idOrName, user, type, (err, id, originId) => {
|
|
70
|
+
if (err && (!err.message || !err.message.includes('permissionError'))) {
|
|
71
|
+
callback && callback(err, undefined, null, originId);
|
|
72
|
+
} else {
|
|
73
|
+
if (err && err.message.includes('permissionError')) {
|
|
74
|
+
// assume it is ID
|
|
75
|
+
id = idOrName;
|
|
76
|
+
}
|
|
77
|
+
if (id) {
|
|
78
|
+
if (adapter.getForeignBinaryState) {
|
|
79
|
+
adapter.getForeignBinaryState(id, {user, limitToOwnerRights: adapter.config.onlyAllowWhenUserIsOwner}, (err, binary) => {
|
|
80
|
+
if (err) {
|
|
81
|
+
binary = undefined;
|
|
82
|
+
}
|
|
83
|
+
callback && callback (err, binary, id, originId);
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
adapter.getBinaryState(id, {user, limitToOwnerRights: adapter.config.onlyAllowWhenUserIsOwner}, (err, binary) => {
|
|
87
|
+
if (err) {
|
|
88
|
+
binary = undefined;
|
|
89
|
+
}
|
|
90
|
+
callback && callback (err, binary, id, originId);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
callback && callback(null, undefined, null, originId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
64
100
|
function parseUrl(url, swagger, webExtensionPrefix) {
|
|
65
101
|
// "/v1/object/adapter.system.admin.0.alive"
|
|
66
102
|
const parts = url.split('?')[0].split('/');
|
|
@@ -108,6 +144,7 @@ module.exports = {
|
|
|
108
144
|
checkPermissions,
|
|
109
145
|
findState,
|
|
110
146
|
getState,
|
|
147
|
+
getBinaryState,
|
|
111
148
|
parseUrl,
|
|
112
149
|
errorResponse,
|
|
113
150
|
};
|
|
@@ -1,120 +1,123 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const commonLib = require('./common.js');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
noResponse = noResponse === 'true';
|
|
4
|
+
function sendTo(req, res) {
|
|
5
|
+
commonLib.checkPermissions(req._adapter, req._user, [{type: 'other', operation: 'sendto'}], async error => {
|
|
6
|
+
if (error) {
|
|
7
|
+
commonLib.errorResponse(req, res, error);
|
|
8
|
+
} else {
|
|
9
|
+
const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
|
|
10
|
+
let message = req.query.message;
|
|
11
|
+
let noResponse = req.query.noResponse;
|
|
12
|
+
let timeout = req.query.timeout;
|
|
13
|
+
let data = req.query.data;
|
|
14
|
+
if (req.body && req.body.message) {
|
|
15
|
+
message = req.query.message;
|
|
16
|
+
}
|
|
17
|
+
if (req.body && req.body.timeout) {
|
|
18
|
+
timeout = req.query.timeout;
|
|
19
|
+
}
|
|
20
|
+
timeout = parseInt(timeout, 10) || 10000;
|
|
21
|
+
if (req.body && req.body.noResponse !== undefined) {
|
|
22
|
+
noResponse = req.query.noResponse;
|
|
23
|
+
}
|
|
24
|
+
noResponse = noResponse === 'true';
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
26
|
+
if (req.body && req.body.data !== undefined) {
|
|
27
|
+
data = req.query.data;
|
|
28
|
+
} else {
|
|
29
|
+
if (data !== undefined && data !== null) {
|
|
30
|
+
if (data === 'null') {
|
|
31
|
+
data = null;
|
|
32
|
+
} else if (data === 'undefined') {
|
|
33
|
+
data = undefined;
|
|
34
|
+
} else if (data === 'true') {
|
|
35
|
+
data = true;
|
|
36
|
+
} else if (data === 'false') {
|
|
37
|
+
data = false;
|
|
38
|
+
} else if (isFinite(data)) {
|
|
39
|
+
data = parseFloat(data);
|
|
40
|
+
} else if (data.startsWith('{') && data.endsWith('}')) {
|
|
41
|
+
try {
|
|
42
|
+
data = JSON.parse(data);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// ignore
|
|
45
|
+
}
|
|
46
|
+
} else if (data.startsWith('[') && data.endsWith(']')) {
|
|
47
|
+
try {
|
|
48
|
+
data = JSON.parse(data);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
// ignore
|
|
53
51
|
}
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
|
-
|
|
54
|
+
}
|
|
55
|
+
const instance = params.instance;
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
if (!instance) {
|
|
58
|
+
res
|
|
59
|
+
.status(422)
|
|
60
|
+
.json({error: 'No instance provided'});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!message) {
|
|
64
|
+
res
|
|
65
|
+
.status(422)
|
|
66
|
+
.json({error: 'No message provided'});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
res
|
|
77
|
-
.status(500)
|
|
78
|
-
.json({error: 'instance is not online', instance});
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
70
|
+
// check if instance is alive
|
|
71
|
+
let state;
|
|
72
|
+
try {
|
|
73
|
+
state = await req._adapter.getForeignStateAsync(`system.adapter.${instance}.alive`);
|
|
74
|
+
if (!state || !state.val) {
|
|
82
75
|
res
|
|
83
76
|
.status(500)
|
|
84
|
-
.json({error: '
|
|
77
|
+
.json({error: 'instance is not online', instance});
|
|
85
78
|
return;
|
|
86
79
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
req._adapter.sendTo(instance, message, data, (result, result1) => {
|
|
104
|
-
timer && clearTimeout(timer);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
res
|
|
82
|
+
.status(500)
|
|
83
|
+
.json({error: 'invalid instance', instance});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (noResponse) {
|
|
87
|
+
req._adapter.sendTo(instance, message, data);
|
|
88
|
+
res.json({result: 'sent'});
|
|
89
|
+
} else {
|
|
90
|
+
let timer;
|
|
91
|
+
let answerDone = false;
|
|
92
|
+
if (timeout) {
|
|
93
|
+
timer = setTimeout(() => {
|
|
94
|
+
timer = null;
|
|
105
95
|
if (!answerDone) {
|
|
106
96
|
answerDone = true;
|
|
107
|
-
|
|
108
|
-
res.json(result1);
|
|
109
|
-
} else if (result && !result1) {
|
|
110
|
-
res.json(result);
|
|
111
|
-
} else {
|
|
112
|
-
res.json({error: result, result: result1});
|
|
113
|
-
}
|
|
97
|
+
res.status(408).json({error: 'timeout'});
|
|
114
98
|
}
|
|
115
|
-
});
|
|
99
|
+
}, timeout);
|
|
116
100
|
}
|
|
101
|
+
|
|
102
|
+
req._adapter.sendTo(instance, message, data, (result, result1) => {
|
|
103
|
+
timer && clearTimeout(timer);
|
|
104
|
+
if (!answerDone) {
|
|
105
|
+
answerDone = true;
|
|
106
|
+
if (!result && result1) {
|
|
107
|
+
res.json(result1);
|
|
108
|
+
} else if (result && !result1) {
|
|
109
|
+
res.json(result);
|
|
110
|
+
} else {
|
|
111
|
+
res.json({error: result, result: result1});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
117
115
|
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
sendToPost: sendTo,
|
|
122
|
+
sendTo: sendTo,
|
|
120
123
|
};
|
|
@@ -295,6 +295,101 @@ module.exports = {
|
|
|
295
295
|
});
|
|
296
296
|
},
|
|
297
297
|
|
|
298
|
+
readBinaryState: function (req, res) {
|
|
299
|
+
commonLib.checkPermissions(req._adapter, req._user, [{type: 'state', operation: 'read'}], async error => {
|
|
300
|
+
if (error) {
|
|
301
|
+
commonLib.errorResponse(req, res, error);
|
|
302
|
+
} else {
|
|
303
|
+
const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
|
|
304
|
+
const oId = getIDs(params.stateId)[0];
|
|
305
|
+
|
|
306
|
+
let result;
|
|
307
|
+
let foundID;
|
|
308
|
+
try {
|
|
309
|
+
const {binary, id, originId} = await new Promise((resolve, reject) => commonLib.getBinaryState(req._adapter, oId, req._user, (error, binary, id) =>
|
|
310
|
+
error ? reject(error) : resolve({binary, id})));
|
|
311
|
+
|
|
312
|
+
if (!id) {
|
|
313
|
+
res.status(404).json({error: 'ID not found', id: originId});
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
result = binary;
|
|
317
|
+
foundID = id;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
req._adapter.log.warn(`Cannot read ${oId}: ${error}`);
|
|
320
|
+
commonLib.errorResponse(req, res, error, {id: oId});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// try to detect popular content types
|
|
325
|
+
foundID = foundID.toLowerCase()
|
|
326
|
+
if (foundID.endsWith('.png')) {
|
|
327
|
+
res.setHeader('Content-Type', 'image/png');
|
|
328
|
+
} else if (foundID.endsWith('.jpg')) {
|
|
329
|
+
res.setHeader('Content-Type', 'image/jpeg');
|
|
330
|
+
} else if (foundID.endsWith('.svg')) {
|
|
331
|
+
res.setHeader('Content-Type', 'image/svg+xml');
|
|
332
|
+
} else if (foundID.endsWith('.txt')) {
|
|
333
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
334
|
+
} else if (foundID.endsWith('.json')) {
|
|
335
|
+
res.setHeader('Content-Type', 'application/json');
|
|
336
|
+
} else if (foundID.endsWith('.mp3')) {
|
|
337
|
+
res.setHeader('Content-Type', 'audio/mpeg');
|
|
338
|
+
} else if (foundID.endsWith('.wav')) {
|
|
339
|
+
res.setHeader('Content-Type', 'audio/wav');
|
|
340
|
+
} else if (foundID.endsWith('.ogg')) {
|
|
341
|
+
res.setHeader('Content-Type', 'audio/ogg');
|
|
342
|
+
} else {
|
|
343
|
+
res.setHeader('Content-Type', 'application/octet-stream');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
res.send(result);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
writeBinaryState: function (req, res) {
|
|
352
|
+
commonLib.checkPermissions(req._adapter, req._user, [{type: 'state', operation: 'write'}], error => {
|
|
353
|
+
if (error) {
|
|
354
|
+
commonLib.errorResponse(req, res, error);
|
|
355
|
+
} else {
|
|
356
|
+
const params = commonLib.parseUrl(req.url, req.swagger, req._adapter.WEB_EXTENSION_PREFIX);
|
|
357
|
+
const oId = getIDs(params.stateId)[0];
|
|
358
|
+
|
|
359
|
+
commonLib.findState(req._adapter, oId, req._user, async (error, id, originId) => {
|
|
360
|
+
if (error && error.message && error.message.includes('permissionError')) {
|
|
361
|
+
// assume it is ID
|
|
362
|
+
id = oId;
|
|
363
|
+
error = null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (error) {
|
|
367
|
+
commonLib.errorResponse(req, res, error, {id: oId});
|
|
368
|
+
} else if (!id) {
|
|
369
|
+
res.status(404).json({error: 'ID not found', id: oId});
|
|
370
|
+
} else {
|
|
371
|
+
try {
|
|
372
|
+
const obj = await req._adapter.getForeignObjectAsync(id, {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
|
|
373
|
+
if (obj && obj.common && obj.common.type === 'file') {
|
|
374
|
+
if (req._adapter.setForeignBinaryStateAsync) {
|
|
375
|
+
await req._adapter.setForeignBinaryStateAsync(id, req.body, {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
|
|
376
|
+
} else {
|
|
377
|
+
await req._adapter.setBinaryStateAsync(id, req.body, {user: req._user, limitToOwnerRights: req._adapter.config.onlyAllowWhenUserIsOwner});
|
|
378
|
+
}
|
|
379
|
+
res.status(200).json({result: 'ok', id});
|
|
380
|
+
} else {
|
|
381
|
+
res.status(422).json({error: 'State is not binary', id});
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
req._adapter.log.warn(`Cannot read object ${id}: ${error.toString()}`);
|
|
385
|
+
res.status(401).json({error: `Cannot read object ${id}: ${error.toString()}`, id});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
},
|
|
392
|
+
|
|
298
393
|
plainState: function (req, res) {
|
|
299
394
|
commonLib.checkPermissions(req._adapter, req._user, [{type: 'state', operation: 'read'}], async error => {
|
|
300
395
|
if (error) {
|
|
@@ -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.
|
|
5
|
+
version: "1.0.5"
|
|
6
6
|
title: "ioBroker Swagger UI"
|
|
7
7
|
contact:
|
|
8
8
|
email: "admin@iobroker.net"
|
|
@@ -16,6 +16,8 @@ basePath: "/v1"
|
|
|
16
16
|
tags:
|
|
17
17
|
- name: "state"
|
|
18
18
|
description: "Read, write the states"
|
|
19
|
+
- name: "binary"
|
|
20
|
+
description: "Read, write the binary states"
|
|
19
21
|
- name: "object"
|
|
20
22
|
description: "Read the objects"
|
|
21
23
|
- name: "history"
|
|
@@ -94,6 +96,12 @@ paths:
|
|
|
94
96
|
required: false
|
|
95
97
|
type: "integer"
|
|
96
98
|
format: "int64"
|
|
99
|
+
- in: "body"
|
|
100
|
+
required: true
|
|
101
|
+
name: "data"
|
|
102
|
+
description: "State object to write"
|
|
103
|
+
schema:
|
|
104
|
+
$ref: "#/definitions/WriteState"
|
|
97
105
|
responses:
|
|
98
106
|
200:
|
|
99
107
|
description: "successful operation"
|
|
@@ -105,6 +113,7 @@ paths:
|
|
|
105
113
|
description: "State not found"
|
|
106
114
|
405:
|
|
107
115
|
description: "Invalid value"
|
|
116
|
+
|
|
108
117
|
/state/{stateId}/toggle:
|
|
109
118
|
x-swagger-router-controller: state
|
|
110
119
|
get:
|
|
@@ -262,6 +271,63 @@ paths:
|
|
|
262
271
|
description: "State not found"
|
|
263
272
|
400:
|
|
264
273
|
description: "Invalid state ID supplied"
|
|
274
|
+
/binary/{stateId}:
|
|
275
|
+
x-swagger-router-controller: state
|
|
276
|
+
get:
|
|
277
|
+
tags:
|
|
278
|
+
- "binary"
|
|
279
|
+
summary: "Read value of the binary state as stream"
|
|
280
|
+
operationId: "readBinaryState"
|
|
281
|
+
produces:
|
|
282
|
+
- "application/octet-stream"
|
|
283
|
+
parameters:
|
|
284
|
+
- name: "stateId"
|
|
285
|
+
in: "path"
|
|
286
|
+
description: "ID of the state to return"
|
|
287
|
+
required: true
|
|
288
|
+
type: "string"
|
|
289
|
+
|
|
290
|
+
responses:
|
|
291
|
+
200:
|
|
292
|
+
description: "successful operation"
|
|
293
|
+
404:
|
|
294
|
+
description: "State not found"
|
|
295
|
+
400:
|
|
296
|
+
description: "Invalid state ID supplied"
|
|
297
|
+
patch:
|
|
298
|
+
tags:
|
|
299
|
+
- "binary"
|
|
300
|
+
summary: "Update the value of the state"
|
|
301
|
+
description: ""
|
|
302
|
+
operationId: "writeBinaryState"
|
|
303
|
+
consumes:
|
|
304
|
+
- "application/octet-stream"
|
|
305
|
+
produces:
|
|
306
|
+
- "application/json"
|
|
307
|
+
parameters:
|
|
308
|
+
- in: "path"
|
|
309
|
+
name: "stateId"
|
|
310
|
+
description: "ID of the state to write"
|
|
311
|
+
required: true
|
|
312
|
+
type: "string"
|
|
313
|
+
- in: "body"
|
|
314
|
+
required: true
|
|
315
|
+
name: "data"
|
|
316
|
+
description: "Binary data to write"
|
|
317
|
+
schema:
|
|
318
|
+
type: string
|
|
319
|
+
format: binary
|
|
320
|
+
responses:
|
|
321
|
+
200:
|
|
322
|
+
description: "successful operation"
|
|
323
|
+
schema:
|
|
324
|
+
$ref: "#/definitions/State"
|
|
325
|
+
400:
|
|
326
|
+
description: "Invalid state ID supplied"
|
|
327
|
+
404:
|
|
328
|
+
description: "State not found"
|
|
329
|
+
405:
|
|
330
|
+
description: "Invalid value"
|
|
265
331
|
/states:
|
|
266
332
|
x-swagger-router-controller: state
|
|
267
333
|
get:
|
|
@@ -456,7 +522,7 @@ paths:
|
|
|
456
522
|
- "object"
|
|
457
523
|
summary: "Delete object"
|
|
458
524
|
description: ""
|
|
459
|
-
operationId: "
|
|
525
|
+
operationId: "deleteObject"
|
|
460
526
|
produces:
|
|
461
527
|
- "application/json"
|
|
462
528
|
parameters:
|
|
@@ -559,9 +625,7 @@ paths:
|
|
|
559
625
|
200:
|
|
560
626
|
description: "successful operation"
|
|
561
627
|
schema:
|
|
562
|
-
|
|
563
|
-
items:
|
|
564
|
-
$ref: "#/definitions/Object"
|
|
628
|
+
$ref: "#/definitions/ObjectArray"
|
|
565
629
|
404:
|
|
566
630
|
description: "URL or session not found"
|
|
567
631
|
422:
|
|
@@ -905,7 +969,7 @@ paths:
|
|
|
905
969
|
- "sendTo"
|
|
906
970
|
summary: "Send message to instance as POST"
|
|
907
971
|
description: "You can send message to instance that supports it"
|
|
908
|
-
operationId: "
|
|
972
|
+
operationId: "sendToPost"
|
|
909
973
|
consumes:
|
|
910
974
|
- "application/json"
|
|
911
975
|
produces:
|
|
@@ -950,6 +1014,23 @@ paths:
|
|
|
950
1014
|
responses:
|
|
951
1015
|
200:
|
|
952
1016
|
description: "successful operation"
|
|
1017
|
+
/command/getForeignStates:
|
|
1018
|
+
get:
|
|
1019
|
+
tags:
|
|
1020
|
+
- "commands"
|
|
1021
|
+
summary: "same as getStates"
|
|
1022
|
+
produces:
|
|
1023
|
+
- "application/json"
|
|
1024
|
+
parameters:
|
|
1025
|
+
|
|
1026
|
+
- name: "pattern"
|
|
1027
|
+
in: "query"
|
|
1028
|
+
description: ""
|
|
1029
|
+
type: "string"
|
|
1030
|
+
required: true
|
|
1031
|
+
responses:
|
|
1032
|
+
200:
|
|
1033
|
+
description: "successful operation"
|
|
953
1034
|
/command/getState:
|
|
954
1035
|
get:
|
|
955
1036
|
tags:
|
|
@@ -1691,6 +1772,16 @@ paths:
|
|
|
1691
1772
|
responses:
|
|
1692
1773
|
200:
|
|
1693
1774
|
description: "successful operation"
|
|
1775
|
+
/command/getCompactSystemRepositories:
|
|
1776
|
+
get:
|
|
1777
|
+
tags:
|
|
1778
|
+
- "commands"
|
|
1779
|
+
summary: ""
|
|
1780
|
+
produces:
|
|
1781
|
+
- "application/json"
|
|
1782
|
+
responses:
|
|
1783
|
+
200:
|
|
1784
|
+
description: "successful operation"
|
|
1694
1785
|
/command/getCompactRepository:
|
|
1695
1786
|
get:
|
|
1696
1787
|
tags:
|
|
@@ -1883,23 +1974,6 @@ paths:
|
|
|
1883
1974
|
responses:
|
|
1884
1975
|
200:
|
|
1885
1976
|
description: "successful operation"
|
|
1886
|
-
/command/getForeignStates:
|
|
1887
|
-
get:
|
|
1888
|
-
tags:
|
|
1889
|
-
- "commands"
|
|
1890
|
-
summary: "same as getStates"
|
|
1891
|
-
produces:
|
|
1892
|
-
- "application/json"
|
|
1893
|
-
parameters:
|
|
1894
|
-
|
|
1895
|
-
- name: "pattern"
|
|
1896
|
-
in: "query"
|
|
1897
|
-
description: ""
|
|
1898
|
-
type: "string"
|
|
1899
|
-
required: true
|
|
1900
|
-
responses:
|
|
1901
|
-
200:
|
|
1902
|
-
description: "successful operation"
|
|
1903
1977
|
/command/delObjects:
|
|
1904
1978
|
get:
|
|
1905
1979
|
tags:
|
|
@@ -2082,7 +2156,6 @@ definitions:
|
|
|
2082
2156
|
type: "string"
|
|
2083
2157
|
description: "ID of the state"
|
|
2084
2158
|
val:
|
|
2085
|
-
type: "string"
|
|
2086
2159
|
description: "Actual value"
|
|
2087
2160
|
q:
|
|
2088
2161
|
type: "integer"
|
|
@@ -2114,6 +2187,29 @@ definitions:
|
|
|
2114
2187
|
native:
|
|
2115
2188
|
type: "object"
|
|
2116
2189
|
description: "Native state description"
|
|
2190
|
+
WriteState:
|
|
2191
|
+
type: "object"
|
|
2192
|
+
properties:
|
|
2193
|
+
val:
|
|
2194
|
+
description: "Actual value"
|
|
2195
|
+
q:
|
|
2196
|
+
type: "integer"
|
|
2197
|
+
format: "int32"
|
|
2198
|
+
description: "Quality"
|
|
2199
|
+
ts:
|
|
2200
|
+
type: "integer"
|
|
2201
|
+
format: "int64"
|
|
2202
|
+
description: "Timestamp in ms from epoch of last update"
|
|
2203
|
+
lc:
|
|
2204
|
+
type: "integer"
|
|
2205
|
+
format: "int64"
|
|
2206
|
+
description: "Timestamp in ms from epoch of last value change"
|
|
2207
|
+
ack:
|
|
2208
|
+
type: "boolean"
|
|
2209
|
+
description: "Is acknowledged. true = update, false = command"
|
|
2210
|
+
expire:
|
|
2211
|
+
type: "integer"
|
|
2212
|
+
description: "Expire in seconds"
|
|
2117
2213
|
#Type:
|
|
2118
2214
|
# type: "string"
|
|
2119
2215
|
# enum: [state, channel, device, enum, instance, adapter, host, chart, script, folder]
|
|
@@ -2133,6 +2229,10 @@ definitions:
|
|
|
2133
2229
|
native:
|
|
2134
2230
|
type: "object"
|
|
2135
2231
|
description: "Native state description"
|
|
2232
|
+
ObjectArray:
|
|
2233
|
+
type: "object"
|
|
2234
|
+
additionalProperties:
|
|
2235
|
+
$ref: "#/definitions/Object"
|
|
2136
2236
|
UrlHook:
|
|
2137
2237
|
type: "object"
|
|
2138
2238
|
properties:
|
|
@@ -2227,7 +2327,6 @@ definitions:
|
|
|
2227
2327
|
type: "object"
|
|
2228
2328
|
properties:
|
|
2229
2329
|
val:
|
|
2230
|
-
type: "number"
|
|
2231
2330
|
description: "Value"
|
|
2232
2331
|
ts:
|
|
2233
2332
|
type: "number"
|
|
@@ -2252,7 +2351,6 @@ definitions:
|
|
|
2252
2351
|
type: "object"
|
|
2253
2352
|
properties:
|
|
2254
2353
|
val:
|
|
2255
|
-
type: "number"
|
|
2256
2354
|
description: "Value"
|
|
2257
2355
|
ts:
|
|
2258
2356
|
type: "number"
|
package/lib/rest-api.js
CHANGED
|
@@ -14,7 +14,7 @@ const cors = require('cors');
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
|
|
17
|
-
const
|
|
17
|
+
const pattern2RegEx = utils.commonTools.pattern2RegEx;
|
|
18
18
|
const CommandsAdmin = require('@iobroker/socket-classes').SocketCommandsAdmin;
|
|
19
19
|
const CommandsCommon = require('@iobroker/socket-classes').SocketCommands;
|
|
20
20
|
const common = require('./common');
|
|
@@ -258,9 +258,28 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
|
|
|
258
258
|
|
|
259
259
|
// enable cors only if standalone
|
|
260
260
|
!instanceSettings && this.app.use(cors());
|
|
261
|
-
|
|
261
|
+
const jsonParser = bodyParser.json({
|
|
262
|
+
limit: '100mb',
|
|
263
|
+
});
|
|
264
|
+
const rawParser = bodyParser.raw({
|
|
265
|
+
limit: '100mb',
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
this.app.use((req, res, next) => {
|
|
269
|
+
if (req.method !== 'GET' && req.url.startsWith('/v1/binary/')) {
|
|
270
|
+
rawParser(req, res, next);
|
|
271
|
+
} else {
|
|
272
|
+
jsonParser(req, res, next);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
/*this.app.use(bodyParser.json({
|
|
262
276
|
limit: '100mb',
|
|
263
277
|
}));
|
|
278
|
+
this.app.use(bodyParser.urlencoded({
|
|
279
|
+
extended: true,
|
|
280
|
+
parameterLimit: 100000,
|
|
281
|
+
limit: '100mb',
|
|
282
|
+
}));*/
|
|
264
283
|
|
|
265
284
|
const _options = {
|
|
266
285
|
appRoot: __dirname,
|
|
@@ -514,7 +533,7 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
|
|
|
514
533
|
const item = {id, delta: options.delta, onchange: options.onchange};
|
|
515
534
|
this.subscribes[urlHash][type].push(item);
|
|
516
535
|
if (item.id.includes('*')) {
|
|
517
|
-
item.regEx = new RegExp(
|
|
536
|
+
item.regEx = new RegExp(pattern2RegEx(item.id));
|
|
518
537
|
}
|
|
519
538
|
|
|
520
539
|
if (type === 'state') {
|
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 =
|
|
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
|
|
|
@@ -137,14 +137,14 @@ function initWebServer(settings, callback) {
|
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
adapter.getPort(settings.port, port => {
|
|
140
|
+
adapter.getPort(settings.port, (!settings.bind || settings.bind === '0.0.0.0') ? undefined : settings.bind || undefined, port => {
|
|
141
141
|
if (port !== settings.port && !adapter.config.findNextPort) {
|
|
142
142
|
adapter.log.error(`port ${settings.port} already in use`);
|
|
143
143
|
process.exit(1);
|
|
144
144
|
}
|
|
145
145
|
serverPort = port;
|
|
146
146
|
|
|
147
|
-
server.server.listen(port, async () => {
|
|
147
|
+
server.server.listen(port, (!settings.bind || settings.bind === '0.0.0.0') ? undefined : settings.bind || undefined, async () => {
|
|
148
148
|
await adapter.setStateAsync('info.connection', true, true);
|
|
149
149
|
adapter.log.info(`http${settings.secure ? 's' : ''} server listening on port ${port}`);
|
|
150
150
|
serverListening = true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.rest-api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "RESTful interface for ioBroker with GUI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "bluefox",
|
|
@@ -19,27 +19,28 @@
|
|
|
19
19
|
"url": "https://github.com/ioBroker/ioBroker.rest-api"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@iobroker/adapter-core": "^2.6.
|
|
23
|
-
"@iobroker/socket-classes": "^
|
|
24
|
-
"axios": "^
|
|
25
|
-
"body-parser": "^1.20.
|
|
22
|
+
"@iobroker/adapter-core": "^2.6.7",
|
|
23
|
+
"@iobroker/socket-classes": "^1.1.5",
|
|
24
|
+
"axios": "^1.3.4",
|
|
25
|
+
"body-parser": "^1.20.2",
|
|
26
26
|
"cors": "^2.8.5",
|
|
27
|
-
"express": "^4.18.
|
|
27
|
+
"express": "^4.18.2",
|
|
28
28
|
"swagger-node-runner-fork": "^0.8.0",
|
|
29
|
-
"swagger-ui-express": "^4.
|
|
29
|
+
"swagger-ui-express": "^4.6.2",
|
|
30
30
|
"yamljs": "^0.3.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@alcalzone/release-script": "^3.5.9",
|
|
34
34
|
"@alcalzone/release-script-plugin-iobroker": "^3.5.9",
|
|
35
35
|
"@alcalzone/release-script-plugin-license": "^3.5.9",
|
|
36
|
-
"@iobroker/
|
|
37
|
-
"
|
|
36
|
+
"@iobroker/adapter-dev": "^1.2.0",
|
|
37
|
+
"@iobroker/testing": "^4.1.0",
|
|
38
|
+
"chai": "^4.3.7",
|
|
38
39
|
"eslint-plugin-eqeqeq-fix": "^1.0.3",
|
|
39
|
-
"eslint-plugin-only-warn": "^1.0
|
|
40
|
-
"eslint-plugin-react": "^7.
|
|
40
|
+
"eslint-plugin-only-warn": "^1.1.0",
|
|
41
|
+
"eslint-plugin-react": "^7.32.2",
|
|
41
42
|
"gulp": "^4.0.2",
|
|
42
|
-
"mocha": "^
|
|
43
|
+
"mocha": "^10.2.0"
|
|
43
44
|
},
|
|
44
45
|
"bugs": {
|
|
45
46
|
"url": "https://github.com/ioBroker/ioBroker.rest-api/issues"
|
|
@@ -60,7 +61,8 @@
|
|
|
60
61
|
"release": "release-script",
|
|
61
62
|
"release-patch": "release-script patch --yes --no-update-lockfile",
|
|
62
63
|
"release-minor": "release-script minor --yes --no-update-lockfile",
|
|
63
|
-
"release-major": "release-script major --yes --no-update-lockfile"
|
|
64
|
+
"release-major": "release-script major --yes --no-update-lockfile",
|
|
65
|
+
"translate": "translate-adapter"
|
|
64
66
|
},
|
|
65
67
|
"license": "Apache-2.0"
|
|
66
68
|
}
|