mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 22:23:27 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7065365a47 | ||
|
|
b82520776e | ||
|
|
638c1f77fd | ||
|
|
73a489ea37 | ||
|
|
77d69f025a | ||
|
|
3bc14ad248 | ||
|
|
03e5a9dec1 | ||
|
|
57b790c332 | ||
|
|
ce2ea63be7 | ||
|
|
2dd4721b7f | ||
|
|
667075dfad | ||
|
|
7abdb06b66 | ||
|
|
43e5822c93 | ||
|
|
bc579514e7 | ||
|
|
825100f94e | ||
|
|
803bfc1560 | ||
|
|
b2013a54ed | ||
|
|
f252407935 | ||
|
|
516f2a2a7b | ||
|
|
c1677ce691 | ||
|
|
5028fb812d | ||
|
|
2db4e2579f | ||
|
|
b339ca6cd2 | ||
|
|
f100999cb1 |
@@ -298,7 +298,7 @@ export async function connectRTCSignalingClients(
|
||||
if (offerOptions?.offer && answerOptions?.offer)
|
||||
throw new Error('Both RTC clients have offers and can not negotiate. Consider implementing this in @scrypted/webrtc.');
|
||||
|
||||
if (offerOptions?.requiresOffer && answerOptions.requiresOffer)
|
||||
if (offerOptions?.requiresOffer && answerOptions?.requiresOffer)
|
||||
throw new Error('Both RTC clients require offers and can not negotiate.');
|
||||
|
||||
offerSetup.type = 'offer';
|
||||
|
||||
2
external/ring-client-api
vendored
2
external/ring-client-api
vendored
Submodule external/ring-client-api updated: d571cdfc00...81f6570f59
@@ -27,13 +27,6 @@ echo "sdk > npm run build"
|
||||
npm run build
|
||||
popd
|
||||
|
||||
pushd external/HAP-NodeJS
|
||||
echo "external/HAP-NodeJS > npm install"
|
||||
npm install
|
||||
echo "external/HAP-NodeJS > npm run build"
|
||||
npm run build
|
||||
popd
|
||||
|
||||
pushd external/werift
|
||||
echo "external/werift > npm install"
|
||||
npm install
|
||||
|
||||
@@ -41,7 +41,7 @@ The Channel number is the hundreds digit and (sub-)stream is ones digit:
|
||||
|
||||
# Troubleshooting
|
||||
## General
|
||||
* Not receiving motion alerts in the device's Scrypted event log? Check all of the following: **(1)** device has a motion detection grid drawn and enabled, **(2)** user or group access permissions of account used for device **(3)** do not use self-signed certs for HTTPS on the device, and **(4)** `CGI` and `ISAPI` integration protocol/service on device is enabled.
|
||||
* Not receiving motion alerts in the device's Scrypted event log? Check all of the following: **(1)** device has a motion detection grid drawn and enabled, **(2)** user or group access permissions of account used for device **(3)** do not use self-signed certs for HTTPS on the device, **(4)** `CGI` and `ISAPI` integration protocol/service on device is enabled, and **(5)** that the authentication method on the device is set to "digest".
|
||||
* If device has HTTPS enabled, try disabling HTTPS on the device to see if that resolves issue (do not use self-signed certs).
|
||||
* If device has enabled user lockout, max connections, concurrent requests, etc., try disabling and/or increasing to max allowed for troubleshooting.
|
||||
* Does your account (`Username`) have proper user and/or group permissions? Try granting all permissions for testing.
|
||||
|
||||
4
plugins/pam-diff/package-lock.json
generated
4
plugins/pam-diff/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/pam-diff",
|
||||
"version": "0.0.18",
|
||||
"version": "0.0.20",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/pam-diff",
|
||||
"version": "0.0.18",
|
||||
"version": "0.0.20",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^16.6.1",
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.0.18"
|
||||
"version": "0.0.20"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { PassThrough, Writable } from 'stream';
|
||||
const { mediaManager } = sdk;
|
||||
|
||||
const defaultDifference = 9;
|
||||
const defaultPercentage = 15;
|
||||
const defaultPercentage = 2;
|
||||
|
||||
interface PamDiffSession {
|
||||
id: string;
|
||||
|
||||
4
plugins/prebuffer-mixin/package-lock.json
generated
4
plugins/prebuffer-mixin/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/prebuffer-mixin",
|
||||
"version": "0.9.79",
|
||||
"version": "0.9.80",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/prebuffer-mixin",
|
||||
"version": "0.9.79",
|
||||
"version": "0.9.80",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/prebuffer-mixin",
|
||||
"version": "0.9.79",
|
||||
"version": "0.9.80",
|
||||
"description": "Video Stream Rebroadcast, Prebuffer, and Management Plugin for Scrypted.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -111,7 +111,7 @@ export function createStreamSettings(device: MixinDeviceBase<VideoCamera>) {
|
||||
placeholder: '-hwaccel auto',
|
||||
choices: Object.keys(getH264DecoderArgs()),
|
||||
combobox: true,
|
||||
mapPut: (oldValue, newValue) => getH264DecoderArgs()[newValue]?.join(' ') || newValue,
|
||||
mapPut: (oldValue, newValue) => getH264DecoderArgs()[newValue]?.join(' ') || newValue || '',
|
||||
hide: true,
|
||||
},
|
||||
videoFilterArguments: {
|
||||
|
||||
4
plugins/python-codecs/package-lock.json
generated
4
plugins/python-codecs/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/python-codecs",
|
||||
"version": "0.1.27",
|
||||
"version": "0.1.30",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/python-codecs",
|
||||
"version": "0.1.27",
|
||||
"version": "0.1.30",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/python-codecs",
|
||||
"version": "0.1.27",
|
||||
"version": "0.1.30",
|
||||
"description": "Python Codecs for Scrypted",
|
||||
"keywords": [
|
||||
"scrypted",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import traceback
|
||||
import asyncio
|
||||
import scrypted_sdk
|
||||
from scrypted_sdk import Setting, SettingValue
|
||||
@@ -6,6 +7,7 @@ import gstreamer
|
||||
import libav
|
||||
import vipsimage
|
||||
import pilimage
|
||||
import time
|
||||
|
||||
Gst = None
|
||||
try:
|
||||
@@ -128,19 +130,29 @@ def create_scrypted_plugin():
|
||||
|
||||
class CodecFork:
|
||||
async def generateVideoFramesGstreamer(self, mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None, h264Decoder: str = None) -> scrypted_sdk.VideoFrame:
|
||||
start = time.time()
|
||||
try:
|
||||
async for data in gstreamer.generateVideoFramesGstreamer(mediaObject, options, filter, h264Decoder):
|
||||
yield data
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise
|
||||
finally:
|
||||
print('gstreamer finished after %s' % (time.time() - start))
|
||||
import os
|
||||
os._exit(os.EX_OK)
|
||||
pass
|
||||
|
||||
async def generateVideoFramesLibav(self, mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None) -> scrypted_sdk.VideoFrame:
|
||||
start = time.time()
|
||||
try:
|
||||
async for data in libav.generateVideoFramesLibav(mediaObject, options, filter):
|
||||
yield data
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise
|
||||
finally:
|
||||
print('libav finished after %s' % (time.time() - start))
|
||||
import os
|
||||
os._exit(os.EX_OK)
|
||||
pass
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# needed by libav to_ndarray
|
||||
numpy>=1.16.2
|
||||
|
||||
# gobject instrospection for gstreamer.
|
||||
PyGObject>=3.30.4; sys_platform != 'win32'
|
||||
|
||||
|
||||
26
plugins/ring/package-lock.json
generated
26
plugins/ring/package-lock.json
generated
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "@scrypted/ring",
|
||||
"version": "0.0.107",
|
||||
"version": "0.0.109",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/ring",
|
||||
"version": "0.0.107",
|
||||
"version": "0.0.109",
|
||||
"dependencies": {
|
||||
"@koush/ring-client-api": "file:../../external/ring-client-api",
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/node": "^18.15.3",
|
||||
"axios": "^1.3.4",
|
||||
"@types/node": "^18.15.11",
|
||||
"axios": "^1.3.5",
|
||||
"rxjs": "^7.8.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@@ -42,14 +42,14 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "^0.4.8",
|
||||
"@changesets/cli": "^2.26.0",
|
||||
"@changesets/cli": "^2.26.1",
|
||||
"@swc-node/register": "^1.6.2",
|
||||
"turbo": "^1.8.2"
|
||||
"turbo": "^1.8.5"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.85",
|
||||
"version": "0.2.86",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
@@ -148,9 +148,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
|
||||
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw=="
|
||||
"version": "18.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
|
||||
},
|
||||
"node_modules/@types/responselike": {
|
||||
"version": "1.0.0",
|
||||
@@ -179,9 +179,9 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
|
||||
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
|
||||
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
|
||||
@@ -36,13 +36,13 @@
|
||||
"@koush/ring-client-api": "file:../../external/ring-client-api",
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/node": "^18.15.3",
|
||||
"axios": "^1.3.4",
|
||||
"@types/node": "^18.15.11",
|
||||
"axios": "^1.3.5",
|
||||
"rxjs": "^7.8.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"got": "11.8.6",
|
||||
"socket.io-client": "^2.5.0"
|
||||
},
|
||||
"version": "0.0.107"
|
||||
"version": "0.0.109"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import sdk, { Device, DeviceProvider, ScryptedDeviceBase, ScryptedDeviceType, Sc
|
||||
import { StorageSettings } from '@scrypted/sdk/storage-settings';
|
||||
import { RingLocationDevice } from './location';
|
||||
import { generateUuid, Location, RingBaseApi, RingRestClient } from './ring-client-api';
|
||||
import { sleep } from '@scrypted/common/src/sleep';
|
||||
|
||||
const { deviceManager, mediaManager } = sdk;
|
||||
|
||||
@@ -19,12 +20,18 @@ class RingPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings
|
||||
},
|
||||
email: {
|
||||
title: 'Email',
|
||||
onPut: async () => this.clearTryDiscoverDevices(),
|
||||
onPut: async () => {
|
||||
if (await this.loginNextTick())
|
||||
this.clearTryDiscoverDevices();
|
||||
},
|
||||
},
|
||||
password: {
|
||||
title: 'Password',
|
||||
type: 'password',
|
||||
onPut: async () => this.clearTryDiscoverDevices(),
|
||||
onPut: async () => {
|
||||
if (await this.loginNextTick())
|
||||
this.clearTryDiscoverDevices();
|
||||
},
|
||||
},
|
||||
loginCode: {
|
||||
title: 'Two Factor Code',
|
||||
@@ -72,8 +79,18 @@ class RingPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings
|
||||
this.settingsStorage.values.systemId = generateUuid();
|
||||
}
|
||||
|
||||
waiting = false;
|
||||
async loginNextTick() {
|
||||
if (this.waiting)
|
||||
return false;
|
||||
this.waiting = true;
|
||||
await sleep(500);
|
||||
this.waiting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
async clearTryDiscoverDevices() {
|
||||
this.settingsStorage.values.refreshToken = undefined;
|
||||
this.settingsStorage.values.refreshToken = '';
|
||||
await this.discoverDevices();
|
||||
this.console.log('discovery completed successfully');
|
||||
}
|
||||
@@ -192,7 +209,7 @@ class RingPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings
|
||||
return this.devices.get(nativeId);
|
||||
}
|
||||
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> {}
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> { }
|
||||
}
|
||||
|
||||
export default RingPlugin;
|
||||
|
||||
2
plugins/webrtc/.vscode/settings.json
vendored
2
plugins/webrtc/.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
"scrypted.debugHost": "koushik-ubuntu",
|
||||
}
|
||||
4
plugins/webrtc/package-lock.json
generated
4
plugins/webrtc/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/webrtc",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/webrtc",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/webrtc",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
|
||||
@@ -128,6 +128,7 @@ class WebRTCMixin extends SettingsMixinDeviceBase<RTCSignalingClient & VideoCame
|
||||
this.plugin.storageSettings.values.maximumCompatibilityMode,
|
||||
this.plugin.getRTCConfiguration(),
|
||||
await this.plugin.getWeriftConfiguration(options?.disableTurn),
|
||||
options?.requiresAnswer === true ? false : true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import annotations
|
||||
from typing import AbstractSet, Any, Callable
|
||||
from typing_extensions import TypedDict
|
||||
try:
|
||||
from typing import TypedDict
|
||||
except:
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
SettingValue = str
|
||||
EventListener = Callable[[Any, Any, Any], None]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import annotations
|
||||
from enum import Enum
|
||||
from typing_extensions import TypedDict
|
||||
try:
|
||||
from typing import TypedDict
|
||||
except:
|
||||
from typing_extensions import TypedDict
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
|
||||
|
||||
@@ -256,7 +256,10 @@ class ${td.name}(TypedDict):
|
||||
|
||||
const pythonTypes = `from __future__ import annotations
|
||||
from enum import Enum
|
||||
from typing_extensions import TypedDict
|
||||
try:
|
||||
from typing import TypedDict
|
||||
except:
|
||||
from typing_extensions import TypedDict
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
|
||||
|
||||
@@ -1967,6 +1967,7 @@ export interface RTCSignalingOptions {
|
||||
*/
|
||||
offer?: RTCSessionDescriptionInit;
|
||||
requiresOffer?: boolean;
|
||||
requiresAnswer?: boolean;
|
||||
/**
|
||||
* Disables trickle ICE. All candidates must be sent in the initial offer/answer sdp.
|
||||
*/
|
||||
|
||||
@@ -10,3 +10,6 @@ scrypted.db.bak
|
||||
__pycache__
|
||||
.venv
|
||||
.vscode
|
||||
tsconfig.json
|
||||
test
|
||||
scripts
|
||||
|
||||
4
server/package-lock.json
generated
4
server/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.7.43",
|
||||
"version": "0.7.50",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.7.43",
|
||||
"version": "0.7.50",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.10",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.7.44",
|
||||
"version": "0.7.51",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.10",
|
||||
@@ -72,7 +72,8 @@
|
||||
"prebeta": "npm version patch && git add package.json && npm run build && git commit -m prebeta",
|
||||
"beta": "npm publish --tag beta",
|
||||
"release": "npm publish",
|
||||
"postrelease": "git tag v$npm_package_version && git push origin v$npm_package_version && npm version patch && git add package.json && npm run build && git commit -m postrelease",
|
||||
"prepublish": "npm run build",
|
||||
"postrelease": "git tag v$npm_package_version && git push origin v$npm_package_version && npm version patch && git add package.json && git commit -m postrelease",
|
||||
"docker": "scripts/github-workflow-publish-docker.sh"
|
||||
},
|
||||
"author": "",
|
||||
|
||||
@@ -2,13 +2,12 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import gc
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import concurrent.futures
|
||||
import time
|
||||
import traceback
|
||||
import zipfile
|
||||
@@ -21,14 +20,23 @@ from os import sys
|
||||
from typing import Any, Optional, Set, Tuple
|
||||
|
||||
import scrypted_python.scrypted_sdk.types
|
||||
from scrypted_python.scrypted_sdk import ScryptedStatic, PluginFork
|
||||
from scrypted_python.scrypted_sdk.types import Device, DeviceManifest, EventDetails, ScryptedInterfaceProperty, Storage
|
||||
from typing_extensions import TypedDict
|
||||
import rpc
|
||||
import rpc_reader
|
||||
from scrypted_python.scrypted_sdk import PluginFork, ScryptedStatic
|
||||
from scrypted_python.scrypted_sdk.types import (Device, DeviceManifest,
|
||||
EventDetails,
|
||||
ScryptedInterfaceProperty,
|
||||
Storage)
|
||||
|
||||
try:
|
||||
from typing import TypedDict
|
||||
except:
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import multiprocessing.connection
|
||||
import hashlib
|
||||
|
||||
import rpc
|
||||
import rpc_reader
|
||||
|
||||
class SystemDeviceState(TypedDict):
|
||||
lastEventTime: int
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
from asyncio.futures import Future
|
||||
from typing import Any, Callable, Dict, Mapping, List
|
||||
import traceback
|
||||
import inspect
|
||||
from typing_extensions import TypedDict
|
||||
import traceback
|
||||
from asyncio.futures import Future
|
||||
from typing import Any, Callable, Dict, List, Mapping
|
||||
|
||||
try:
|
||||
from typing import TypedDict
|
||||
except:
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
import weakref
|
||||
|
||||
jsonSerializable = set()
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
const packageJson = require('../package.json');
|
||||
console.log(packageJson.version);
|
||||
async function main() {
|
||||
const response = await fetch('https://registry.npmjs.org/@scrypted/server');
|
||||
const json = await response.json();
|
||||
console.log(json['dist-tags'].latest);
|
||||
// const packageJson = require('../package.json');
|
||||
// console.log(packageJson.version);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -9,6 +9,19 @@ import rimraf from "rimraf";
|
||||
import semver from 'semver';
|
||||
import { ensurePluginVolume } from "./plugin-volume";
|
||||
|
||||
export function defaultNpmExec(args: string[], options: child_process.SpawnOptions) {
|
||||
let npm = 'npm';
|
||||
if (os.platform() === 'win32')
|
||||
npm += '.cmd';
|
||||
const cp = child_process.spawn(npm, args, options);
|
||||
return cp;
|
||||
}
|
||||
|
||||
let npmExecFunction = defaultNpmExec;
|
||||
export function setNpmExecFunction(f: typeof npmExecFunction) {
|
||||
npmExecFunction = f;
|
||||
}
|
||||
|
||||
export function getPluginNodePath(name: string) {
|
||||
const pluginVolume = ensurePluginVolume(name);
|
||||
const nodeMajorVersion = semver.parse(process.version).major;
|
||||
@@ -56,10 +69,7 @@ export async function installOptionalDependencies(console: Console, packageJson:
|
||||
mkdirp.sync(nodePrefix);
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(reduced));
|
||||
|
||||
let npm = 'npm';
|
||||
if (os.platform() === 'win32')
|
||||
npm += '.cmd';
|
||||
const cp = child_process.spawn(npm, ['--prefix', nodePrefix, 'install'], {
|
||||
const cp = npmExecFunction(['--prefix', nodePrefix, 'install'], {
|
||||
cwd: nodePrefix,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
@@ -279,6 +279,15 @@ export class DeviceManagerImpl implements DeviceManager {
|
||||
}
|
||||
}
|
||||
|
||||
function toStorageString(value: any) {
|
||||
if (value === null)
|
||||
return 'null';
|
||||
if (value === undefined)
|
||||
return 'undefined;'
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
class StorageImpl implements Storage {
|
||||
api: PluginAPI;
|
||||
[name: string]: any;
|
||||
@@ -293,17 +302,17 @@ class StorageImpl implements Storage {
|
||||
];
|
||||
private static indexedHandler: ProxyHandler<StorageImpl> = {
|
||||
get(target, property) {
|
||||
if (StorageImpl.allowedMethods.includes(property.toString())) {
|
||||
const prop = property.toString();
|
||||
const f = target[property.toString()];
|
||||
if (prop === 'length')
|
||||
const keyString = property.toString();
|
||||
if (StorageImpl.allowedMethods.includes(keyString)) {
|
||||
const f = target[keyString];
|
||||
if (keyString === 'length')
|
||||
return f;
|
||||
return f.bind(target);
|
||||
}
|
||||
return target.getItem(property.toString());
|
||||
return target.getItem(toStorageString(property));
|
||||
},
|
||||
set(target, property, value): boolean {
|
||||
target.setItem(property.toString(), value);
|
||||
target.setItem(toStorageString(property), value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -351,6 +360,8 @@ class StorageImpl implements Storage {
|
||||
this.api.setStorage(this.nativeId, this.storage);
|
||||
}
|
||||
setItem(key: string, value: string): void {
|
||||
key = toStorageString(key);
|
||||
value = toStorageString(value);
|
||||
if (this.storage[this.prefix + key] === value)
|
||||
return;
|
||||
this.storage[this.prefix + key] = value;
|
||||
|
||||
@@ -94,6 +94,8 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
||||
super(app);
|
||||
this.datastore = datastore;
|
||||
this.app = app;
|
||||
// ensure that all the users are loaded from the db.
|
||||
this.usersService.getAllUsers();
|
||||
|
||||
this.pluginHosts.set('python', (_, pluginId, options) => new PythonRuntimeWorker(pluginId, options));
|
||||
this.pluginHosts.set('node', (mainFilename, pluginId, options) => new NodeForkWorker(mainFilename, pluginId, options));
|
||||
|
||||
@@ -212,7 +212,7 @@ async function start(mainFilename: string, options?: {
|
||||
const sha = hash.digest().toString('hex');
|
||||
|
||||
if (checkHash === sha) {
|
||||
const userToken = validateToken(tokenPart);
|
||||
const userToken = checkValidUserToken(tokenPart);
|
||||
if (userToken) {
|
||||
res.locals.username = userToken.username;
|
||||
res.locals.aclId = userToken.aclId;
|
||||
@@ -420,19 +420,23 @@ async function start(mainFilename: string, options?: {
|
||||
return req.secure ? 'login_user_token' : 'login_user_token_insecure';
|
||||
};
|
||||
|
||||
const validateToken = (token: string) => {
|
||||
const checkValidUserToken = (token: string) => {
|
||||
if (!token)
|
||||
return;
|
||||
try {
|
||||
return UserToken.validateToken(token);
|
||||
const userToken = UserToken.validateToken(token);
|
||||
if (scrypted.usersService.users.has(userToken.username))
|
||||
return userToken;
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('invalid token', e.message);
|
||||
// console.warn('invalid token', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
const getSignedLoginUserTokenRawValue = (req: Request<any>) => req.signedCookies[getLoginUserToken(req)] as string;
|
||||
const getSignedLoginUserToken = (req: Request<any>) => validateToken(getSignedLoginUserTokenRawValue(req));
|
||||
const getSignedLoginUserToken = (req: Request<any>) => {
|
||||
const token = req.signedCookies[getLoginUserToken(req)] as string;
|
||||
return checkValidUserToken(token)
|
||||
};
|
||||
|
||||
app.get('/logout', (req, res) => {
|
||||
res.clearCookie(getLoginUserToken(req));
|
||||
@@ -449,11 +453,7 @@ async function start(mainFilename: string, options?: {
|
||||
if (process.env.SCRYPTED_ADMIN_USERNAME && process.env.SCRYPTED_ADMIN_TOKEN) {
|
||||
let user = await db.tryGet(ScryptedUser, process.env.SCRYPTED_ADMIN_USERNAME);
|
||||
if (!user) {
|
||||
user = new ScryptedUser();
|
||||
user._id = process.env.SCRYPTED_ADMIN_USERNAME;
|
||||
setScryptedUserPassword(user, crypto.randomBytes(8).toString('hex'), Date.now());
|
||||
user.token = crypto.randomBytes(16).toString('hex');
|
||||
await db.upsert(user);
|
||||
user = await scrypted.usersService.addUserInternal(process.env.SCRYPTED_ADMIN_USERNAME, crypto.randomBytes(8).toString('hex'), undefined);
|
||||
hasLogin = true;
|
||||
}
|
||||
}
|
||||
@@ -524,11 +524,7 @@ async function start(mainFilename: string, options?: {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = new ScryptedUser();
|
||||
user._id = username;
|
||||
setScryptedUserPassword(user, password, timestamp);
|
||||
user.token = crypto.randomBytes(16).toString('hex');
|
||||
await db.upsert(user);
|
||||
const user = await scrypted.usersService.addUserInternal(username, password, undefined);
|
||||
hasLogin = true;
|
||||
|
||||
const userToken = new UserToken(username, user.aclId, timestamp);
|
||||
@@ -621,10 +617,9 @@ async function start(mainFilename: string, options?: {
|
||||
|
||||
// cookie auth
|
||||
try {
|
||||
const login_user_token = getSignedLoginUserTokenRawValue(req);
|
||||
if (!login_user_token)
|
||||
const userToken = getSignedLoginUserToken(req);
|
||||
if (!userToken)
|
||||
throw new Error('Not logged in.');
|
||||
const userToken = UserToken.validateToken(login_user_token);
|
||||
|
||||
res.send({
|
||||
...createTokens(userToken),
|
||||
|
||||
@@ -3,14 +3,32 @@ import { ScryptedRuntime } from "../runtime";
|
||||
import crypto from 'crypto';
|
||||
|
||||
export class UsersService {
|
||||
users = new Map<string, ScryptedUser>();
|
||||
usersPromise: Promise<ScryptedUser[]>;
|
||||
|
||||
constructor(public scrypted: ScryptedRuntime) {
|
||||
}
|
||||
|
||||
async getAllUsers() {
|
||||
const users: ScryptedUser[] = [];
|
||||
for await (const user of this.scrypted.datastore.getAll(ScryptedUser)) {
|
||||
users.push(user);
|
||||
private async ensureUsersPromise() {
|
||||
if (!this.usersPromise) {
|
||||
this.usersPromise = (async() => {
|
||||
const users = new Map<string, ScryptedUser>();
|
||||
for await (const user of this.scrypted.datastore.getAll(ScryptedUser)) {
|
||||
users.set(user._id, user);
|
||||
}
|
||||
this.users = users;
|
||||
return [...this.users.values()];
|
||||
})();
|
||||
}
|
||||
return this.usersPromise;
|
||||
}
|
||||
|
||||
private updateUsersPromise() {
|
||||
this.usersPromise = Promise.resolve([...this.users.values()]);
|
||||
}
|
||||
|
||||
async getAllUsers() {
|
||||
const users = await this.ensureUsersPromise();
|
||||
|
||||
return users.map(user => ({
|
||||
username: user._id,
|
||||
@@ -19,19 +37,38 @@ export class UsersService {
|
||||
}
|
||||
|
||||
async removeUser(username: string) {
|
||||
await this.ensureUsersPromise();
|
||||
|
||||
await this.scrypted.datastore.removeId(ScryptedUser, username);
|
||||
this.users.delete(username);
|
||||
this.updateUsersPromise();
|
||||
}
|
||||
|
||||
async removeAllUsers() {
|
||||
await this.ensureUsersPromise();
|
||||
|
||||
await this.scrypted.datastore.removeAll(ScryptedUser);
|
||||
this.users.clear();
|
||||
this.updateUsersPromise();
|
||||
}
|
||||
|
||||
async addUser(username: string, password: string, aclId: string) {
|
||||
async addUserInternal(username: string, password: string, aclId: string) {
|
||||
await this.ensureUsersPromise();
|
||||
|
||||
const user = new ScryptedUser();
|
||||
user._id = username;
|
||||
user.aclId = aclId;
|
||||
user.token = crypto.randomBytes(16).toString('hex');
|
||||
setScryptedUserPassword(user, password, Date.now());
|
||||
await this.scrypted.datastore.upsert(user);
|
||||
this.users.set(username, user);
|
||||
this.updateUsersPromise();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async addUser(username: string, password: string, aclId: string) {
|
||||
await this.addUserInternal(username, password, aclId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ export class UserToken {
|
||||
}
|
||||
|
||||
static validateToken(token: string): UserToken {
|
||||
if (!token)
|
||||
throw new Error('Token not found.');
|
||||
|
||||
let json: {
|
||||
u: string,
|
||||
a: string,
|
||||
|
||||
Reference in New Issue
Block a user