@scrypted/server 0.1.14 → 0.2.1
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/event-registry.js +3 -4
- package/dist/event-registry.js.map +1 -1
- package/dist/http-interfaces.js +11 -0
- package/dist/http-interfaces.js.map +1 -1
- package/dist/plugin/media.js +78 -67
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-api.js +1 -1
- package/dist/plugin/plugin-api.js.map +1 -1
- package/dist/plugin/plugin-device.js +25 -11
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host-api.js.map +1 -1
- package/dist/plugin/plugin-host.js +11 -6
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-http.js +1 -1
- package/dist/plugin/plugin-http.js.map +1 -1
- package/dist/plugin/plugin-remote-worker.js +170 -17
- package/dist/plugin/plugin-remote-worker.js.map +1 -1
- package/dist/plugin/plugin-remote.js +25 -85
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/plugin/runtime/node-fork-worker.js +11 -3
- package/dist/plugin/runtime/node-fork-worker.js.map +1 -1
- package/dist/plugin/socket-serializer.js +17 -0
- package/dist/plugin/socket-serializer.js.map +1 -0
- package/dist/rpc-serializer.js +23 -10
- package/dist/rpc-serializer.js.map +1 -1
- package/dist/rpc.js +3 -3
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.js +36 -33
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-plugin-main.js +4 -1
- package/dist/scrypted-plugin-main.js.map +1 -1
- package/dist/scrypted-server-main.js +53 -12
- package/dist/scrypted-server-main.js.map +1 -1
- package/dist/server-settings.js +5 -1
- package/dist/server-settings.js.map +1 -1
- package/dist/state.js +2 -1
- package/dist/state.js.map +1 -1
- package/package.json +5 -12
- package/src/event-registry.ts +3 -4
- package/src/http-interfaces.ts +13 -0
- package/src/plugin/media.ts +94 -75
- package/src/plugin/plugin-api.ts +5 -4
- package/src/plugin/plugin-device.ts +25 -11
- package/src/plugin/plugin-host-api.ts +1 -1
- package/src/plugin/plugin-host.ts +6 -5
- package/src/plugin/plugin-http.ts +2 -2
- package/src/plugin/plugin-remote-worker.ts +211 -23
- package/src/plugin/plugin-remote.ts +31 -95
- package/src/plugin/runtime/node-fork-worker.ts +11 -3
- package/src/plugin/runtime/runtime-worker.ts +1 -1
- package/src/plugin/socket-serializer.ts +15 -0
- package/src/rpc-serializer.ts +30 -13
- package/src/rpc.ts +3 -2
- package/src/runtime.ts +37 -38
- package/src/scrypted-plugin-main.ts +4 -1
- package/src/scrypted-server-main.ts +59 -13
- package/src/state.ts +2 -1
package/src/rpc-serializer.ts
CHANGED
@@ -3,7 +3,7 @@ import { SidebandBufferSerializer } from "./plugin/buffer-serializer";
|
|
3
3
|
import { RpcPeer } from "./rpc";
|
4
4
|
|
5
5
|
export function createDuplexRpcPeer(selfName: string, peerName: string, readable: Readable, writable: Writable) {
|
6
|
-
const serializer = createRpcDuplexSerializer(
|
6
|
+
const serializer = createRpcDuplexSerializer(writable);
|
7
7
|
|
8
8
|
const rpcPeer = new RpcPeer(selfName, peerName, (message, reject, serializationContext) => {
|
9
9
|
try {
|
@@ -16,6 +16,7 @@ export function createDuplexRpcPeer(selfName: string, peerName: string, readable
|
|
16
16
|
});
|
17
17
|
|
18
18
|
serializer.setupRpcPeer(rpcPeer);
|
19
|
+
readable.on('data', data => serializer.onData(data));
|
19
20
|
readable.on('close', serializer.onDisconnected);
|
20
21
|
readable.on('error', serializer.onDisconnected);
|
21
22
|
return rpcPeer;
|
@@ -34,7 +35,7 @@ export function createRpcSerializer(options: {
|
|
34
35
|
rpcPeer.kill('connection closed.');
|
35
36
|
}
|
36
37
|
|
37
|
-
const sendMessage = (message: any, reject: (e: Error) => void, serializationContext: any,
|
38
|
+
const sendMessage = (message: any, reject: (e: Error) => void, serializationContext: any,) => {
|
38
39
|
if (!connected) {
|
39
40
|
reject?.(new Error('peer disconnected'));
|
40
41
|
return;
|
@@ -78,7 +79,9 @@ export function createRpcSerializer(options: {
|
|
78
79
|
};
|
79
80
|
}
|
80
81
|
|
81
|
-
export function createRpcDuplexSerializer(
|
82
|
+
export function createRpcDuplexSerializer(writable: {
|
83
|
+
write: (data: Buffer) => void;
|
84
|
+
}) {
|
82
85
|
const socketSend = (type: number, data: Buffer) => {
|
83
86
|
const header = Buffer.alloc(5);
|
84
87
|
header.writeUInt32BE(data.length + 1, 0);
|
@@ -102,38 +105,52 @@ export function createRpcDuplexSerializer(readable: Readable, writable: Writable
|
|
102
105
|
});
|
103
106
|
|
104
107
|
let header: Buffer;
|
105
|
-
|
108
|
+
let pending: Buffer;
|
109
|
+
|
110
|
+
const readPending = (length: number) => {
|
111
|
+
if (!pending || pending.length < length)
|
112
|
+
return;
|
113
|
+
|
114
|
+
const ret = pending.slice(0, length);
|
115
|
+
pending = pending.slice(length);
|
116
|
+
if (!pending.length)
|
117
|
+
pending = undefined;
|
118
|
+
return ret;
|
119
|
+
}
|
120
|
+
|
121
|
+
const onData = (data: Buffer) => {
|
122
|
+
if (!pending)
|
123
|
+
pending = data;
|
124
|
+
else
|
125
|
+
pending = Buffer.concat([pending, data]);
|
126
|
+
|
106
127
|
while (true) {
|
107
128
|
if (!header) {
|
108
|
-
header =
|
129
|
+
header = readPending(5);
|
109
130
|
if (!header)
|
110
131
|
return;
|
111
132
|
}
|
112
133
|
|
113
134
|
const length = header.readUInt32BE(0);
|
114
135
|
const type = header.readUInt8(4);
|
115
|
-
const payload: Buffer =
|
136
|
+
const payload: Buffer = readPending(length - 1);
|
116
137
|
if (!payload)
|
117
138
|
return;
|
118
139
|
|
119
140
|
header = undefined;
|
120
141
|
|
121
|
-
const data = payload;
|
122
|
-
|
123
142
|
if (type === 0) {
|
124
|
-
const message = JSON.parse(
|
143
|
+
const message = JSON.parse(payload.toString());
|
125
144
|
serializer.onMessageFinish(message);
|
126
145
|
}
|
127
146
|
else {
|
128
|
-
serializer.onMessageBuffer(
|
147
|
+
serializer.onMessageBuffer(payload);
|
129
148
|
}
|
130
149
|
}
|
131
150
|
}
|
132
151
|
|
133
|
-
readable.on('readable', readMessages);
|
134
|
-
readMessages();
|
135
|
-
|
136
152
|
return {
|
153
|
+
onData,
|
137
154
|
setupRpcPeer: serializer.setupRpcPeer,
|
138
155
|
sendMessage: serializer.sendMessage,
|
139
156
|
onDisconnected: serializer.onDisconnected,
|
package/src/rpc.ts
CHANGED
@@ -138,7 +138,7 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
|
|
138
138
|
|
139
139
|
if (this.proxyOneWayMethods?.includes?.(method)) {
|
140
140
|
rpcApply.oneway = true;
|
141
|
-
this.peer.send(rpcApply);
|
141
|
+
this.peer.send(rpcApply, undefined, serializationContext);
|
142
142
|
return Promise.resolve();
|
143
143
|
}
|
144
144
|
|
@@ -321,7 +321,8 @@ export class RpcPeer {
|
|
321
321
|
const params = Object.assign({}, this.params, coercedParams);
|
322
322
|
let compile: CompileFunction;
|
323
323
|
try {
|
324
|
-
|
324
|
+
// prevent bundlers from trying to include non-existent vm module.
|
325
|
+
compile = module[`require`]('vm').compileFunction;
|
325
326
|
}
|
326
327
|
catch (e) {
|
327
328
|
compile = compileFunction;
|
package/src/runtime.ts
CHANGED
@@ -1,39 +1,37 @@
|
|
1
|
-
import {
|
2
|
-
import
|
3
|
-
import { ScryptedNativeId, Device, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty, DeviceInformation } from '@scrypted/types';
|
4
|
-
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
|
5
|
-
import { Plugin, PluginDevice, ScryptedAlert } from './db-types';
|
6
|
-
import { getState, ScryptedStateManager, setState } from './state';
|
7
|
-
import { Request, Response } from 'express';
|
8
|
-
import { createResponseInterface } from './http-interfaces';
|
9
|
-
import http, { ServerResponse, IncomingHttpHeaders } from 'http';
|
10
|
-
import https from 'https';
|
11
|
-
import express from 'express';
|
12
|
-
import { LogEntry, Logger, makeAlertId } from './logger';
|
13
|
-
import { getDisplayName, getDisplayRoom, getDisplayType, getProvidedNameOrDefault, getProvidedRoomOrDefault, getProvidedTypeOrDefault } from './infer-defaults';
|
14
|
-
import { URL } from "url";
|
15
|
-
import qs from "query-string";
|
16
|
-
import { PluginComponent } from './services/plugin';
|
17
|
-
import WebSocket, { Server as WebSocketServer } from "ws";
|
1
|
+
import { Device, DeviceInformation, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty, ScryptedNativeId } from '@scrypted/types';
|
2
|
+
import AdmZip from 'adm-zip';
|
18
3
|
import axios from 'axios';
|
19
|
-
import
|
4
|
+
import * as io from 'engine.io';
|
20
5
|
import { once } from 'events';
|
6
|
+
import express, { Request, Response } from 'express';
|
7
|
+
import http, { ServerResponse } from 'http';
|
8
|
+
import https from 'https';
|
9
|
+
import { spawn as ptySpawn } from 'node-pty-prebuilt-multiarch';
|
10
|
+
import path from 'path';
|
11
|
+
import rimraf from 'rimraf';
|
12
|
+
import semver from 'semver';
|
21
13
|
import { PassThrough } from 'stream';
|
14
|
+
import tar from 'tar';
|
15
|
+
import { URL } from "url";
|
16
|
+
import WebSocket, { Server as WebSocketServer } from "ws";
|
17
|
+
import { Plugin, PluginDevice, ScryptedAlert } from './db-types';
|
18
|
+
import { createResponseInterface } from './http-interfaces';
|
19
|
+
import { getDisplayName, getDisplayRoom, getDisplayType, getProvidedNameOrDefault, getProvidedRoomOrDefault, getProvidedTypeOrDefault } from './infer-defaults';
|
20
|
+
import { IOServer } from './io';
|
21
|
+
import { Level } from './level';
|
22
|
+
import { LogEntry, Logger, makeAlertId } from './logger';
|
22
23
|
import { PluginDebug } from './plugin/plugin-debug';
|
24
|
+
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
|
25
|
+
import { PluginHost } from './plugin/plugin-host';
|
26
|
+
import { isConnectionUpgrade, PluginHttp } from './plugin/plugin-http';
|
27
|
+
import { getPluginVolume } from './plugin/plugin-volume';
|
23
28
|
import { getIpAddress, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings';
|
24
|
-
import semver from 'semver';
|
25
|
-
import { ServiceControl } from './services/service-control';
|
26
29
|
import { Alerts } from './services/alerts';
|
27
|
-
import { Info } from './services/info';
|
28
|
-
import * as io from 'engine.io';
|
29
|
-
import { spawn as ptySpawn } from 'node-pty';
|
30
|
-
import rimraf from 'rimraf';
|
31
|
-
import { getPluginVolume } from './plugin/plugin-volume';
|
32
|
-
import { isConnectionUpgrade, PluginHttp } from './plugin/plugin-http';
|
33
|
-
import AdmZip from 'adm-zip';
|
34
|
-
import path from 'path';
|
35
30
|
import { CORSControl, CORSServer } from './services/cors';
|
36
|
-
import {
|
31
|
+
import { Info } from './services/info';
|
32
|
+
import { PluginComponent } from './services/plugin';
|
33
|
+
import { ServiceControl } from './services/service-control';
|
34
|
+
import { getState, ScryptedStateManager, setState } from './state';
|
37
35
|
|
38
36
|
interface DeviceProxyPair {
|
39
37
|
handler: PluginDeviceProxyHandler;
|
@@ -185,12 +183,10 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
185
183
|
|
186
184
|
const url = new URL(callback_url as string);
|
187
185
|
if (url.search) {
|
188
|
-
const
|
189
|
-
const state = search.state as string;
|
186
|
+
const state = url.searchParams.get('state');
|
190
187
|
if (state) {
|
191
188
|
const { s, d, r } = JSON.parse(state);
|
192
|
-
|
193
|
-
url.search = '?' + qs.stringify(search);
|
189
|
+
url.searchParams.set('state', s);
|
194
190
|
const oauthClient: ScryptedDevice & OauthClient = this.getDevice(d);
|
195
191
|
await oauthClient.onOauthCallback(url.toString()).catch();
|
196
192
|
res.redirect(r);
|
@@ -198,12 +194,12 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
198
194
|
}
|
199
195
|
}
|
200
196
|
if (url.hash) {
|
201
|
-
const hash =
|
202
|
-
const state = hash.state
|
197
|
+
const hash = new URLSearchParams(url.hash.substring(1));
|
198
|
+
const state = hash.get('state');
|
203
199
|
if (state) {
|
204
200
|
const { s, d, r } = JSON.parse(state);
|
205
|
-
hash.state
|
206
|
-
url.hash = '#' +
|
201
|
+
hash.set('state', s);
|
202
|
+
url.hash = '#' + hash.toString();
|
207
203
|
const oauthClient: ScryptedDevice & OauthClient = this.getDevice(d);
|
208
204
|
await oauthClient.onOauthCallback(url.toString());
|
209
205
|
res.redirect(r);
|
@@ -283,8 +279,11 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
283
279
|
this.shellio.handleRequest(req, res);
|
284
280
|
}
|
285
281
|
|
286
|
-
async getEndpointPluginData(endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<HttpPluginData> {
|
282
|
+
async getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<HttpPluginData> {
|
287
283
|
const ret = await this.getPluginForEndpoint(endpoint);
|
284
|
+
if (req.url.indexOf('/engine.io/api') !== -1)
|
285
|
+
return ret;
|
286
|
+
|
288
287
|
const { pluginDevice } = ret;
|
289
288
|
|
290
289
|
// check if upgrade requests can be handled. must be websocket.
|
@@ -2,6 +2,8 @@ import { startPluginRemote } from "./plugin/plugin-remote-worker";
|
|
2
2
|
import { RpcMessage } from "./rpc";
|
3
3
|
import worker_threads from "worker_threads";
|
4
4
|
import v8 from 'v8';
|
5
|
+
import net from 'net';
|
6
|
+
import { SidebandSocketSerializer } from "./plugin/socket-serializer";
|
5
7
|
|
6
8
|
if (process.argv[2] === 'child-thread') {
|
7
9
|
const peer = startPluginRemote(process.argv[3], (message, reject) => {
|
@@ -16,7 +18,7 @@ if (process.argv[2] === 'child-thread') {
|
|
16
18
|
worker_threads.parentPort.on('message', message => peer.handleMessage(v8.deserialize(message)));
|
17
19
|
}
|
18
20
|
else {
|
19
|
-
const peer = startPluginRemote(process.argv[3], (message, reject) => process.send(message,
|
21
|
+
const peer = startPluginRemote(process.argv[3], (message, reject, serializationContext) => process.send(message, serializationContext?.sendHandle, {
|
20
22
|
swallowErrors: !reject,
|
21
23
|
}, e => {
|
22
24
|
if (e)
|
@@ -24,6 +26,7 @@ else {
|
|
24
26
|
}));
|
25
27
|
|
26
28
|
peer.transportSafeArgumentTypes.add(Buffer.name);
|
29
|
+
peer.addSerializer(net.Socket, net.Socket.name, new SidebandSocketSerializer());
|
27
30
|
process.on('message', message => peer.handleMessage(message as RpcMessage));
|
28
31
|
process.on('disconnect', () => {
|
29
32
|
console.error('peer host disconnected, exiting.');
|
@@ -12,7 +12,6 @@ import { getHostAddresses, SCRYPTED_DEBUG_PORT, SCRYPTED_INSECURE_PORT, SCRYPTED
|
|
12
12
|
import crypto from 'crypto';
|
13
13
|
import cookieParser from 'cookie-parser';
|
14
14
|
import axios from 'axios';
|
15
|
-
import qs from 'query-string';
|
16
15
|
import { RPCResultError } from './rpc';
|
17
16
|
import fs from 'fs';
|
18
17
|
import mkdirp from 'mkdirp';
|
@@ -155,7 +154,30 @@ async function start() {
|
|
155
154
|
// use a hash of the private key as the cookie secret.
|
156
155
|
app.use(cookieParser(crypto.createHash('sha256').update(certSetting.value.serviceKey).digest().toString('hex')));
|
157
156
|
|
158
|
-
|
157
|
+
// trap to add access control headers.
|
158
|
+
app.use((req, res, next) => {
|
159
|
+
if (!req.headers.upgrade)
|
160
|
+
scrypted.addAccessControlHeaders(req, res);
|
161
|
+
next();
|
162
|
+
})
|
163
|
+
|
164
|
+
app.options('*', (req, res) => {
|
165
|
+
// add more?
|
166
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
167
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
|
168
|
+
res.send(200);
|
169
|
+
});
|
170
|
+
|
171
|
+
const authSalt = crypto.randomBytes(16);
|
172
|
+
const createAuthorizationToken = (login_user_token: string) => {
|
173
|
+
const salted = login_user_token + authSalt;
|
174
|
+
const hash = crypto.createHash('sha256');
|
175
|
+
hash.update(salted);
|
176
|
+
const sha = hash.digest().toString('hex');
|
177
|
+
return `Bearer ${sha}#${login_user_token}`;
|
178
|
+
}
|
179
|
+
|
180
|
+
app.use(async (req, res, next) => {
|
159
181
|
// this is a trap for all auth.
|
160
182
|
// only basic auth will fail with 401. it is up to the endpoints to manage
|
161
183
|
// lack of login from cookie auth.
|
@@ -165,10 +187,8 @@ async function start() {
|
|
165
187
|
const userTokenParts = login_user_token.split('#');
|
166
188
|
const username = userTokenParts[0];
|
167
189
|
const timestamp = parseInt(userTokenParts[1]);
|
168
|
-
if (timestamp + 86400000 < Date.now())
|
169
|
-
console.warn('login expired');
|
190
|
+
if (timestamp + 86400000 < Date.now())
|
170
191
|
return next();
|
171
|
-
}
|
172
192
|
|
173
193
|
// this database lookup on every web request is not necessary, the cookie
|
174
194
|
// itself is the auth, and is signed. furthermore, this is currently
|
@@ -182,7 +202,27 @@ async function start() {
|
|
182
202
|
// }
|
183
203
|
|
184
204
|
res.locals.username = username;
|
185
|
-
|
205
|
+
}
|
206
|
+
else if (req.headers.authorization?.startsWith('Bearer ')) {
|
207
|
+
const splits = req.headers.authorization.substring('Bearer '.length).split('#');
|
208
|
+
const login_user_token = splits[1] + '#' + splits[2];
|
209
|
+
if (login_user_token) {
|
210
|
+
const check = splits[0];
|
211
|
+
|
212
|
+
const salted = login_user_token + authSalt;
|
213
|
+
const hash = crypto.createHash('sha256');
|
214
|
+
hash.update(salted);
|
215
|
+
const sha = hash.digest().toString('hex');
|
216
|
+
|
217
|
+
if (check === sha) {
|
218
|
+
const splits2 = login_user_token.split('#');
|
219
|
+
const username = splits2[0];
|
220
|
+
const timestamp = parseInt(splits2[1]);
|
221
|
+
if (timestamp + 86400000 < Date.now())
|
222
|
+
return next();
|
223
|
+
res.locals.username = username;
|
224
|
+
}
|
225
|
+
}
|
186
226
|
}
|
187
227
|
next();
|
188
228
|
});
|
@@ -277,8 +317,8 @@ async function start() {
|
|
277
317
|
|
278
318
|
app.get('/web/component/script/search', async (req, res) => {
|
279
319
|
try {
|
280
|
-
const query =
|
281
|
-
text: req.query.text,
|
320
|
+
const query = new URLSearchParams({
|
321
|
+
text: req.query.text.toString(),
|
282
322
|
})
|
283
323
|
const response = await axios(`https://registry.npmjs.org/-/v1/search?${query}`);
|
284
324
|
res.send(response.data);
|
@@ -355,7 +395,7 @@ async function start() {
|
|
355
395
|
});
|
356
396
|
|
357
397
|
const getLoginUserToken = (reqSecure: boolean) => {
|
358
|
-
return reqSecure ? 'login_user_token' : '
|
398
|
+
return reqSecure ? 'login_user_token' : 'login_user_token_insecure';
|
359
399
|
};
|
360
400
|
|
361
401
|
const getSignedLoginUserToken = (req: Request<any>): string => {
|
@@ -364,21 +404,23 @@ async function start() {
|
|
364
404
|
|
365
405
|
app.get('/logout', (req, res) => {
|
366
406
|
res.clearCookie(getLoginUserToken(req.secure));
|
367
|
-
|
407
|
+
if (req.headers['accept']?.startsWith('application/json')) {
|
408
|
+
res.send({});
|
409
|
+
}
|
410
|
+
else {
|
411
|
+
res.redirect('/endpoint/@scrypted/core/public/');
|
412
|
+
}
|
368
413
|
});
|
369
414
|
|
370
415
|
let hasLogin = await db.getCount(ScryptedUser) > 0;
|
371
416
|
|
372
417
|
app.options('/login', (req, res) => {
|
373
|
-
scrypted.addAccessControlHeaders(req, res);
|
374
418
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
375
419
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
|
376
420
|
res.send(200);
|
377
421
|
});
|
378
422
|
|
379
423
|
app.post('/login', async (req, res) => {
|
380
|
-
scrypted.addAccessControlHeaders(req, res);
|
381
|
-
|
382
424
|
const { username, password, change_password } = req.body;
|
383
425
|
const timestamp = Date.now();
|
384
426
|
const maxAge = 86400000;
|
@@ -422,6 +464,7 @@ async function start() {
|
|
422
464
|
}
|
423
465
|
|
424
466
|
res.send({
|
467
|
+
authorization: createAuthorizationToken(login_user_token),
|
425
468
|
username,
|
426
469
|
expiration: maxAge,
|
427
470
|
addresses,
|
@@ -456,6 +499,7 @@ async function start() {
|
|
456
499
|
});
|
457
500
|
|
458
501
|
res.send({
|
502
|
+
authorization: createAuthorizationToken(login_user_token),
|
459
503
|
username,
|
460
504
|
token: user.token,
|
461
505
|
expiration: maxAge,
|
@@ -463,6 +507,7 @@ async function start() {
|
|
463
507
|
});
|
464
508
|
});
|
465
509
|
|
510
|
+
|
466
511
|
app.get('/login', async (req, res) => {
|
467
512
|
scrypted.addAccessControlHeaders(req, res);
|
468
513
|
|
@@ -510,6 +555,7 @@ async function start() {
|
|
510
555
|
}
|
511
556
|
|
512
557
|
res.send({
|
558
|
+
authorization: createAuthorizationToken(login_user_token),
|
513
559
|
expiration: 86400000 - (Date.now() - timestamp),
|
514
560
|
username,
|
515
561
|
addresses,
|
package/src/state.ts
CHANGED
@@ -116,7 +116,7 @@ export class ScryptedStateManager extends EventRegistry {
|
|
116
116
|
let cb = (eventDetails: EventDetails, eventData: any) => {
|
117
117
|
if (denoise && lastData === eventData)
|
118
118
|
return;
|
119
|
-
callback(eventDetails, eventData);
|
119
|
+
callback?.(eventDetails, eventData);
|
120
120
|
};
|
121
121
|
|
122
122
|
const wrappedRegister = super.listenDevice(id, options, cb);
|
@@ -124,6 +124,7 @@ export class ScryptedStateManager extends EventRegistry {
|
|
124
124
|
return new EventListenerRegisterImpl(() => {
|
125
125
|
wrappedRegister.removeListener();
|
126
126
|
cb = undefined;
|
127
|
+
callback = undefined;
|
127
128
|
polling = false;
|
128
129
|
});
|
129
130
|
}
|