@scrypted/server 0.0.105 → 0.0.109
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.
Potentially problematic release.
This version of @scrypted/server might be problematic. Click here for more details.
- package/dist/http-interfaces.js +2 -2
- package/dist/http-interfaces.js.map +1 -1
- package/dist/plugin/listen-zero.js +13 -1
- package/dist/plugin/listen-zero.js.map +1 -1
- package/dist/plugin/plugin-device.js +11 -2
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host-api.js +8 -7
- package/dist/plugin/plugin-host-api.js.map +1 -1
- package/dist/plugin/plugin-host.js +36 -87
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-http.js +100 -0
- package/dist/plugin/plugin-http.js.map +1 -0
- package/dist/plugin/plugin-lazy-remote.js +73 -0
- package/dist/plugin/plugin-lazy-remote.js.map +1 -0
- package/dist/plugin/plugin-npm-dependencies.js +16 -16
- package/dist/plugin/plugin-npm-dependencies.js.map +1 -1
- package/dist/plugin/plugin-remote-websocket.js +40 -34
- package/dist/plugin/plugin-remote-websocket.js.map +1 -1
- package/dist/plugin/plugin-remote.js +35 -26
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/rpc.js +53 -26
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.js +45 -111
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-main.js +3 -0
- package/dist/scrypted-main.js.map +1 -1
- package/dist/services/plugin.js +2 -2
- package/dist/services/plugin.js.map +1 -1
- package/dist/state.js +2 -8
- package/dist/state.js.map +1 -1
- package/package.json +2 -2
- package/python/plugin-remote.py +4 -4
- package/python/rpc.py +68 -26
- package/src/http-interfaces.ts +3 -3
- package/src/plugin/listen-zero.ts +13 -0
- package/src/plugin/plugin-api.ts +1 -1
- package/src/plugin/plugin-device.ts +11 -2
- package/src/plugin/plugin-host-api.ts +9 -8
- package/src/plugin/plugin-host.ts +40 -95
- package/src/plugin/plugin-http.ts +117 -0
- package/src/plugin/plugin-lazy-remote.ts +70 -0
- package/src/plugin/plugin-npm-dependencies.ts +19 -18
- package/src/plugin/plugin-remote-websocket.ts +55 -60
- package/src/plugin/plugin-remote.ts +45 -38
- package/src/rpc.ts +62 -25
- package/src/runtime.ts +55 -128
- package/src/scrypted-main.ts +4 -0
- package/src/services/plugin.ts +2 -2
- package/src/state.ts +2 -10
package/src/rpc.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import vm from 'vm';
|
|
2
2
|
|
|
3
|
+
const finalizerIdSymbol = Symbol('rpcFinalizerId');
|
|
4
|
+
|
|
3
5
|
function getDefaultTransportSafeArgumentTypes() {
|
|
4
6
|
const jsonSerializable = new Set<string>();
|
|
5
7
|
jsonSerializable.add(Number.name);
|
|
@@ -40,6 +42,7 @@ interface RpcOob extends RpcMessage {
|
|
|
40
42
|
|
|
41
43
|
interface RpcRemoteProxyValue {
|
|
42
44
|
__remote_proxy_id: string;
|
|
45
|
+
__remote_proxy_finalizer_id: string;
|
|
43
46
|
__remote_constructor_name: string;
|
|
44
47
|
__remote_proxy_props: any;
|
|
45
48
|
__remote_proxy_oneway_methods: string[];
|
|
@@ -52,6 +55,7 @@ interface RpcLocalProxyValue {
|
|
|
52
55
|
|
|
53
56
|
interface RpcFinalize extends RpcMessage {
|
|
54
57
|
__local_proxy_id: string;
|
|
58
|
+
__local_proxy_finalizer_id: string;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
interface Deferred {
|
|
@@ -78,20 +82,17 @@ export const PROPERTY_PROXY_PROPERTIES = '__proxy_props';
|
|
|
78
82
|
export const PROPERTY_JSON_COPY_SERIALIZE_CHILDREN = '__json_copy_serialize_children';
|
|
79
83
|
|
|
80
84
|
class RpcProxy implements ProxyHandler<any> {
|
|
85
|
+
|
|
81
86
|
constructor(public peer: RpcPeer,
|
|
82
|
-
public
|
|
87
|
+
public entry: LocalProxiedEntry,
|
|
83
88
|
public constructorName: string,
|
|
84
89
|
public proxyProps: any,
|
|
85
90
|
public proxyOneWayMethods: string[]) {
|
|
86
|
-
this.peer = peer;
|
|
87
|
-
this.id = id;
|
|
88
|
-
this.constructorName = constructorName;
|
|
89
|
-
this.proxyProps = proxyProps;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
get(target: any, p: PropertyKey, receiver: any): any {
|
|
93
94
|
if (p === '__proxy_id')
|
|
94
|
-
return this.id;
|
|
95
|
+
return this.entry.id;
|
|
95
96
|
if (p === '__proxy_constructor')
|
|
96
97
|
return this.constructorName;
|
|
97
98
|
if (p === '__proxy_peer')
|
|
@@ -114,6 +115,12 @@ class RpcProxy implements ProxyHandler<any> {
|
|
|
114
115
|
return new Proxy(() => p, this);
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
set(target: any, p: string | symbol, value: any, receiver: any): boolean {
|
|
119
|
+
if (p === finalizerIdSymbol)
|
|
120
|
+
this.entry.finalizerId = value;
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
apply(target: any, thisArg: any, argArray?: any): any {
|
|
118
125
|
const method = target();
|
|
119
126
|
const args: any[] = [];
|
|
@@ -124,7 +131,7 @@ class RpcProxy implements ProxyHandler<any> {
|
|
|
124
131
|
const rpcApply: RpcApply = {
|
|
125
132
|
type: "apply",
|
|
126
133
|
id: undefined,
|
|
127
|
-
proxyId: this.id,
|
|
134
|
+
proxyId: this.entry.id,
|
|
128
135
|
args,
|
|
129
136
|
method,
|
|
130
137
|
};
|
|
@@ -187,16 +194,21 @@ export interface RpcSerializer {
|
|
|
187
194
|
deserialize(serialized: any): any;
|
|
188
195
|
}
|
|
189
196
|
|
|
197
|
+
interface LocalProxiedEntry {
|
|
198
|
+
id: string;
|
|
199
|
+
finalizerId: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
190
202
|
export class RpcPeer {
|
|
191
203
|
idCounter = 1;
|
|
192
204
|
onOob: (oob: any) => void;
|
|
193
205
|
params: { [name: string]: any } = {};
|
|
194
206
|
pendingResults: { [id: string]: Deferred } = {};
|
|
195
207
|
proxyCounter = 1;
|
|
196
|
-
localProxied = new Map<any,
|
|
208
|
+
localProxied = new Map<any, LocalProxiedEntry>();
|
|
197
209
|
localProxyMap: { [id: string]: any } = {};
|
|
198
210
|
remoteWeakProxies: { [id: string]: WeakRef<any> } = {};
|
|
199
|
-
finalizers = new FinalizationRegistry(
|
|
211
|
+
finalizers = new FinalizationRegistry(entry => this.finalize(entry as LocalProxiedEntry));
|
|
200
212
|
nameDeserializerMap = new Map<string, RpcSerializer>();
|
|
201
213
|
constructorSerializerMap = new Map<string, string>();
|
|
202
214
|
transportSafeArgumentTypes = getDefaultTransportSafeArgumentTypes();
|
|
@@ -238,10 +250,11 @@ export class RpcPeer {
|
|
|
238
250
|
this.constructorSerializerMap.set(ctr, name);
|
|
239
251
|
}
|
|
240
252
|
|
|
241
|
-
finalize(
|
|
242
|
-
delete this.remoteWeakProxies[id];
|
|
253
|
+
finalize(entry: LocalProxiedEntry) {
|
|
254
|
+
delete this.remoteWeakProxies[entry.id];
|
|
243
255
|
const rpcFinalize: RpcFinalize = {
|
|
244
|
-
__local_proxy_id: id,
|
|
256
|
+
__local_proxy_id: entry.id,
|
|
257
|
+
__local_proxy_finalizer_id: entry.finalizerId,
|
|
245
258
|
type: 'finalize',
|
|
246
259
|
}
|
|
247
260
|
this.send(rpcFinalize);
|
|
@@ -294,9 +307,12 @@ export class RpcPeer {
|
|
|
294
307
|
return ret;
|
|
295
308
|
}
|
|
296
309
|
|
|
297
|
-
const { __remote_proxy_id, __local_proxy_id, __remote_constructor_name, __serialized_value, __remote_proxy_props, __remote_proxy_oneway_methods } = value;
|
|
310
|
+
const { __remote_proxy_id, __remote_proxy_finalizer_id, __local_proxy_id, __remote_constructor_name, __serialized_value, __remote_proxy_props, __remote_proxy_oneway_methods } = value;
|
|
298
311
|
if (__remote_proxy_id) {
|
|
299
|
-
|
|
312
|
+
let proxy = this.remoteWeakProxies[__remote_proxy_id]?.deref();
|
|
313
|
+
if (!proxy)
|
|
314
|
+
proxy = this.newProxy(__remote_proxy_id, __remote_constructor_name, __remote_proxy_props, __remote_proxy_oneway_methods);
|
|
315
|
+
proxy[finalizerIdSymbol] = __remote_proxy_finalizer_id;
|
|
300
316
|
return proxy;
|
|
301
317
|
}
|
|
302
318
|
|
|
@@ -329,10 +345,13 @@ export class RpcPeer {
|
|
|
329
345
|
|
|
330
346
|
let __remote_constructor_name = value.__proxy_constructor || value.constructor?.name?.toString();
|
|
331
347
|
|
|
332
|
-
let
|
|
333
|
-
if (
|
|
348
|
+
let proxiedEntry = this.localProxied.get(value);
|
|
349
|
+
if (proxiedEntry) {
|
|
350
|
+
const __remote_proxy_finalizer_id = (this.proxyCounter++).toString();
|
|
351
|
+
proxiedEntry.finalizerId = __remote_proxy_finalizer_id;
|
|
334
352
|
const ret: RpcRemoteProxyValue = {
|
|
335
|
-
__remote_proxy_id:
|
|
353
|
+
__remote_proxy_id: proxiedEntry.id,
|
|
354
|
+
__remote_proxy_finalizer_id,
|
|
336
355
|
__remote_constructor_name,
|
|
337
356
|
__remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
|
|
338
357
|
__remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
|
|
@@ -355,6 +374,7 @@ export class RpcPeer {
|
|
|
355
374
|
const serialized = serializer.serialize(value);
|
|
356
375
|
const ret: RpcRemoteProxyValue = {
|
|
357
376
|
__remote_proxy_id: undefined,
|
|
377
|
+
__remote_proxy_finalizer_id: undefined,
|
|
358
378
|
__remote_constructor_name,
|
|
359
379
|
__remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
|
|
360
380
|
__remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
|
|
@@ -363,12 +383,17 @@ export class RpcPeer {
|
|
|
363
383
|
return ret;
|
|
364
384
|
}
|
|
365
385
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
386
|
+
const __remote_proxy_id = (this.proxyCounter++).toString();
|
|
387
|
+
proxiedEntry = {
|
|
388
|
+
id: __remote_proxy_id,
|
|
389
|
+
finalizerId: __remote_proxy_id,
|
|
390
|
+
};
|
|
391
|
+
this.localProxied.set(value, proxiedEntry);
|
|
392
|
+
this.localProxyMap[__remote_proxy_id] = value;
|
|
369
393
|
|
|
370
394
|
const ret: RpcRemoteProxyValue = {
|
|
371
|
-
__remote_proxy_id
|
|
395
|
+
__remote_proxy_id,
|
|
396
|
+
__remote_proxy_finalizer_id: __remote_proxy_id,
|
|
372
397
|
__remote_constructor_name,
|
|
373
398
|
__remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
|
|
374
399
|
__remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
|
|
@@ -378,12 +403,16 @@ export class RpcPeer {
|
|
|
378
403
|
}
|
|
379
404
|
|
|
380
405
|
newProxy(proxyId: string, proxyConstructorName: string, proxyProps: any, proxyOneWayMethods: string[]) {
|
|
381
|
-
const
|
|
406
|
+
const localProxiedEntry: LocalProxiedEntry = {
|
|
407
|
+
id: proxyId,
|
|
408
|
+
finalizerId: undefined,
|
|
409
|
+
}
|
|
410
|
+
const rpc = new RpcProxy(this, localProxiedEntry, proxyConstructorName, proxyProps, proxyOneWayMethods);
|
|
382
411
|
const target = proxyConstructorName === 'Function' || proxyConstructorName === 'AsyncFunction' ? function () { } : rpc;
|
|
383
412
|
const proxy = new Proxy(target, rpc);
|
|
384
413
|
const weakref = new WeakRef(proxy);
|
|
385
414
|
this.remoteWeakProxies[proxyId] = weakref;
|
|
386
|
-
this.finalizers.register(rpc,
|
|
415
|
+
this.finalizers.register(rpc, localProxiedEntry);
|
|
387
416
|
global.gc?.();
|
|
388
417
|
return proxy;
|
|
389
418
|
}
|
|
@@ -460,8 +489,16 @@ export class RpcPeer {
|
|
|
460
489
|
case 'finalize': {
|
|
461
490
|
const rpcFinalize = message as RpcFinalize;
|
|
462
491
|
const local = this.localProxyMap[rpcFinalize.__local_proxy_id];
|
|
463
|
-
|
|
464
|
-
|
|
492
|
+
if (local) {
|
|
493
|
+
const localProxiedEntry = this.localProxied.get(local);
|
|
494
|
+
// if a finalizer id is specified, it must match.
|
|
495
|
+
if (rpcFinalize.__local_proxy_finalizer_id && rpcFinalize.__local_proxy_finalizer_id !== localProxiedEntry?.finalizerId) {
|
|
496
|
+
console.error(this.selfName, this.peerName, 'finalizer mismatch')
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
delete this.localProxyMap[rpcFinalize.__local_proxy_id];
|
|
500
|
+
this.localProxied.delete(local);
|
|
501
|
+
}
|
|
465
502
|
break;
|
|
466
503
|
}
|
|
467
504
|
case 'oob': {
|
package/src/runtime.ts
CHANGED
|
@@ -4,9 +4,8 @@ import { ScryptedNativeId, Device, EngineIOHandler, HttpRequest, HttpRequestHand
|
|
|
4
4
|
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
|
|
5
5
|
import { Plugin, PluginDevice, ScryptedAlert } from './db-types';
|
|
6
6
|
import { getState, ScryptedStateManager, setState } from './state';
|
|
7
|
-
import { Request, Response
|
|
7
|
+
import { Request, Response } from 'express';
|
|
8
8
|
import { createResponseInterface } from './http-interfaces';
|
|
9
|
-
import bodyParser from 'body-parser';
|
|
10
9
|
import http, { ServerResponse } from 'http';
|
|
11
10
|
import https from 'https';
|
|
12
11
|
import express from 'express';
|
|
@@ -15,7 +14,7 @@ import { getDisplayName, getDisplayRoom, getDisplayType, getProvidedNameOrDefaul
|
|
|
15
14
|
import { URL } from "url";
|
|
16
15
|
import qs from "query-string";
|
|
17
16
|
import { PluginComponent } from './services/plugin';
|
|
18
|
-
import { Server as WebSocketServer } from "ws";
|
|
17
|
+
import WebSocket, { Server as WebSocketServer } from "ws";
|
|
19
18
|
import axios from 'axios';
|
|
20
19
|
import tar from 'tar';
|
|
21
20
|
import { once } from 'events';
|
|
@@ -30,6 +29,7 @@ import io from 'engine.io';
|
|
|
30
29
|
import { spawn as ptySpawn } from 'node-pty';
|
|
31
30
|
import rimraf from 'rimraf';
|
|
32
31
|
import { getPluginVolume } from './plugin/plugin-volume';
|
|
32
|
+
import { PluginHttp } from './plugin/plugin-http';
|
|
33
33
|
|
|
34
34
|
interface DeviceProxyPair {
|
|
35
35
|
handler: PluginDeviceProxyHandler;
|
|
@@ -39,13 +39,17 @@ interface DeviceProxyPair {
|
|
|
39
39
|
const MIN_SCRYPTED_CORE_VERSION = 'v0.0.146';
|
|
40
40
|
const PLUGIN_DEVICE_STATE_VERSION = 2;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
interface HttpPluginData {
|
|
43
|
+
pluginHost: PluginHost;
|
|
44
|
+
pluginDevice: PluginDevice
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
43
48
|
datastore: Level;
|
|
44
49
|
plugins: { [id: string]: PluginHost } = {};
|
|
45
50
|
pluginDevices: { [id: string]: PluginDevice } = {};
|
|
46
51
|
devices: { [id: string]: DeviceProxyPair } = {};
|
|
47
52
|
stateManager = new ScryptedStateManager(this);
|
|
48
|
-
app: Router;
|
|
49
53
|
logger = new Logger(this, '', 'Scrypted');
|
|
50
54
|
devicesLogger = this.logger.getLogger('device', 'Devices');
|
|
51
55
|
wss = new WebSocketServer({ noServer: true });
|
|
@@ -55,30 +59,12 @@ export class ScryptedRuntime {
|
|
|
55
59
|
});
|
|
56
60
|
|
|
57
61
|
constructor(datastore: Level, insecure: http.Server, secure: https.Server, app: express.Application) {
|
|
62
|
+
super(app);
|
|
58
63
|
this.datastore = datastore;
|
|
59
64
|
this.app = app;
|
|
60
65
|
|
|
61
66
|
app.disable('x-powered-by');
|
|
62
67
|
|
|
63
|
-
app.all(['/endpoint/@:owner/:pkg/public/engine.io/*', '/endpoint/:pkg/public/engine.io/*'], (req, res) => {
|
|
64
|
-
this.endpointHandler(req, res, true, true, this.handleEngineIOEndpoint.bind(this))
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
app.all(['/endpoint/@:owner/:pkg/engine.io/*', '/endpoint/@:owner/:pkg/engine.io/*'], (req, res) => {
|
|
68
|
-
this.endpointHandler(req, res, false, true, this.handleEngineIOEndpoint.bind(this))
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// stringify all http endpoints
|
|
72
|
-
app.all(['/endpoint/@:owner/:pkg/public', '/endpoint/@:owner/:pkg/public/*', '/endpoint/:pkg', '/endpoint/:pkg/*'], bodyParser.text() as any);
|
|
73
|
-
|
|
74
|
-
app.all(['/endpoint/@:owner/:pkg/public', '/endpoint/@:owner/:pkg/public/*', '/endpoint/:pkg/public', '/endpoint/:pkg/public/*'], (req, res) => {
|
|
75
|
-
this.endpointHandler(req, res, true, false, this.handleRequestEndpoint.bind(this))
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
app.all(['/endpoint/@:owner/:pkg', '/endpoint/@:owner/:pkg/*', '/endpoint/:pkg', '/endpoint/:pkg/*'], (req, res) => {
|
|
79
|
-
this.endpointHandler(req, res, false, false, this.handleRequestEndpoint.bind(this))
|
|
80
|
-
});
|
|
81
|
-
|
|
82
68
|
app.get('/web/oauth/callback', (req, res) => {
|
|
83
69
|
this.oauthCallback(req, res);
|
|
84
70
|
});
|
|
@@ -190,7 +176,7 @@ export class ScryptedRuntime {
|
|
|
190
176
|
}
|
|
191
177
|
}
|
|
192
178
|
|
|
193
|
-
async getPluginForEndpoint(endpoint: string) {
|
|
179
|
+
async getPluginForEndpoint(endpoint: string): Promise<HttpPluginData> {
|
|
194
180
|
let pluginHost = this.plugins[endpoint] ?? this.getPluginHostForDeviceId(endpoint);
|
|
195
181
|
if (endpoint === '@scrypted/core') {
|
|
196
182
|
// enforce a minimum version on @scrypted/core
|
|
@@ -254,110 +240,55 @@ export class ScryptedRuntime {
|
|
|
254
240
|
this.shellio.handleRequest(req, res);
|
|
255
241
|
}
|
|
256
242
|
|
|
257
|
-
async
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const isUpgrade = req.headers.connection?.toLowerCase() === 'upgrade';
|
|
261
|
-
|
|
262
|
-
const end = (code: number, message: string) => {
|
|
263
|
-
if (isUpgrade) {
|
|
264
|
-
const socket = res.socket;
|
|
265
|
-
socket.write(`HTTP/1.1 ${code} ${message}\r\n` +
|
|
266
|
-
'\r\n');
|
|
267
|
-
socket.destroy();
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
res.status(code);
|
|
271
|
-
res.send(message);
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
if (!isPublicEndpoint && !res.locals.username) {
|
|
276
|
-
end(401, 'Not Authorized');
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const { owner, pkg } = req.params;
|
|
281
|
-
let endpoint = pkg;
|
|
282
|
-
if (owner)
|
|
283
|
-
endpoint = `@${owner}/${endpoint}`;
|
|
284
|
-
|
|
285
|
-
const { pluginHost, pluginDevice } = await this.getPluginForEndpoint(endpoint);
|
|
243
|
+
async getEndpointPluginData(endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<HttpPluginData> {
|
|
244
|
+
const ret = await this.getPluginForEndpoint(endpoint);
|
|
245
|
+
const { pluginDevice } = ret;
|
|
286
246
|
|
|
287
247
|
// check if upgrade requests can be handled. must be websocket.
|
|
288
248
|
if (isUpgrade) {
|
|
289
|
-
if (
|
|
290
|
-
end(404, 'Not Found');
|
|
249
|
+
if (!pluginDevice?.state.interfaces.value.includes(ScryptedInterface.EngineIOHandler)) {
|
|
291
250
|
return;
|
|
292
251
|
}
|
|
293
252
|
}
|
|
294
253
|
else {
|
|
295
254
|
if (!isEngineIOEndpoint && !pluginDevice?.state.interfaces.value.includes(ScryptedInterface.HttpRequestHandler)) {
|
|
296
|
-
end(404, 'Not Found');
|
|
297
255
|
return;
|
|
298
256
|
}
|
|
299
257
|
}
|
|
300
258
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
rootPath += '/public'
|
|
304
|
-
|
|
305
|
-
const body = req.body && typeof req.body !== 'string' ? JSON.stringify(req.body) : req.body;
|
|
259
|
+
return ret;
|
|
260
|
+
}
|
|
306
261
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
headers: req.headers,
|
|
310
|
-
method: req.method,
|
|
311
|
-
rootPath,
|
|
312
|
-
url: req.url,
|
|
313
|
-
isPublicEndpoint,
|
|
314
|
-
username: res.locals.username,
|
|
315
|
-
};
|
|
262
|
+
async handleWebSocket(endpoint: string, httpRequest: HttpRequest, ws: WebSocket, pluginData: HttpPluginData): Promise<void> {
|
|
263
|
+
const { pluginDevice } = pluginData;
|
|
316
264
|
|
|
317
|
-
|
|
318
|
-
|
|
265
|
+
const handler = this.getDevice<EngineIOHandler>(pluginDevice._id);
|
|
266
|
+
const id = 'ws-' + this.wsAtomic++;
|
|
267
|
+
const pluginHost = this.plugins[endpoint] ?? this.getPluginHostForDeviceId(endpoint);
|
|
268
|
+
if (!pluginHost) {
|
|
269
|
+
ws.close();
|
|
270
|
+
return;
|
|
319
271
|
}
|
|
272
|
+
pluginHost.ws[id] = ws;
|
|
320
273
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
ws.on('close', async (reason) => {
|
|
342
|
-
try {
|
|
343
|
-
pluginHost.remote.ioEvent(id, 'close');
|
|
344
|
-
}
|
|
345
|
-
catch (e) {
|
|
346
|
-
}
|
|
347
|
-
delete pluginHost.ws[id];
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
await handler.onConnection(httpRequest, `ws://${id}`);
|
|
351
|
-
}
|
|
352
|
-
catch (e) {
|
|
353
|
-
console.error('websocket plugin error', e);
|
|
354
|
-
ws.close();
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
handler(req, res, httpRequest, pluginHost, pluginDevice);
|
|
360
|
-
}
|
|
274
|
+
ws.on('message', async (message) => {
|
|
275
|
+
try {
|
|
276
|
+
pluginHost.remote.ioEvent(id, 'message', message)
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
ws.close();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
ws.on('close', async (reason) => {
|
|
283
|
+
try {
|
|
284
|
+
pluginHost.remote.ioEvent(id, 'close');
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
}
|
|
288
|
+
delete pluginHost.ws[id];
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
await handler.onConnection(httpRequest, `ws://${id}`);
|
|
361
292
|
}
|
|
362
293
|
|
|
363
294
|
async getComponent(componentId: string): Promise<any> {
|
|
@@ -381,7 +312,9 @@ export class ScryptedRuntime {
|
|
|
381
312
|
}
|
|
382
313
|
}
|
|
383
314
|
|
|
384
|
-
async handleEngineIOEndpoint(req: Request, res: ServerResponse, endpointRequest: HttpRequest,
|
|
315
|
+
async handleEngineIOEndpoint(req: Request, res: ServerResponse, endpointRequest: HttpRequest, pluginData: HttpPluginData) {
|
|
316
|
+
const { pluginHost, pluginDevice } = pluginData;
|
|
317
|
+
|
|
385
318
|
(req as any).scrypted = {
|
|
386
319
|
endpointRequest,
|
|
387
320
|
pluginDevice,
|
|
@@ -392,21 +325,15 @@ export class ScryptedRuntime {
|
|
|
392
325
|
pluginHost.io.handleRequest(req, res);
|
|
393
326
|
}
|
|
394
327
|
|
|
395
|
-
async handleRequestEndpoint(req: Request, res: Response, endpointRequest: HttpRequest,
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
handler.onRequest(endpointRequest, createResponseInterface(res, pluginHost));
|
|
404
|
-
}
|
|
405
|
-
catch (e) {
|
|
406
|
-
res.status(500);
|
|
407
|
-
res.send(e.toString());
|
|
408
|
-
console.error(e);
|
|
328
|
+
async handleRequestEndpoint(req: Request, res: Response, endpointRequest: HttpRequest, pluginData: HttpPluginData) {
|
|
329
|
+
const { pluginHost, pluginDevice } = pluginData;
|
|
330
|
+
const handler = this.getDevice<HttpRequestHandler>(pluginDevice._id);
|
|
331
|
+
if (handler.interfaces.includes(ScryptedInterface.EngineIOHandler) && req.headers.connection === 'upgrade' && req.headers.upgrade?.toLowerCase() === 'websocket') {
|
|
332
|
+
this.wss.handleUpgrade(req, req.socket, null, ws => {
|
|
333
|
+
console.log(ws);
|
|
334
|
+
});
|
|
409
335
|
}
|
|
336
|
+
handler.onRequest(endpointRequest, createResponseInterface(res, pluginHost.zip));
|
|
410
337
|
}
|
|
411
338
|
|
|
412
339
|
killPlugin(plugin: Plugin) {
|
package/src/scrypted-main.ts
CHANGED
|
@@ -116,6 +116,10 @@ else {
|
|
|
116
116
|
const db = level(dbPath);
|
|
117
117
|
await db.open();
|
|
118
118
|
|
|
119
|
+
if (process.env.SCRYPTED_RESET_ALL_USERS === 'true') {
|
|
120
|
+
await db.removeAll(ScryptedUser);
|
|
121
|
+
}
|
|
122
|
+
|
|
119
123
|
let certSetting = await db.tryGet(Settings, 'certificate') as Settings;
|
|
120
124
|
|
|
121
125
|
if (certSetting?.value?.version !== CURRENT_SELF_SIGNED_CERTIFICATE_VERSION) {
|
package/src/services/plugin.ts
CHANGED
|
@@ -120,7 +120,7 @@ export class PluginComponent {
|
|
|
120
120
|
console.log('done updating plugins');
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
async getRemoteServicePort(pluginId: string, name: string): Promise<number> {
|
|
123
|
+
async getRemoteServicePort(pluginId: string, name: string, ...args: any[]): Promise<number> {
|
|
124
124
|
if (name === 'console') {
|
|
125
125
|
const consoleServer = await this.scrypted.plugins[pluginId].consoleServer;
|
|
126
126
|
return consoleServer.readPort;
|
|
@@ -129,6 +129,6 @@ export class PluginComponent {
|
|
|
129
129
|
const consoleServer = await this.scrypted.plugins[pluginId].consoleServer;
|
|
130
130
|
return consoleServer.writePort;
|
|
131
131
|
}
|
|
132
|
-
return this.scrypted.plugins[pluginId].remote.getServicePort(name);
|
|
132
|
+
return this.scrypted.plugins[pluginId].remote.getServicePort(name, ...args);
|
|
133
133
|
}
|
|
134
134
|
}
|
package/src/state.ts
CHANGED
|
@@ -72,23 +72,15 @@ export class ScryptedStateManager extends EventRegistry {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
updateDescriptor(device: PluginDevice) {
|
|
75
|
-
|
|
76
|
-
plugin.remote?.updateDeviceState(device._id, device.state);
|
|
77
|
-
}
|
|
75
|
+
this.notify(device._id, undefined, ScryptedInterface.ScryptedDevice, undefined, device.state, true);
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
removeDevice(id: string) {
|
|
81
|
-
for (const plugin of Object.values(this.scrypted.plugins)) {
|
|
82
|
-
plugin.remote?.updateDeviceState(id, undefined);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
79
|
this.notify(undefined, undefined, ScryptedInterface.ScryptedDevice, ScryptedInterfaceProperty.id, id, true);
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
notifyInterfaceEvent(device: PluginDevice, eventInterface: ScryptedInterface | string, value: any) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.notify(device?._id, eventTime, eventInterface, undefined, value, true);
|
|
83
|
+
this.notify(device?._id, Date.now(), eventInterface, undefined, value, true);
|
|
92
84
|
}
|
|
93
85
|
|
|
94
86
|
setState(id: string, property: string, value: any) {
|