From 5fefc8b4fb53937e007f3ddc482ec07b9e561ba9 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Fri, 25 Nov 2022 20:58:05 -0800 Subject: [PATCH] server/sdk: add support for local addresses preference at sdk level --- plugins/cloud/src/main.ts | 2 +- sdk/package-lock.json | 4 +- sdk/package.json | 2 +- sdk/types/package-lock.json | 4 +- sdk/types/package.json | 2 +- .../scrypted_python/scrypted_sdk/types.py | 2 +- sdk/types/src/types.input.ts | 4 +- server/package-lock.json | 26 +++---- server/package.json | 2 +- server/src/plugin/plugin-remote.ts | 76 ++++++++++++++++--- server/src/runtime.ts | 16 +++- server/src/services/addresses.ts | 19 +++++ 12 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 server/src/services/addresses.ts diff --git a/plugins/cloud/src/main.ts b/plugins/cloud/src/main.ts index 09afd805a..7bfcc2b42 100644 --- a/plugins/cloud/src/main.ts +++ b/plugins/cloud/src/main.ts @@ -210,7 +210,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings, } } - getDevice(nativeId: string) { + async getDevice(nativeId: string) { return this.push; } diff --git a/sdk/package-lock.json b/sdk/package-lock.json index d3045f939..74af95de6 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/sdk", - "version": "0.2.14", + "version": "0.2.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/sdk", - "version": "0.2.14", + "version": "0.2.21", "license": "ISC", "dependencies": { "@babel/preset-typescript": "^7.16.7", diff --git a/sdk/package.json b/sdk/package.json index 3896d62db..3878334ac 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sdk", - "version": "0.2.14", + "version": "0.2.21", "description": "", "main": "dist/src/index.js", "exports": { diff --git a/sdk/types/package-lock.json b/sdk/types/package-lock.json index 945e9b485..1f84a1caf 100644 --- a/sdk/types/package-lock.json +++ b/sdk/types/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/types", - "version": "0.2.11", + "version": "0.2.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/types", - "version": "0.2.11", + "version": "0.2.18", "license": "ISC", "devDependencies": { "@types/rimraf": "^3.0.2", diff --git a/sdk/types/package.json b/sdk/types/package.json index 663795ee3..dfd84002d 100644 --- a/sdk/types/package.json +++ b/sdk/types/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/types", - "version": "0.2.11", + "version": "0.2.18", "description": "", "main": "dist/index.js", "author": "", diff --git a/sdk/types/scrypted_python/scrypted_sdk/types.py b/sdk/types/scrypted_python/scrypted_sdk/types.py index 66e27df7f..860f3f1a3 100644 --- a/sdk/types/scrypted_python/scrypted_sdk/types.py +++ b/sdk/types/scrypted_python/scrypted_sdk/types.py @@ -1155,7 +1155,7 @@ class EndpointManager: pass async def getInsecurePublicLocalEndpoint(self, nativeId: str = None) -> str: pass - def getLocalAddresses(self) -> list[str]: + async def getLocalAddresses(self) -> list[str]: pass async def getLocalEndpoint(self, nativeId: str = None, options: Any = None) -> str: pass diff --git a/sdk/types/src/types.input.ts b/sdk/types/src/types.input.ts index 7eb5e5b9c..eb13a219a 100644 --- a/sdk/types/src/types.input.ts +++ b/sdk/types/src/types.input.ts @@ -1404,12 +1404,12 @@ export interface EndpointManager { * Set the recommended local addresses used by Scrypted plugins that listen for incoming connections. * @param addresses */ - setLocalAddresses(...addresses: string[]): Promise; + setLocalAddresses(addresses: string[]): Promise; /** * Get the recommended local addresess used by Scrypted plugins that listen for incoming connections. */ - getLocalAddresses(): Promise[]; + getLocalAddresses(): Promise; } /** * SystemManager is used by scripts to query device state and access devices. diff --git a/server/package-lock.json b/server/package-lock.json index ce85d0809..effe85f25 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@ffmpeg-installer/ffmpeg": "^1.1.0", "@mapbox/node-pre-gyp": "^1.0.10", - "@scrypted/types": "^0.2.8", + "@scrypted/types": "^0.2.18", "adm-zip": "^0.5.9", "axios": "^0.21.4", "body-parser": "^1.19.0", @@ -245,9 +245,9 @@ } }, "node_modules/@scrypted/types": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.8.tgz", - "integrity": "sha512-wH28nmRzsflZPNoBK/Oyog6w6Wx0KEw4S8AZFBz7DreQZ4km8sqa72L2muiZIsZmt76OcsGm2RoRMGZP6EHoCA==" + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.18.tgz", + "integrity": "sha512-LEhdAgpWZbVMDt74zM/jqBQr42xQl4fDaGwAGtwz0XJ1xnx/hXBXVLs+SdP+Gtnoujjqd1kWTdZCDrnPP4/luw==" }, "node_modules/@tootallnate/once": { "version": "1.1.2", @@ -1084,9 +1084,9 @@ } }, "node_modules/engine.io": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", - "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -3281,9 +3281,9 @@ } }, "@scrypted/types": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.8.tgz", - "integrity": "sha512-wH28nmRzsflZPNoBK/Oyog6w6Wx0KEw4S8AZFBz7DreQZ4km8sqa72L2muiZIsZmt76OcsGm2RoRMGZP6EHoCA==" + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.18.tgz", + "integrity": "sha512-LEhdAgpWZbVMDt74zM/jqBQr42xQl4fDaGwAGtwz0XJ1xnx/hXBXVLs+SdP+Gtnoujjqd1kWTdZCDrnPP4/luw==" }, "@tootallnate/once": { "version": "1.1.2", @@ -3979,9 +3979,9 @@ } }, "engine.io": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", - "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", "requires": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", diff --git a/server/package.json b/server/package.json index 2d1c1659d..358358c28 100644 --- a/server/package.json +++ b/server/package.json @@ -5,7 +5,7 @@ "dependencies": { "@ffmpeg-installer/ffmpeg": "^1.1.0", "@mapbox/node-pre-gyp": "^1.0.10", - "@scrypted/types": "^0.2.8", + "@scrypted/types": "^0.2.18", "adm-zip": "^0.5.9", "axios": "^0.21.4", "body-parser": "^1.19.0", diff --git a/server/src/plugin/plugin-remote.ts b/server/src/plugin/plugin-remote.ts index a56173f73..c8d28d897 100644 --- a/server/src/plugin/plugin-remote.ts +++ b/server/src/plugin/plugin-remote.ts @@ -78,26 +78,82 @@ class EndpointManagerImpl implements EndpointManager { return ip?.includes(':') ? `[${ip}]` : ip; } + /** + * @deprecated + */ async getAuthenticatedPath(nativeId?: ScryptedNativeId): Promise { - return `/endpoint/${this.getEndpoint(nativeId)}/`; + return this.getPath(nativeId); } + + /** + * @deprecated + */ async getInsecurePublicLocalEndpoint(nativeId?: ScryptedNativeId): Promise { - return `http://${await this.getUrlSafeIp()}:${await this.api.getComponent('SCRYPTED_INSECURE_PORT')}/endpoint/${this.getEndpoint(nativeId)}/public/`; + return this.getLocalEndpoint(nativeId, { + insecure: true, + public: true, + }) } + + /** + * @deprecated + */ async getPublicCloudEndpoint(nativeId?: ScryptedNativeId): Promise { - const local = await this.getPublicLocalEndpoint(nativeId); - const mo = await this.mediaManager.createMediaObject(Buffer.from(local), ScryptedMimeTypes.LocalUrl); - return this.mediaManager.convertMediaObjectToUrl(mo, ScryptedMimeTypes.LocalUrl); + return this.getCloudEndpoint(nativeId, { + public: true, + }); } + + /** + * @deprecated + */ async getPublicLocalEndpoint(nativeId?: ScryptedNativeId): Promise { - return `https://${await this.getUrlSafeIp()}:${await this.api.getComponent('SCRYPTED_SECURE_PORT')}/endpoint/${this.getEndpoint(nativeId)}/public/`; + return this.getLocalEndpoint(nativeId, { + public: true, + }) } + + /** + * @deprecated + */ async getPublicPushEndpoint(nativeId?: ScryptedNativeId): Promise { const mo = await this.mediaManager.createMediaObject(Buffer.from(this.getEndpoint(nativeId)), ScryptedMimeTypes.PushEndpoint); return this.mediaManager.convertMediaObjectToUrl(mo, ScryptedMimeTypes.PushEndpoint); } - async deliverPush(endpoint: string, request: HttpRequest) { - return this.api.deliverPush(endpoint, request); + + async deliverPush(id: string, request: HttpRequest) { + return this.api.deliverPush(id, request); + } + + async getPath(nativeId?: string, options?: { public?: boolean; }): Promise { + return `/endpoint/${this.getEndpoint(nativeId)}/${options?.public ? '' : 'public/'}` + } + + async getLocalEndpoint(nativeId?: string, options?: { public?: boolean; insecure?: boolean; }): Promise { + const protocol = options?.insecure ? 'http' : 'https'; + const port = await this.api.getComponent(options?.insecure ? 'SCRYPTED_INSECURE_PORT' : 'SCRYPTED_INSECURE_PORT'); + return `${protocol}://${await this.getUrlSafeIp()}:${port}${this.getPath(nativeId, options)}`; + } + + async getCloudEndpoint(nativeId?: string, options?: { public?: boolean; }): Promise { + const local = await this.getLocalEndpoint(nativeId, options); + const mo = await this.mediaManager.createMediaObject(Buffer.from(local), ScryptedMimeTypes.LocalUrl); + return this.mediaManager.convertMediaObjectToUrl(mo, ScryptedMimeTypes.LocalUrl); + } + + async getCloudPushEndpoint(nativeId?: string): Promise { + const mo = await this.mediaManager.createMediaObject(Buffer.from(this.getEndpoint(nativeId)), ScryptedMimeTypes.PushEndpoint); + return this.mediaManager.convertMediaObjectToUrl(mo, ScryptedMimeTypes.PushEndpoint); + } + + async setLocalAddresses(addresses: string[]): Promise { + const addressSettings = await this.api.getComponent('addresses'); + return addressSettings.setLocalAddresses(addresses); + } + + async getLocalAddresses(): Promise { + const addressSettings = await this.api.getComponent('addresses'); + return await addressSettings.getLocalAddresses() as string[]; } } @@ -326,10 +382,10 @@ export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId: } if (eventDetails.property) { - remote.notify(id, eventDetails.eventTime, eventDetails.eventInterface, eventDetails.property, getSystemState()[id]?.[eventDetails.property], eventDetails.changed).catch(() => {}); + remote.notify(id, eventDetails.eventTime, eventDetails.eventInterface, eventDetails.property, getSystemState()[id]?.[eventDetails.property], eventDetails.changed).catch(() => { }); } else { - remote.notify(id, eventDetails.eventTime, eventDetails.eventInterface, eventDetails.property, eventData, eventDetails.changed).catch(() => {}); + remote.notify(id, eventDetails.eventTime, eventDetails.eventInterface, eventDetails.property, eventData, eventDetails.changed).catch(() => { }); } }); diff --git a/server/src/runtime.ts b/server/src/runtime.ts index 53e71aea4..ac1dae3da 100644 --- a/server/src/runtime.ts +++ b/server/src/runtime.ts @@ -28,6 +28,7 @@ import { isConnectionUpgrade, PluginHttp } from './plugin/plugin-http'; import { WebSocketConnection } from './plugin/plugin-remote-websocket'; import { getPluginVolume } from './plugin/plugin-volume'; import { getIpAddress, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings'; +import { AddressSettigns as AddressSettings } from './services/addresses'; import { Alerts } from './services/alerts'; import { CORSControl, CORSServer } from './services/cors'; import { Info } from './services/info'; @@ -70,6 +71,11 @@ export class ScryptedRuntime extends PluginHttp { }, }); cors: CORSServer[] = []; + pluginComponent = new PluginComponent(this); + servieControl = new ServiceControl(this); + alerts = new Alerts(this); + corsControl = new CORSControl(this); + addressSettings = new AddressSettings(this); constructor(datastore: Level, insecure: http.Server, secure: https.Server, app: express.Application) { super(app); @@ -353,15 +359,17 @@ export class ScryptedRuntime extends PluginHttp { case 'info': return new Info(); case 'plugins': - return new PluginComponent(this); + return this.pluginComponent; case 'service-control': - return new ServiceControl(this); + return this.servieControl; case 'logger': return this.logger; case 'alerts': - return new Alerts(this); + return this.alerts; case 'cors': - return new CORSControl(this); + return this.corsControl; + case 'addresses': + return this.addressSettings; } } diff --git a/server/src/services/addresses.ts b/server/src/services/addresses.ts new file mode 100644 index 000000000..0c1a29218 --- /dev/null +++ b/server/src/services/addresses.ts @@ -0,0 +1,19 @@ +import { Settings } from "../db-types"; +import { ScryptedRuntime } from "../runtime"; + +export class AddressSettigns { + constructor(public scrypted: ScryptedRuntime) { + } + + async setLocalAddresses(addresses: string[]) { + const localAddresses = new Settings(); + localAddresses._id = 'localAddresses'; + localAddresses.value = addresses; + await this.scrypted.datastore.upsert(localAddresses); + } + + async getLocalAddresses(): Promise { + const settings = await this.scrypted.datastore.tryGet(Settings, 'localAddresses'); + return settings.value as string[]; + } +}