@scrypted/server 0.0.107 → 0.0.108

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.

Files changed (38) hide show
  1. package/dist/http-interfaces.js +2 -2
  2. package/dist/http-interfaces.js.map +1 -1
  3. package/dist/plugin/listen-zero.js +13 -1
  4. package/dist/plugin/listen-zero.js.map +1 -1
  5. package/dist/plugin/plugin-host-api.js +2 -2
  6. package/dist/plugin/plugin-host-api.js.map +1 -1
  7. package/dist/plugin/plugin-host.js +36 -87
  8. package/dist/plugin/plugin-host.js.map +1 -1
  9. package/dist/plugin/plugin-http.js +100 -0
  10. package/dist/plugin/plugin-http.js.map +1 -0
  11. package/dist/plugin/plugin-lazy-remote.js +73 -0
  12. package/dist/plugin/plugin-lazy-remote.js.map +1 -0
  13. package/dist/plugin/plugin-remote-websocket.js +40 -34
  14. package/dist/plugin/plugin-remote-websocket.js.map +1 -1
  15. package/dist/plugin/plugin-remote.js +35 -26
  16. package/dist/plugin/plugin-remote.js.map +1 -1
  17. package/dist/runtime.js +45 -111
  18. package/dist/runtime.js.map +1 -1
  19. package/dist/scrypted-main.js +3 -0
  20. package/dist/scrypted-main.js.map +1 -1
  21. package/dist/services/plugin.js +2 -2
  22. package/dist/services/plugin.js.map +1 -1
  23. package/dist/state.js +2 -8
  24. package/dist/state.js.map +1 -1
  25. package/package.json +2 -2
  26. package/src/http-interfaces.ts +3 -3
  27. package/src/plugin/listen-zero.ts +13 -0
  28. package/src/plugin/plugin-api.ts +1 -1
  29. package/src/plugin/plugin-host-api.ts +2 -3
  30. package/src/plugin/plugin-host.ts +40 -95
  31. package/src/plugin/plugin-http.ts +117 -0
  32. package/src/plugin/plugin-lazy-remote.ts +70 -0
  33. package/src/plugin/plugin-remote-websocket.ts +55 -60
  34. package/src/plugin/plugin-remote.ts +45 -38
  35. package/src/runtime.ts +55 -128
  36. package/src/scrypted-main.ts +4 -0
  37. package/src/services/plugin.ts +2 -2
  38. package/src/state.ts +2 -10
@@ -0,0 +1,70 @@
1
+ import { ScryptedNativeId, SystemDeviceState } from '@scrypted/sdk/types'
2
+ import { PluginRemote, PluginRemoteLoadZipOptions } from './plugin-api';
3
+
4
+ /**
5
+ * This remote is necessary as the host needs to create a remote synchronously
6
+ * in the constructor and immediately begin queueing commands.
7
+ * Warning: do not await in any of these methods unless necessary, otherwise
8
+ * execution order of state reporting may fail.
9
+ */
10
+ export class LazyRemote implements PluginRemote {
11
+ remote: PluginRemote;
12
+
13
+ constructor(public remotePromise: Promise<PluginRemote>, public remoteReadyPromise: Promise<PluginRemote>) {
14
+ this.remoteReadyPromise = (async () => {
15
+ this.remote = await remoteReadyPromise;
16
+ return this.remote;
17
+ })();
18
+ }
19
+
20
+ async loadZip(packageJson: any, zipData: Buffer, options?: PluginRemoteLoadZipOptions): Promise<any> {
21
+ if (!this.remote)
22
+ await this.remoteReadyPromise;
23
+ return this.remote.loadZip(packageJson, zipData, options);
24
+ }
25
+ async setSystemState(state: { [id: string]: { [property: string]: SystemDeviceState; }; }): Promise<void> {
26
+ if (!this.remote)
27
+ await this.remoteReadyPromise;
28
+ return this.remote.setSystemState(state);
29
+ }
30
+ async setNativeId(nativeId: ScryptedNativeId, id: string, storage: { [key: string]: any; }): Promise<void> {
31
+ if (!this.remote)
32
+ await this.remoteReadyPromise;
33
+ return this.remote.setNativeId(nativeId, id, storage);
34
+ }
35
+ async updateDeviceState(id: string, state: { [property: string]: SystemDeviceState; }): Promise<void> {
36
+ try {
37
+ if (!this.remote)
38
+ await this.remoteReadyPromise;
39
+ }
40
+ catch (e) {
41
+ return;
42
+ }
43
+ return this.remote.updateDeviceState(id, state);
44
+ }
45
+ async notify(id: string, eventTime: number, eventInterface: string, property: string, propertyState: SystemDeviceState, changed?: boolean): Promise<void> {
46
+ try {
47
+ if (!this.remote)
48
+ await this.remoteReadyPromise;
49
+ }
50
+ catch (e) {
51
+ return;
52
+ }
53
+ return this.remote.notify(id, eventTime, eventInterface, property, propertyState, changed);
54
+ }
55
+ async ioEvent(id: string, event: string, message?: any): Promise<void> {
56
+ if (!this.remote)
57
+ await this.remoteReadyPromise;
58
+ return this.remote.ioEvent(id, event, message);
59
+ }
60
+ async createDeviceState(id: string, setState: (property: string, value: any) => Promise<void>): Promise<any> {
61
+ if (!this.remote)
62
+ await this.remoteReadyPromise;
63
+ return this.remote.createDeviceState(id, setState);
64
+ }
65
+
66
+ async getServicePort(name: string, ...args: any[]): Promise<number> {
67
+ const remote = await this.remotePromise;
68
+ return remote.getServicePort(name, ...args);
69
+ }
70
+ }
@@ -2,7 +2,7 @@ interface WebSocketEvent {
2
2
  type: string;
3
3
  reason?: string;
4
4
  message?: string;
5
- data?: string|ArrayBufferLike;
5
+ data?: string | ArrayBufferLike;
6
6
  source?: any;
7
7
  }
8
8
 
@@ -12,7 +12,7 @@ interface WebSocketEventListener {
12
12
 
13
13
  // @ts-ignore
14
14
  class WebSocketEventTarget {
15
- events: { [type: string]: WebSocketEventListener[]} = {};
15
+ events: { [type: string]: WebSocketEventListener[] } = {};
16
16
 
17
17
  dispatchEvent(event: WebSocketEvent) {
18
18
  const list = this.events[event.type];
@@ -53,32 +53,20 @@ function defineEventAttribute(p: any, type: string) {
53
53
  });
54
54
  }
55
55
 
56
- interface WebSocketEndCallback {
57
- (): void;
56
+ export interface WebSocketConnectCallbacks {
57
+ connect(e: Error, ws: WebSocketMethods): void;
58
+ end(): void;
59
+ error(e: Error): void;
60
+ data(data: string | ArrayBufferLike): void;
58
61
  }
59
62
 
60
- interface WebSocketErrorCallback {
61
- (e: Error): void;
63
+ export interface WebSocketConnect {
64
+ (url: string, protocols: string[], callbacks: WebSocketConnectCallbacks): void;
62
65
  }
63
66
 
64
- interface WebSocketDataCallback {
65
- (data: string | ArrayBufferLike): void;
66
- }
67
-
68
- interface WebSocketSend {
69
- (message: string|ArrayBufferLike): void;
70
- }
71
-
72
- interface WebSocketConnectCallback {
73
- (e: Error, ws: any, send: WebSocketSend): void;
74
- }
75
-
76
- interface WebSocketConnect {
77
- (url: string, protocols: string[],
78
- connect: WebSocketConnectCallback,
79
- end: WebSocketEndCallback,
80
- error: WebSocketErrorCallback,
81
- data: WebSocketDataCallback): void;
67
+ export interface WebSocketMethods {
68
+ send(message: string | ArrayBufferLike): void;
69
+ close(message: string): void;
82
70
  }
83
71
 
84
72
  export function createWebSocketClass(__websocketConnect: WebSocketConnect) {
@@ -88,8 +76,7 @@ export function createWebSocketClass(__websocketConnect: WebSocketConnect) {
88
76
  _url: string;
89
77
  _protocols: string[];
90
78
  readyState: number;
91
- send: (message: string|ArrayBufferLike) => void;
92
- _ws: any;
79
+ _ws: WebSocketMethods;
93
80
 
94
81
  constructor(url: string, protocols?: string[]) {
95
82
  super();
@@ -97,44 +84,52 @@ export function createWebSocketClass(__websocketConnect: WebSocketConnect) {
97
84
  this._protocols = protocols;
98
85
  this.readyState = 0;
99
86
 
100
- __websocketConnect(url, protocols, (e, ws, send) => {
101
- // connect
102
- if (e != null) {
87
+ __websocketConnect(url, protocols, {
88
+ connect: (e, ws) => {
89
+ // connect
90
+ if (e != null) {
91
+ this.dispatchEvent({
92
+ type: 'error',
93
+ message: e.toString(),
94
+ });
95
+ return;
96
+ }
97
+
98
+ this._ws = ws;
99
+ this.readyState = 1;
100
+ this.dispatchEvent({
101
+ type: 'open',
102
+ });
103
+ },
104
+ end: () => {
105
+ // end
106
+ this.readyState = 3;
107
+ this.dispatchEvent({
108
+ type: 'close',
109
+ reason: 'closed',
110
+ });
111
+ },
112
+ error: (e: Error) => {
113
+ // error
114
+ this.readyState = 3;
103
115
  this.dispatchEvent({
104
116
  type: 'error',
105
117
  message: e.toString(),
106
118
  });
107
- return;
119
+ },
120
+ data: (data: string | ArrayBufferLike) => {
121
+ // data
122
+ this.dispatchEvent({
123
+ type: 'message',
124
+ data: data,
125
+ source: this,
126
+ });
108
127
  }
128
+ })
129
+ }
109
130
 
110
- this._ws = ws;
111
- this.send = send;
112
- this.readyState = 1;
113
- this.dispatchEvent({
114
- type: 'open',
115
- });
116
- }, () => {
117
- // end
118
- this.readyState = 3;
119
- this.dispatchEvent({
120
- type: 'close',
121
- reason: 'closed',
122
- });
123
- }, (e: Error) => {
124
- // error
125
- this.readyState = 3;
126
- this.dispatchEvent({
127
- type: 'error',
128
- message: e.toString(),
129
- });
130
- }, (data: string | ArrayBufferLike) => {
131
- // data
132
- this.dispatchEvent({
133
- type: 'message',
134
- data: data,
135
- source: this,
136
- });
137
- });
131
+ send(message: string | ArrayBufferLike) {
132
+ this._ws.send(message);
138
133
  }
139
134
 
140
135
  get url() {
@@ -145,8 +140,8 @@ export function createWebSocketClass(__websocketConnect: WebSocketConnect) {
145
140
  return "";
146
141
  }
147
142
 
148
- close() {
149
- this._ws.close();
143
+ close(reason: string) {
144
+ this._ws.close(reason);
150
145
  }
151
146
  }
152
147
 
@@ -6,8 +6,7 @@ import { PluginAPI, PluginLogger, PluginRemote, PluginRemoteLoadZipOptions } fro
6
6
  import { SystemManagerImpl } from './system';
7
7
  import { RpcPeer, RPCResultError, PROPERTY_PROXY_ONEWAY_METHODS, PROPERTY_JSON_DISABLE_SERIALIZATION } from '../rpc';
8
8
  import { BufferSerializer } from './buffer-serializer';
9
- import { EventEmitter } from 'events';
10
- import { createWebSocketClass } from './plugin-remote-websocket';
9
+ import { createWebSocketClass, WebSocketConnectCallbacks, WebSocketMethods } from './plugin-remote-websocket';
11
10
 
12
11
  class DeviceLogger implements Logger {
13
12
  nativeId: ScryptedNativeId;
@@ -262,14 +261,7 @@ class StorageImpl implements Storage {
262
261
  }
263
262
  }
264
263
 
265
- interface WebSocketCallbacks {
266
- end: any;
267
- error: any;
268
- data: any;
269
- }
270
-
271
-
272
- export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId: string): Promise<PluginRemote> {
264
+ export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId: string, getSystemState: () => { [id: string]: { [property: string]: SystemDeviceState } }): Promise<PluginRemote> {
273
265
  try {
274
266
  // the host/remote connection can be from server to plugin (node to node),
275
267
  // core plugin to web (node to browser).
@@ -277,16 +269,46 @@ export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId:
277
269
  // but in plugin-host, mark Buffer as transport safe.
278
270
  peer.addSerializer(Buffer, 'Buffer', new BufferSerializer());
279
271
  const getRemote = await peer.getParam('getRemote');
280
- return await getRemote(api, pluginId);
272
+ const remote = await getRemote(api, pluginId);
273
+
274
+ await remote.setSystemState(getSystemState());
275
+ api.listen((id, eventDetails, eventData) => {
276
+ // ScryptedDevice events will be handled specially and repropagated by the remote.
277
+ if (eventDetails.eventInterface === ScryptedInterface.ScryptedDevice) {
278
+ if (eventDetails.property === ScryptedInterfaceProperty.id) {
279
+ // a change on the id property means device was deleted
280
+ remote.updateDeviceState(eventData, undefined);
281
+ }
282
+ else {
283
+ // a change on anything else is a descriptor update
284
+ remote.updateDeviceState(id, getSystemState()[id]);
285
+ }
286
+ return;
287
+ }
288
+
289
+ if (eventDetails.property) {
290
+ remote.notify(id, eventDetails.eventTime, eventDetails.eventInterface, eventDetails.property, getSystemState()[id]?.[eventDetails.property], eventDetails.changed);
291
+ }
292
+ else {
293
+ remote.notify(id, eventDetails.eventTime, eventDetails.eventInterface, eventDetails.property, eventData, eventDetails.changed);
294
+ }
295
+ });
296
+
297
+ return remote;
281
298
  }
282
299
  catch (e) {
283
300
  throw new RPCResultError(peer, 'error while retrieving PluginRemote', e);
284
301
  }
285
302
  }
286
303
 
304
+ export interface WebSocketCustomHandler {
305
+ id: string,
306
+ methods: WebSocketMethods;
307
+ }
308
+
287
309
  export interface PluginRemoteAttachOptions {
288
310
  createMediaManager?: (systemManager: SystemManager) => Promise<MediaManager>;
289
- getServicePort?: (name: string) => Promise<number>;
311
+ getServicePort?: (name: string, ...args: any[]) => Promise<number>;
290
312
  getDeviceConsole?: (nativeId?: ScryptedNativeId) => Console;
291
313
  getPluginConsole?: () => Console;
292
314
  getMixinConsole?: (id: string, nativeId?: ScryptedNativeId) => Console;
@@ -309,8 +331,9 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
309
331
  const systemManager = new SystemManagerImpl();
310
332
  const deviceManager = new DeviceManagerImpl(systemManager, getDeviceConsole, getMixinConsole);
311
333
  const endpointManager = new EndpointManagerImpl();
312
- const ioSockets: { [id: string]: WebSocketCallbacks } = {};
313
334
  const mediaManager = await api.getMediaManager() || await createMediaManager(systemManager);
335
+ peer.params['mediaManager'] = mediaManager;
336
+ const ioSockets: { [id: string]: WebSocketConnectCallbacks } = {};
314
337
 
315
338
  systemManager.api = api;
316
339
  deviceManager.api = api;
@@ -383,11 +406,11 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
383
406
  async updateDeviceState(id: string, state: { [property: string]: SystemDeviceState }) {
384
407
  if (!state) {
385
408
  delete systemManager.state[id];
386
- systemManager.events.notify(id, Date.now(), ScryptedInterface.ScryptedDevice, ScryptedInterfaceProperty.id, id, true);
409
+ systemManager.events.notify(undefined, undefined, ScryptedInterface.ScryptedDevice, ScryptedInterfaceProperty.id, id, true);
387
410
  }
388
411
  else {
389
412
  systemManager.state[id] = state;
390
- systemManager.events.notify(id, Date.now(), ScryptedInterface.ScryptedDevice, undefined, undefined, true);
413
+ systemManager.events.notify(id, undefined, ScryptedInterface.ScryptedDevice, undefined, state, true);
391
414
  }
392
415
  },
393
416
 
@@ -432,32 +455,16 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
432
455
  volume.writeFileSync(name, entry.getData());
433
456
  }
434
457
 
435
- function websocketConnect(url: string, protocols: any, connect: any, end: any, error: any, data: any) {
436
- if (url.startsWith('io://')) {
437
- const id = url.substring('io://'.length);
438
-
439
- ioSockets[id] = {
440
- data,
441
- error,
442
- end
443
- };
444
-
445
- connect(undefined, {
446
- close: () => api.ioClose(id),
447
- }, (message: string) => api.ioSend(id, message));
448
- }
449
- else if (url.startsWith('ws://')) {
450
- const id = url.substring('ws://'.length);
458
+ function websocketConnect(url: string, protocols: any, callbacks: WebSocketConnectCallbacks) {
459
+ if (url.startsWith('io://') || url.startsWith('ws://')) {
460
+ const id = url.substring('xx://'.length);
451
461
 
452
- ioSockets[id] = {
453
- data,
454
- error,
455
- end
456
- };
462
+ ioSockets[id] = callbacks;
457
463
 
458
- connect(undefined, {
464
+ callbacks.connect(undefined, {
459
465
  close: () => api.ioClose(id),
460
- }, (message: string) => api.ioSend(id, message));
466
+ send: (message: string) => api.ioSend(id, message),
467
+ });
461
468
  }
462
469
  else {
463
470
  throw new Error('unsupported websocket');
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, Router } from 'express';
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
- export class ScryptedRuntime {
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 endpointHandler(req: Request, res: Response, isPublicEndpoint: boolean, isEngineIOEndpoint: boolean,
258
- handler: (req: Request, res: Response, endpointRequest: HttpRequest, pluginHost: PluginHost, pluginDevice: PluginDevice) => void) {
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 (req.headers.upgrade?.toLowerCase() !== 'websocket' || !pluginDevice?.state.interfaces.value.includes(ScryptedInterface.EngineIOHandler)) {
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
- let rootPath = `/endpoint/${endpoint}`;
302
- if (isPublicEndpoint)
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
- const httpRequest: HttpRequest = {
308
- body,
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
- if (isEngineIOEndpoint && !isUpgrade && isPublicEndpoint) {
318
- res.header("Access-Control-Allow-Origin", '*');
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
- if (!isEngineIOEndpoint && isUpgrade) {
322
- this.wss.handleUpgrade(req, req.socket, (req as any).upgradeHead, async (ws) => {
323
- try {
324
- const handler = this.getDevice<EngineIOHandler>(pluginDevice._id);
325
- const id = 'ws-' + this.wsAtomic++;
326
- const pluginHost = this.plugins[endpoint] ?? this.getPluginHostForDeviceId(endpoint);
327
- if (!pluginHost) {
328
- ws.close();
329
- return;
330
- }
331
- pluginHost.ws[id] = ws;
332
-
333
- ws.on('message', async (message) => {
334
- try {
335
- pluginHost.remote.ioEvent(id, 'message', message)
336
- }
337
- catch (e) {
338
- ws.close();
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, pluginHost: PluginHost, pluginDevice: PluginDevice) {
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, pluginHost: PluginHost, pluginDevice: PluginDevice) {
396
- try {
397
- const handler = this.getDevice<HttpRequestHandler>(pluginDevice._id);
398
- if (handler.interfaces.includes(ScryptedInterface.EngineIOHandler) && req.headers.connection === 'upgrade' && req.headers.upgrade?.toLowerCase() === 'websocket') {
399
- this.wss.handleUpgrade(req, req.socket, null, ws => {
400
- console.log(ws);
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) {
@@ -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) {
@@ -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
  }