@scrypted/server 0.0.181 → 0.1.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.

Files changed (48) hide show
  1. package/dist/io.js +3 -0
  2. package/dist/io.js.map +1 -0
  3. package/dist/plugin/buffer-serializer.js +18 -1
  4. package/dist/plugin/buffer-serializer.js.map +1 -1
  5. package/dist/plugin/media.js +4 -3
  6. package/dist/plugin/media.js.map +1 -1
  7. package/dist/plugin/plugin-host-api.js +2 -0
  8. package/dist/plugin/plugin-host-api.js.map +1 -1
  9. package/dist/plugin/plugin-host.js +83 -14
  10. package/dist/plugin/plugin-host.js.map +1 -1
  11. package/dist/plugin/plugin-http.js +7 -5
  12. package/dist/plugin/plugin-http.js.map +1 -1
  13. package/dist/plugin/plugin-remote-websocket.js +0 -2
  14. package/dist/plugin/plugin-remote-websocket.js.map +1 -1
  15. package/dist/plugin/plugin-remote-worker.js +12 -10
  16. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  17. package/dist/plugin/plugin-remote.js +4 -2
  18. package/dist/plugin/plugin-remote.js.map +1 -1
  19. package/dist/rpc.js +18 -27
  20. package/dist/rpc.js.map +1 -1
  21. package/dist/runtime.js +95 -2
  22. package/dist/runtime.js.map +1 -1
  23. package/dist/scrypted-server-main.js +20 -21
  24. package/dist/scrypted-server-main.js.map +1 -1
  25. package/dist/server-settings.js +67 -15
  26. package/dist/server-settings.js.map +1 -1
  27. package/dist/services/cors.js +17 -0
  28. package/dist/services/cors.js.map +1 -0
  29. package/package.json +6 -4
  30. package/python/plugin-remote.py +22 -17
  31. package/python/rpc.py +0 -6
  32. package/src/io.ts +25 -0
  33. package/src/plugin/buffer-serializer.ts +19 -0
  34. package/src/plugin/media.ts +5 -4
  35. package/src/plugin/plugin-host-api.ts +2 -0
  36. package/src/plugin/plugin-host.ts +70 -17
  37. package/src/plugin/plugin-http.ts +11 -8
  38. package/src/plugin/plugin-remote-websocket.ts +0 -2
  39. package/src/plugin/plugin-remote-worker.ts +12 -10
  40. package/src/plugin/plugin-remote.ts +4 -2
  41. package/src/rpc.ts +22 -36
  42. package/src/runtime.ts +86 -3
  43. package/src/scrypted-server-main.ts +24 -27
  44. package/src/server-settings.ts +54 -16
  45. package/src/services/cors.ts +19 -0
  46. package/dist/addresses.js +0 -12
  47. package/dist/addresses.js.map +0 -1
  48. package/src/addresses.ts +0 -5
@@ -8,7 +8,7 @@ import net from 'net';
8
8
  import { ScryptedRuntime } from './runtime';
9
9
  import level from './level';
10
10
  import { Plugin, ScryptedUser, Settings } from './db-types';
11
- import { SCRYPTED_DEBUG_PORT, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings';
11
+ import { getHostAddresses, SCRYPTED_DEBUG_PORT, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings';
12
12
  import crypto from 'crypto';
13
13
  import cookieParser from 'cookie-parser';
14
14
  import axios from 'axios';
@@ -20,7 +20,6 @@ import { install as installSourceMapSupport } from 'source-map-support';
20
20
  import httpAuth from 'http-auth';
21
21
  import semver from 'semver';
22
22
  import { Info } from './services/info';
23
- import { getAddresses } from './addresses';
24
23
  import { sleep } from './sleep';
25
24
  import { createSelfSignedCertificate, CURRENT_SELF_SIGNED_CERTIFICATE_VERSION } from './cert';
26
25
  import { PluginError } from './plugin/plugin-error';
@@ -123,7 +122,6 @@ async function start() {
123
122
  certSetting = await db.upsert(certSetting);
124
123
  }
125
124
 
126
-
127
125
  const basicAuth = httpAuth.basic({
128
126
  realm: 'Scrypted',
129
127
  }, async (username, password, callback) => {
@@ -184,6 +182,7 @@ async function start() {
184
182
  // }
185
183
 
186
184
  res.locals.username = username;
185
+ (req as any).username = username;
187
186
  }
188
187
  next();
189
188
  });
@@ -193,6 +192,7 @@ async function start() {
193
192
  if (req.protocol === 'https' && req.headers.authorization && req.headers.authorization.toLowerCase()?.indexOf('basic') !== -1) {
194
193
  const basicChecker = basicAuth.check((req) => {
195
194
  res.locals.username = req.user;
195
+ (req as any).username = req.user;
196
196
  next();
197
197
  });
198
198
 
@@ -226,7 +226,7 @@ async function start() {
226
226
 
227
227
  console.log('#######################################################');
228
228
  console.log(`Scrypted Server (Local) : https://localhost:${SCRYPTED_SECURE_PORT}/`);
229
- for (const address of getAddresses()) {
229
+ for (const address of getHostAddresses(true, true)) {
230
230
  console.log(`Scrypted Server (Remote) : https://${address}:${SCRYPTED_SECURE_PORT}/`);
231
231
  }
232
232
  console.log(`Version: : ${await new Info().getVersion()}`);
@@ -360,10 +360,20 @@ async function start() {
360
360
 
361
361
  let hasLogin = await db.getCount(ScryptedUser) > 0;
362
362
 
363
+ app.options('/login', (req, res) => {
364
+ scrypted.addAccessControlHeaders(req, res);
365
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
366
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
367
+ res.send(200);
368
+ });
369
+
363
370
  app.post('/login', async (req, res) => {
371
+ scrypted.addAccessControlHeaders(req, res);
372
+
364
373
  const { username, password, change_password } = req.body;
365
374
  const timestamp = Date.now();
366
375
  const maxAge = 86400000;
376
+ const addresses = getHostAddresses(true, true).map(address => `https://${address}:${SCRYPTED_SECURE_PORT}`);
367
377
 
368
378
  if (hasLogin) {
369
379
  const user = await db.tryGet(ScryptedUser, username);
@@ -393,6 +403,7 @@ async function start() {
393
403
  secure: true,
394
404
  signed: true,
395
405
  httpOnly: true,
406
+ sameSite: 'none',
396
407
  });
397
408
 
398
409
  if (change_password) {
@@ -405,6 +416,7 @@ async function start() {
405
416
  res.send({
406
417
  username,
407
418
  expiration: maxAge,
419
+ addresses,
408
420
  });
409
421
 
410
422
  return;
@@ -423,6 +435,7 @@ async function start() {
423
435
  user.salt = crypto.randomBytes(64).toString('base64');
424
436
  user.passwordHash = crypto.createHash('sha256').update(user.salt + password).digest().toString('hex');
425
437
  user.passwordDate = timestamp;
438
+ user.token = crypto.randomBytes(16).toString('hex');
426
439
  await db.upsert(user);
427
440
  hasLogin = true;
428
441
 
@@ -432,16 +445,21 @@ async function start() {
432
445
  secure: true,
433
446
  signed: true,
434
447
  httpOnly: true,
448
+ sameSite: 'none',
435
449
  });
436
450
 
437
451
  res.send({
438
452
  username,
453
+ token: user.token,
439
454
  expiration: maxAge,
455
+ addresses,
440
456
  });
441
457
  });
442
458
 
443
-
444
459
  app.get('/login', async (req, res) => {
460
+ scrypted.addAccessControlHeaders(req, res);
461
+
462
+ const addresses = getHostAddresses(true, true).map(address => `https://${address}:${SCRYPTED_SECURE_PORT}`);
445
463
  if (req.protocol === 'https' && req.headers.authorization) {
446
464
  const username = await new Promise(resolve => {
447
465
  const basicChecker = basicAuth.check((req) => {
@@ -484,31 +502,10 @@ async function start() {
484
502
  return;
485
503
  }
486
504
 
487
- // this database lookup on every web request is not necessary, the cookie
488
- // itself is the auth, and is signed. furthermore, this is currently
489
- // a single user setup anywyas. revisit this at some point when
490
- // multiple users are implemented.
491
-
492
- // const user = await db.tryGet(ScryptedUser, username);
493
- // if (!user) {
494
- // res.send({
495
- // error: 'User not found.',
496
- // hasLogin,
497
- // })
498
- // return;
499
- // }
500
-
501
- // if (timestamp < user.passwordDate) {
502
- // res.send({
503
- // error: 'Login invalid. Password has changed.',
504
- // hasLogin,
505
- // })
506
- // return;
507
- // }
508
-
509
505
  res.send({
510
506
  expiration: 86400000 - (Date.now() - timestamp),
511
507
  username,
508
+ addresses,
512
509
  })
513
510
  });
514
511
 
@@ -1,24 +1,62 @@
1
1
  import os from 'os';
2
+ import * as nodeIp from 'ip';
2
3
 
3
4
  export const SCRYPTED_INSECURE_PORT = parseInt(process.env.SCRYPTED_INSECURE_PORT) || 11080;
4
5
  export const SCRYPTED_SECURE_PORT = parseInt(process.env.SCRYPTED_SECURE_PORT) || 10443;
5
6
  export const SCRYPTED_DEBUG_PORT = parseInt(process.env.SCRYPTED_DEBUG_PORT) || 10081;
6
7
 
7
8
  export function getIpAddress(): string {
8
- const ni = os.networkInterfaces();
9
- for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) {
10
- let ipv4: os.NetworkInterfaceInfo;
11
- let ipv6: os.NetworkInterfaceInfo;
12
- for (const en of (ni[`en${i}`] || [])) {
13
- if (en.family === 'IPv4')
14
- ipv4 = en;
15
- else if (en.family === 'IPv6')
16
- ipv6 = en;
17
- }
18
-
19
- if (ipv4 || ipv6)
20
- return (ipv4 || ipv6).address;
21
- }
22
-
23
- return '127.0.0.1';
9
+ return nodeIp.address();
10
+ }
11
+
12
+ function nodeIpAddress(family: string): string[] {
13
+ // https://chromium.googlesource.com/external/webrtc/+/master/rtc_base/network.cc#236
14
+ const costlyNetworks = ["ipsec", "tun", "utun", "tap"];
15
+
16
+ const ignoreNetworks = [
17
+ // seen these on macos
18
+ 'llw',
19
+ 'awdl',
20
+
21
+ ...costlyNetworks,
22
+ ];
23
+
24
+ const interfaces = os.networkInterfaces();
25
+
26
+ const all = Object.keys(interfaces)
27
+ .map((nic) => {
28
+ for (const costly of ignoreNetworks) {
29
+ if (nic.startsWith(costly)) {
30
+ return {
31
+ nic,
32
+ addresses: [],
33
+ };
34
+ }
35
+ }
36
+ const addresses = interfaces[nic]!.filter(
37
+ (details) =>
38
+ details.family.toLowerCase() === family
39
+ && !nodeIp.isLoopback(details.address)
40
+ );
41
+ return {
42
+ nic,
43
+ addresses: addresses.map((address) => address.address),
44
+ };
45
+ })
46
+ .filter((address) => !!address);
47
+
48
+ // os.networkInterfaces doesn't actually return addresses in a good order.
49
+ // have seen instances where en0 (ethernet) is after en1 (wlan), etc.
50
+ // eth0 > eth1
51
+ all.sort((a, b) => a.nic.localeCompare(b.nic));
52
+ return Object.values(all)
53
+ .map((entry) => entry.addresses)
54
+ .flat();
55
+ }
56
+
57
+ export function getHostAddresses(useIpv4: boolean, useIpv6: boolean) {
58
+ const address: string[] = [];
59
+ if (useIpv4) address.push(...nodeIpAddress("ipv4"));
60
+ if (useIpv6) address.push(...nodeIpAddress("ipv6"));
61
+ return address;
24
62
  }
@@ -0,0 +1,19 @@
1
+ import { ScryptedRuntime } from "../runtime";
2
+
3
+ export interface CORSServer {
4
+ tag: string;
5
+ server: string;
6
+ }
7
+
8
+ export class CORSControl {
9
+ constructor(public runtime: ScryptedRuntime) {
10
+ }
11
+
12
+ async getCORS(): Promise<CORSServer[]> {
13
+ return this.runtime.cors;
14
+ }
15
+
16
+ async setCORS(servers: CORSServer[]) {
17
+ this.runtime.cors = servers;
18
+ }
19
+ }
package/dist/addresses.js DELETED
@@ -1,12 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getAddresses = void 0;
7
- const os_1 = __importDefault(require("os"));
8
- function getAddresses() {
9
- return Object.entries(os_1.default.networkInterfaces()).filter(([iface]) => iface.startsWith('en') || iface.startsWith('eth') || iface.startsWith('wlan')).map(([_, addr]) => addr).flat().map(info => info.address).filter(address => address);
10
- }
11
- exports.getAddresses = getAddresses;
12
- //# sourceMappingURL=addresses.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"addresses.js","sourceRoot":"","sources":["../src/addresses.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AAEpB,SAAgB,YAAY;IACxB,OAAO,MAAM,CAAC,OAAO,CAAC,YAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAC1O,CAAC;AAFD,oCAEC"}
package/src/addresses.ts DELETED
@@ -1,5 +0,0 @@
1
- import os from 'os';
2
-
3
- export function getAddresses() {
4
- return Object.entries(os.networkInterfaces()).filter(([iface]) => iface.startsWith('en') || iface.startsWith('eth') || iface.startsWith('wlan')).map(([_, addr]) => addr).flat().map(info => info.address).filter(address => address);
5
- }