mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Trying code editor (#1173)
Implement hidden dev service packaging tools. Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com> Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
@@ -30,6 +30,11 @@
|
|||||||
"glob": "**/*.svg",
|
"glob": "**/*.svg",
|
||||||
"input": "node_modules/ionicons/dist/ionicons/svg",
|
"input": "node_modules/ionicons/dist/ionicons/svg",
|
||||||
"output": "./svg"
|
"output": "./svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "node_modules/monaco-editor",
|
||||||
|
"output": "assets/monaco-editor/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
|||||||
188
frontend/package-lock.json
generated
188
frontend/package-lock.json
generated
@@ -18,10 +18,9 @@
|
|||||||
"@angular/router": "^13.2.0",
|
"@angular/router": "^13.2.0",
|
||||||
"@ionic/angular": "^6.0.3",
|
"@ionic/angular": "^6.0.3",
|
||||||
"@ionic/storage-angular": "^3.0.6",
|
"@ionic/storage-angular": "^3.0.6",
|
||||||
|
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||||
"@start9labs/argon2": "^0.1.0",
|
"@start9labs/argon2": "^0.1.0",
|
||||||
"@start9labs/emver": "^0.1.5",
|
"@start9labs/emver": "^0.1.5",
|
||||||
"@types/aes-js": "^3.1.1",
|
|
||||||
"@types/pbkdf2": "^3.1.0",
|
|
||||||
"aes-js": "^3.1.2",
|
"aes-js": "^3.1.2",
|
||||||
"ajv": "^6.12.6",
|
"ajv": "^6.12.6",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
@@ -29,7 +28,9 @@
|
|||||||
"dompurify": "^2.3.3",
|
"dompurify": "^2.3.3",
|
||||||
"fast-json-patch": "^3.1.0",
|
"fast-json-patch": "^3.1.0",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"marked": "^4.0.0",
|
"marked": "^4.0.0",
|
||||||
|
"monaco-editor": "^0.32.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"ng-qrcode": "^6.0.0",
|
"ng-qrcode": "^6.0.0",
|
||||||
"patch-db-client": "file: ../../../patch-db/client",
|
"patch-db-client": "file: ../../../patch-db/client",
|
||||||
@@ -45,10 +46,13 @@
|
|||||||
"@angular/compiler-cli": "^13.2.0",
|
"@angular/compiler-cli": "^13.2.0",
|
||||||
"@angular/language-service": "^13.2.0",
|
"@angular/language-service": "^13.2.0",
|
||||||
"@ionic/cli": "^6.18.1",
|
"@ionic/cli": "^6.18.1",
|
||||||
|
"@types/aes-js": "^3.1.1",
|
||||||
"@types/dompurify": "^2.3.3",
|
"@types/dompurify": "^2.3.3",
|
||||||
|
"@types/js-yaml": "^4.0.5",
|
||||||
"@types/marked": "^4.0.0",
|
"@types/marked": "^4.0.0",
|
||||||
"@types/mustache": "^4.1.2",
|
"@types/mustache": "^4.1.2",
|
||||||
"@types/node": "^16.9.1",
|
"@types/node": "^16.9.1",
|
||||||
|
"@types/pbkdf2": "^3.1.0",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"husky": "^4.3.8",
|
"husky": "^4.3.8",
|
||||||
"lint-staged": "^12.1.2",
|
"lint-staged": "^12.1.2",
|
||||||
@@ -2900,6 +2904,28 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@istanbuljs/schema": {
|
"node_modules/@istanbuljs/schema": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
||||||
@@ -2918,6 +2944,18 @@
|
|||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@materia-ui/ngx-monaco-editor": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@materia-ui/ngx-monaco-editor/-/ngx-monaco-editor-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-gTqNQjOGznZxOC0NlmKdKSGCJuTts8YmK4dsTQAGc5IgIV7cZdQWiW6AL742h0ruED6q0cAunEYjXT6jzHBoIQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": ">=13.0.0",
|
||||||
|
"rxjs": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ngtools/webpack": {
|
"node_modules/@ngtools/webpack": {
|
||||||
"version": "13.2.0",
|
"version": "13.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.0.tgz",
|
||||||
@@ -3267,7 +3305,8 @@
|
|||||||
"node_modules/@types/aes-js": {
|
"node_modules/@types/aes-js": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/aes-js/-/aes-js-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aes-js/-/aes-js-3.1.1.tgz",
|
||||||
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw=="
|
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.2",
|
"version": "1.19.2",
|
||||||
@@ -3374,6 +3413,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/js-yaml": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.9",
|
"version": "7.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||||
@@ -3401,7 +3446,8 @@
|
|||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.11.21",
|
"version": "16.11.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz",
|
||||||
"integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A=="
|
"integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@@ -3413,6 +3459,7 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
|
||||||
"integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
|
"integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -3925,13 +3972,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "1.0.10",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"sprintf-js": "~1.0.2"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@@ -8154,13 +8197,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "3.14.1",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^2.0.1"
|
||||||
"esprima": "^4.0.0"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"js-yaml": "bin/js-yaml.js"
|
"js-yaml": "bin/js-yaml.js"
|
||||||
@@ -9312,6 +9353,11 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.32.0.tgz",
|
||||||
|
"integrity": "sha512-r3xvo6XMA/fg3SuVJb+NMxf+fXHO8GPIOLoPFRO2LIf7GbqfV9W7FdddqT9ze+bCtnLd+s4IScnOGCgDQDg4BQ=="
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -12949,6 +12995,15 @@
|
|||||||
"typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev"
|
"typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tslint/node_modules/argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tslint/node_modules/builtin-modules": {
|
"node_modules/tslint/node_modules/builtin-modules": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
||||||
@@ -12964,6 +13019,19 @@
|
|||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tslint/node_modules/js-yaml": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tslint/node_modules/mkdirp": {
|
"node_modules/tslint/node_modules/mkdirp": {
|
||||||
"version": "0.5.5",
|
"version": "0.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
@@ -15914,6 +15982,27 @@
|
|||||||
"get-package-type": "^0.1.0",
|
"get-package-type": "^0.1.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"resolve-from": "^5.0.0"
|
"resolve-from": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"js-yaml": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@istanbuljs/schema": {
|
"@istanbuljs/schema": {
|
||||||
@@ -15928,6 +16017,14 @@
|
|||||||
"integrity": "sha512-fuIOnc81C5iRNevb/XPiM8Khp9bVjreydRQ37rt0C/dY0PAW1DRvEM3WrKX/5rStS5lbgwS0FCgqSndh9tvK5w==",
|
"integrity": "sha512-fuIOnc81C5iRNevb/XPiM8Khp9bVjreydRQ37rt0C/dY0PAW1DRvEM3WrKX/5rStS5lbgwS0FCgqSndh9tvK5w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@materia-ui/ngx-monaco-editor": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@materia-ui/ngx-monaco-editor/-/ngx-monaco-editor-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-gTqNQjOGznZxOC0NlmKdKSGCJuTts8YmK4dsTQAGc5IgIV7cZdQWiW6AL742h0ruED6q0cAunEYjXT6jzHBoIQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@ngtools/webpack": {
|
"@ngtools/webpack": {
|
||||||
"version": "13.2.0",
|
"version": "13.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.0.tgz",
|
||||||
@@ -16191,7 +16288,8 @@
|
|||||||
"@types/aes-js": {
|
"@types/aes-js": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/aes-js/-/aes-js-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aes-js/-/aes-js-3.1.1.tgz",
|
||||||
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw=="
|
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/body-parser": {
|
"@types/body-parser": {
|
||||||
"version": "1.19.2",
|
"version": "1.19.2",
|
||||||
@@ -16298,6 +16396,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/js-yaml": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.9",
|
"version": "7.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||||
@@ -16325,7 +16429,8 @@
|
|||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.11.21",
|
"version": "16.11.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz",
|
||||||
"integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A=="
|
"integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@@ -16337,6 +16442,7 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
|
||||||
"integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
|
"integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -16782,13 +16888,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"argparse": {
|
"argparse": {
|
||||||
"version": "1.0.10",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"sprintf-js": "~1.0.2"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"array-flatten": {
|
"array-flatten": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@@ -19908,13 +20010,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "3.14.1",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^2.0.1"
|
||||||
"esprima": "^4.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jsesc": {
|
"jsesc": {
|
||||||
@@ -20792,6 +20892,11 @@
|
|||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"monaco-editor": {
|
||||||
|
"version": "0.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.32.0.tgz",
|
||||||
|
"integrity": "sha512-r3xvo6XMA/fg3SuVJb+NMxf+fXHO8GPIOLoPFRO2LIf7GbqfV9W7FdddqT9ze+bCtnLd+s4IScnOGCgDQDg4BQ=="
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -23492,6 +23597,15 @@
|
|||||||
"tsutils": "^2.29.0"
|
"tsutils": "^2.29.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"builtin-modules": {
|
"builtin-modules": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
||||||
@@ -23504,6 +23618,16 @@
|
|||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"js-yaml": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.5",
|
"version": "0.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
|||||||
@@ -30,10 +30,9 @@
|
|||||||
"@angular/router": "^13.2.0",
|
"@angular/router": "^13.2.0",
|
||||||
"@ionic/angular": "^6.0.3",
|
"@ionic/angular": "^6.0.3",
|
||||||
"@ionic/storage-angular": "^3.0.6",
|
"@ionic/storage-angular": "^3.0.6",
|
||||||
|
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||||
"@start9labs/argon2": "^0.1.0",
|
"@start9labs/argon2": "^0.1.0",
|
||||||
"@start9labs/emver": "^0.1.5",
|
"@start9labs/emver": "^0.1.5",
|
||||||
"@types/aes-js": "^3.1.1",
|
|
||||||
"@types/pbkdf2": "^3.1.0",
|
|
||||||
"aes-js": "^3.1.2",
|
"aes-js": "^3.1.2",
|
||||||
"ajv": "^6.12.6",
|
"ajv": "^6.12.6",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
@@ -41,7 +40,9 @@
|
|||||||
"dompurify": "^2.3.3",
|
"dompurify": "^2.3.3",
|
||||||
"fast-json-patch": "^3.1.0",
|
"fast-json-patch": "^3.1.0",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"marked": "^4.0.0",
|
"marked": "^4.0.0",
|
||||||
|
"monaco-editor": "^0.32.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"ng-qrcode": "^6.0.0",
|
"ng-qrcode": "^6.0.0",
|
||||||
"patch-db-client": "file: ../../../patch-db/client",
|
"patch-db-client": "file: ../../../patch-db/client",
|
||||||
@@ -57,10 +58,13 @@
|
|||||||
"@angular/compiler-cli": "^13.2.0",
|
"@angular/compiler-cli": "^13.2.0",
|
||||||
"@angular/language-service": "^13.2.0",
|
"@angular/language-service": "^13.2.0",
|
||||||
"@ionic/cli": "^6.18.1",
|
"@ionic/cli": "^6.18.1",
|
||||||
|
"@types/aes-js": "^3.1.1",
|
||||||
"@types/dompurify": "^2.3.3",
|
"@types/dompurify": "^2.3.3",
|
||||||
|
"@types/js-yaml": "^4.0.5",
|
||||||
"@types/marked": "^4.0.0",
|
"@types/marked": "^4.0.0",
|
||||||
"@types/mustache": "^4.1.2",
|
"@types/mustache": "^4.1.2",
|
||||||
"@types/node": "^16.9.1",
|
"@types/node": "^16.9.1",
|
||||||
|
"@types/pbkdf2": "^3.1.0",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"husky": "^4.3.8",
|
"husky": "^4.3.8",
|
||||||
"lint-staged": "^12.1.2",
|
"lint-staged": "^12.1.2",
|
||||||
|
|||||||
@@ -12,30 +12,52 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'login',
|
path: 'login',
|
||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
loadChildren: () => import('./pages/login/login.module').then(m => m.LoginPageModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/login/login.module').then(m => m.LoginPageModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'embassy',
|
path: 'embassy',
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
canActivateChild: [AuthGuard],
|
canActivateChild: [AuthGuard],
|
||||||
loadChildren: () => import('./pages/server-routes/server-routing.module').then(m => m.ServerRoutingModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/server-routes/server-routing.module').then(
|
||||||
|
m => m.ServerRoutingModule,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'marketplace',
|
path: 'marketplace',
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
canActivateChild: [AuthGuard],
|
canActivateChild: [AuthGuard],
|
||||||
loadChildren: () => import('./pages/marketplace-routes/marketplace-routing.module').then(m => m.MarketplaceRoutingModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/marketplace-routes/marketplace-routing.module').then(
|
||||||
|
m => m.MarketplaceRoutingModule,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'notifications',
|
path: 'notifications',
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
loadChildren: () => import('./pages/notifications/notifications.module').then(m => m.NotificationsPageModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/notifications/notifications.module').then(
|
||||||
|
m => m.NotificationsPageModule,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'services',
|
path: 'services',
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
canActivateChild: [AuthGuard],
|
canActivateChild: [AuthGuard],
|
||||||
loadChildren: () => import('./pages/apps-routes/apps-routing.module').then(m => m.AppsRoutingModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/apps-routes/apps-routing.module').then(
|
||||||
|
m => m.AppsRoutingModule,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'developer',
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
canActivateChild: [AuthGuard],
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./pages/developer-routes/developer-routing.module').then(
|
||||||
|
m => m.DeveloperRoutingModule,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -50,4 +72,4 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule {}
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
<ion-app>
|
<ion-app>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-split-pane [disabled]="!showMenu" (ionSplitPaneVisible)="splitPaneVisible($event)" contentId="main-content">
|
<ion-split-pane
|
||||||
|
[disabled]="!showMenu"
|
||||||
|
(ionSplitPaneVisible)="splitPaneVisible($event)"
|
||||||
|
contentId="main-content"
|
||||||
|
>
|
||||||
<ion-menu contentId="main-content" type="overlay">
|
<ion-menu contentId="main-content" type="overlay">
|
||||||
<ion-content color="light" scrollY="false">
|
<ion-content color="light" scrollY="false">
|
||||||
<div style="text-align: center;" class="ion-padding">
|
<div style="text-align: center" class="ion-padding">
|
||||||
<img style="width: 45%; cursor: pointer;" src="assets/img/logo.png" (click)="goToWebsite()">
|
<img
|
||||||
|
style="width: 45%; cursor: pointer"
|
||||||
|
src="assets/img/logo.png"
|
||||||
|
(click)="goToWebsite()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<ion-item-group style="padding: 30px 0px;">
|
<ion-item-group style="padding: 30px 0px">
|
||||||
<ion-menu-toggle auto-hide="false" *ngFor="let page of appPages; let i = index">
|
<ion-menu-toggle
|
||||||
|
auto-hide="false"
|
||||||
|
*ngFor="let page of appPages; let i = index"
|
||||||
|
>
|
||||||
<ion-item
|
<ion-item
|
||||||
style="padding-left: 10px;"
|
style="padding-left: 10px"
|
||||||
color="transparent"
|
color="transparent"
|
||||||
button
|
button
|
||||||
(click)="selectedIndex = i"
|
(click)="selectedIndex = i"
|
||||||
@@ -18,38 +29,69 @@
|
|||||||
[routerLink]="[page.url]"
|
[routerLink]="[page.url]"
|
||||||
lines="none"
|
lines="none"
|
||||||
detail="false"
|
detail="false"
|
||||||
|
*ngIf="
|
||||||
|
page.url !== '/developer' ||
|
||||||
|
(localStorageService.showDevTools$ | async)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<ion-icon slot="start" [name]="page.icon" [class]="selectedIndex === i ? 'bold' : 'dim'"></ion-icon>
|
<ion-icon
|
||||||
|
slot="start"
|
||||||
|
[name]="page.icon"
|
||||||
|
[class]="selectedIndex === i ? 'bold' : 'dim'"
|
||||||
|
></ion-icon>
|
||||||
<ion-label
|
<ion-label
|
||||||
style="font-family: 'Montserrat';"
|
style="font-family: 'Montserrat'"
|
||||||
[class]="selectedIndex === i ? 'bold' : 'dim'"
|
[class]="selectedIndex === i ? 'bold' : 'dim'"
|
||||||
>
|
>
|
||||||
{{ page.title }}
|
{{ page.title }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-badge *ngIf="page.url === '/notifications' && unreadCount" color="danger" style="margin-right: 3%;" [class.selected-badge]="selectedIndex == i">{{ unreadCount }}</ion-badge>
|
<ion-badge
|
||||||
|
*ngIf="page.url === '/notifications' && unreadCount"
|
||||||
|
color="danger"
|
||||||
|
style="margin-right: 3%"
|
||||||
|
[class.selected-badge]="selectedIndex == i"
|
||||||
|
>{{ unreadCount }}</ion-badge
|
||||||
|
>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-menu-toggle>
|
</ion-menu-toggle>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
<div style="text-align: center; height: 75px; position:absolute; bottom:0; right:0; width: 100%;">
|
<div
|
||||||
<div class="divider" style="margin-bottom: 10px;"></div>
|
style="
|
||||||
|
text-align: center;
|
||||||
|
height: 75px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="divider" style="margin-bottom: 10px"></div>
|
||||||
<ion-menu-toggle auto-hide="false">
|
<ion-menu-toggle auto-hide="false">
|
||||||
<ion-item button lines="none" style="--background: transparent; margin-bottom: 86px; text-align: center;" fill="clear" (click)="presentAlertLogout()">
|
<ion-item
|
||||||
<ion-label><ion-text
|
button
|
||||||
style="font-family: 'Montserrat';"
|
lines="none"
|
||||||
color="dark"
|
style="
|
||||||
>
|
--background: transparent;
|
||||||
|
margin-bottom: 86px;
|
||||||
|
text-align: center;
|
||||||
|
"
|
||||||
|
fill="clear"
|
||||||
|
(click)="presentAlertLogout()"
|
||||||
|
>
|
||||||
|
<ion-label
|
||||||
|
><ion-text style="font-family: 'Montserrat'" color="dark">
|
||||||
Log Out
|
Log Out
|
||||||
</ion-text></ion-label>
|
</ion-text></ion-label
|
||||||
|
>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-menu-toggle>
|
</ion-menu-toggle>
|
||||||
</div>
|
</div>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
</ion-menu>
|
</ion-menu>
|
||||||
<ion-router-outlet id="main-content"></ion-router-outlet>
|
<ion-router-outlet id="main-content"></ion-router-outlet>
|
||||||
|
|
||||||
</ion-split-pane>
|
</ion-split-pane>
|
||||||
|
|
||||||
<section id="preload" style="display: none;">
|
<section id="preload" style="display: none">
|
||||||
<!-- 3rd party components -->
|
<!-- 3rd party components -->
|
||||||
<qr-code value="hello"></qr-code>
|
<qr-code value="hello"></qr-code>
|
||||||
|
|
||||||
@@ -66,7 +108,8 @@
|
|||||||
<ion-icon name="checkmark"></ion-icon>
|
<ion-icon name="checkmark"></ion-icon>
|
||||||
<ion-icon name="chevron-down"></ion-icon>
|
<ion-icon name="chevron-down"></ion-icon>
|
||||||
<ion-icon name="chevron-up"></ion-icon>
|
<ion-icon name="chevron-up"></ion-icon>
|
||||||
<ion-icon name="chevron-forward"></ion-icon> <!-- needed for detail="true" on ion-item button -->
|
<ion-icon name="chevron-forward"></ion-icon>
|
||||||
|
<!-- needed for detail="true" on ion-item button -->
|
||||||
<ion-icon name="close"></ion-icon>
|
<ion-icon name="close"></ion-icon>
|
||||||
<ion-icon name="cloud-outline"></ion-icon>
|
<ion-icon name="cloud-outline"></ion-icon>
|
||||||
<ion-icon name="cloud-done-outline"></ion-icon>
|
<ion-icon name="cloud-done-outline"></ion-icon>
|
||||||
@@ -90,6 +133,7 @@
|
|||||||
<ion-icon name="folder-open-outline"></ion-icon>
|
<ion-icon name="folder-open-outline"></ion-icon>
|
||||||
<ion-icon name="grid-outline"></ion-icon>
|
<ion-icon name="grid-outline"></ion-icon>
|
||||||
<ion-icon name="help-circle-outline"></ion-icon>
|
<ion-icon name="help-circle-outline"></ion-icon>
|
||||||
|
<ion-icon name="hammer-outline"></ion-icon>
|
||||||
<ion-icon name="home-outline"></ion-icon>
|
<ion-icon name="home-outline"></ion-icon>
|
||||||
<ion-icon name="information-circle-outline"></ion-icon>
|
<ion-icon name="information-circle-outline"></ion-icon>
|
||||||
<ion-icon name="key-outline"></ion-icon>
|
<ion-icon name="key-outline"></ion-icon>
|
||||||
@@ -119,7 +163,7 @@
|
|||||||
<ion-icon name="trash-outline"></ion-icon>
|
<ion-icon name="trash-outline"></ion-icon>
|
||||||
<ion-icon name="warning-outline"></ion-icon>
|
<ion-icon name="warning-outline"></ion-icon>
|
||||||
<ion-icon name="wifi"></ion-icon>
|
<ion-icon name="wifi"></ion-icon>
|
||||||
|
|
||||||
<!-- Ionic components -->
|
<!-- Ionic components -->
|
||||||
<ion-action-sheet></ion-action-sheet>
|
<ion-action-sheet></ion-action-sheet>
|
||||||
<ion-alert></ion-alert>
|
<ion-alert></ion-alert>
|
||||||
@@ -141,7 +185,9 @@
|
|||||||
<ion-refresher slot="fixed"></ion-refresher>
|
<ion-refresher slot="fixed"></ion-refresher>
|
||||||
<ion-refresher-content pullingContent="lines"></ion-refresher-content>
|
<ion-refresher-content pullingContent="lines"></ion-refresher-content>
|
||||||
<ion-infinite-scroll></ion-infinite-scroll>
|
<ion-infinite-scroll></ion-infinite-scroll>
|
||||||
<ion-infinite-scroll-content loadingSpinner="lines"></ion-infinite-scroll-content>
|
<ion-infinite-scroll-content
|
||||||
|
loadingSpinner="lines"
|
||||||
|
></ion-infinite-scroll-content>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
<ion-input></ion-input>
|
<ion-input></ion-input>
|
||||||
<ion-item></ion-item>
|
<ion-item></ion-item>
|
||||||
@@ -172,26 +218,38 @@
|
|||||||
<ion-footer
|
<ion-footer
|
||||||
[ngStyle]="{
|
[ngStyle]="{
|
||||||
'max-height': osUpdateProgress ? '100px' : '0px',
|
'max-height': osUpdateProgress ? '100px' : '0px',
|
||||||
'overflow': 'hidden',
|
overflow: 'hidden',
|
||||||
'transition-property': 'max-height',
|
'transition-property': 'max-height',
|
||||||
'transition-duration': '1s',
|
'transition-duration': '1s',
|
||||||
'transition-delay': '.05s'
|
'transition-delay': '.05s'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ion-toolbar style="border-top: 1px solid var(--ion-color-dark);" color="light">
|
<ion-toolbar
|
||||||
|
style="border-top: 1px solid var(--ion-color-dark)"
|
||||||
|
color="light"
|
||||||
|
>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<ion-list-header>
|
<ion-list-header>
|
||||||
<ion-label>Downloading EOS: {{ (100 * (osUpdateProgress?.downloaded || 1) / (osUpdateProgress?.size || 1)).toFixed(0) }}%</ion-label>
|
<ion-label
|
||||||
|
>Downloading EOS:
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(100 * (osUpdateProgress?.downloaded || 1)) /
|
||||||
|
(osUpdateProgress?.size || 1)
|
||||||
|
).toFixed(0)
|
||||||
|
}}%</ion-label
|
||||||
|
>
|
||||||
</ion-list-header>
|
</ion-list-header>
|
||||||
<div style="padding: 0 16px 16px 16px;">
|
<div style="padding: 0 16px 16px 16px">
|
||||||
<ion-progress-bar
|
<ion-progress-bar
|
||||||
color="secondary"
|
color="secondary"
|
||||||
[value]="osUpdateProgress && osUpdateProgress.downloaded / osUpdateProgress.size"
|
[value]="
|
||||||
|
osUpdateProgress &&
|
||||||
|
osUpdateProgress.downloaded / osUpdateProgress.size
|
||||||
|
"
|
||||||
></ion-progress-bar>
|
></ion-progress-bar>
|
||||||
</div>
|
</div>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,19 +3,33 @@ import { Storage } from '@ionic/storage-angular'
|
|||||||
import { AuthService, AuthState } from './services/auth.service'
|
import { AuthService, AuthState } from './services/auth.service'
|
||||||
import { ApiService } from './services/api/embassy-api.service'
|
import { ApiService } from './services/api/embassy-api.service'
|
||||||
import { Router, RoutesRecognized } from '@angular/router'
|
import { Router, RoutesRecognized } from '@angular/router'
|
||||||
import { debounceTime, distinctUntilChanged, filter, finalize, take, takeWhile } from 'rxjs/operators'
|
import {
|
||||||
import { AlertController, IonicSafeString, LoadingController, ToastController } from '@ionic/angular'
|
debounceTime,
|
||||||
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
|
take,
|
||||||
|
} from 'rxjs/operators'
|
||||||
|
import {
|
||||||
|
AlertController,
|
||||||
|
IonicSafeString,
|
||||||
|
LoadingController,
|
||||||
|
ToastController,
|
||||||
|
} from '@ionic/angular'
|
||||||
import { Emver } from './services/emver.service'
|
import { Emver } from './services/emver.service'
|
||||||
import { SplitPaneTracker } from './services/split-pane.service'
|
import { SplitPaneTracker } from './services/split-pane.service'
|
||||||
import { ToastButton } from '@ionic/core'
|
import { ToastButton } from '@ionic/core'
|
||||||
import { PatchDbService } from './services/patch-db/patch-db.service'
|
import { PatchDbService } from './services/patch-db/patch-db.service'
|
||||||
import { ServerStatus } from './services/patch-db/data-model'
|
import { ServerStatus } from './services/patch-db/data-model'
|
||||||
import { ConnectionFailure, ConnectionService } from './services/connection.service'
|
import {
|
||||||
|
ConnectionFailure,
|
||||||
|
ConnectionService,
|
||||||
|
} from './services/connection.service'
|
||||||
import { StartupAlertsService } from './services/startup-alerts.service'
|
import { StartupAlertsService } from './services/startup-alerts.service'
|
||||||
import { ConfigService } from './services/config.service'
|
import { ConfigService } from './services/config.service'
|
||||||
import { debounce, isEmptyObject, pauseFor } from './util/misc.util'
|
import { debounce, isEmptyObject } from './util/misc.util'
|
||||||
import { ErrorToastService } from './services/error-toast.service'
|
import { ErrorToastService } from './services/error-toast.service'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
|
import { LocalStorageService } from './services/local-storage.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@@ -25,7 +39,7 @@ import { Subscription } from 'rxjs'
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
@HostListener('document:keydown.enter', ['$event'])
|
@HostListener('document:keydown.enter', ['$event'])
|
||||||
@debounce()
|
@debounce()
|
||||||
handleKeyboardEvent () {
|
handleKeyboardEvent() {
|
||||||
const elems = document.getElementsByClassName('enter-click')
|
const elems = document.getElementsByClassName('enter-click')
|
||||||
const elem = elems[elems.length - 1] as HTMLButtonElement
|
const elem = elems[elems.length - 1] as HTMLButtonElement
|
||||||
if (!elem || elem.classList.contains('no-click') || elem.disabled) return
|
if (!elem || elem.classList.contains('no-click') || elem.disabled) return
|
||||||
@@ -41,7 +55,7 @@ export class AppComponent {
|
|||||||
serverName: string
|
serverName: string
|
||||||
unreadCount: number
|
unreadCount: number
|
||||||
subscriptions: Subscription[] = []
|
subscriptions: Subscription[] = []
|
||||||
osUpdateProgress: { size: number, downloaded: number }
|
osUpdateProgress: { size: number; downloaded: number }
|
||||||
appPages = [
|
appPages = [
|
||||||
{
|
{
|
||||||
title: 'Services',
|
title: 'Services',
|
||||||
@@ -63,9 +77,14 @@ export class AppComponent {
|
|||||||
url: '/notifications',
|
url: '/notifications',
|
||||||
icon: 'notifications-outline',
|
icon: 'notifications-outline',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Developer Tools',
|
||||||
|
url: '/developer',
|
||||||
|
icon: 'hammer-outline',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
constructor (
|
constructor(
|
||||||
private readonly storage: Storage,
|
private readonly storage: Storage,
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly router: Router,
|
private readonly router: Router,
|
||||||
@@ -77,23 +96,24 @@ export class AppComponent {
|
|||||||
private readonly startupAlertsService: StartupAlertsService,
|
private readonly startupAlertsService: StartupAlertsService,
|
||||||
private readonly toastCtrl: ToastController,
|
private readonly toastCtrl: ToastController,
|
||||||
private readonly errToast: ErrorToastService,
|
private readonly errToast: ErrorToastService,
|
||||||
private readonly patch: PatchDbService,
|
|
||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
private readonly zone: NgZone,
|
private readonly zone: NgZone,
|
||||||
readonly splitPane: SplitPaneTracker,
|
public readonly splitPane: SplitPaneTracker,
|
||||||
|
public readonly patch: PatchDbService,
|
||||||
|
public readonly localStorageService: LocalStorageService,
|
||||||
) {
|
) {
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
async init () {
|
async init() {
|
||||||
await this.storage.create()
|
await this.storage.create()
|
||||||
await this.authService.init()
|
await this.authService.init()
|
||||||
|
await this.localStorageService.init()
|
||||||
|
|
||||||
this.router.initialNavigation()
|
this.router.initialNavigation()
|
||||||
|
|
||||||
// watch auth
|
// watch auth
|
||||||
this.authService.watch$()
|
this.authService.watch$().subscribe(async auth => {
|
||||||
.subscribe(async auth => {
|
|
||||||
// VERIFIED
|
// VERIFIED
|
||||||
if (auth === AuthState.VERIFIED) {
|
if (auth === AuthState.VERIFIED) {
|
||||||
await this.patch.start()
|
await this.patch.start()
|
||||||
@@ -114,26 +134,27 @@ export class AppComponent {
|
|||||||
// watch status to display/hide maintenance page
|
// watch status to display/hide maintenance page
|
||||||
])
|
])
|
||||||
|
|
||||||
this.patch.watch$()
|
this.patch
|
||||||
.pipe(
|
.watch$()
|
||||||
filter(obj => !isEmptyObject(obj)),
|
.pipe(
|
||||||
take(1),
|
filter(obj => !isEmptyObject(obj)),
|
||||||
)
|
take(1),
|
||||||
.subscribe(_ => {
|
)
|
||||||
this.subscriptions = this.subscriptions.concat([
|
.subscribe(_ => {
|
||||||
// watch status to present toast for updated state
|
this.subscriptions = this.subscriptions.concat([
|
||||||
this.watchStatus(),
|
// watch status to present toast for updated state
|
||||||
// watch update-progress to present progress bar when server is updating
|
this.watchStatus(),
|
||||||
this.watchUpdateProgress(),
|
// watch update-progress to present progress bar when server is updating
|
||||||
// watch version to refresh browser window
|
this.watchUpdateProgress(),
|
||||||
this.watchVersion(),
|
// watch version to refresh browser window
|
||||||
// watch unread notification count to display toast
|
this.watchVersion(),
|
||||||
this.watchNotifications(),
|
// watch unread notification count to display toast
|
||||||
// run startup alerts
|
this.watchNotifications(),
|
||||||
this.startupAlertsService.runChecks(),
|
// run startup alerts
|
||||||
])
|
this.startupAlertsService.runChecks(),
|
||||||
})
|
])
|
||||||
// UNVERIFIED
|
})
|
||||||
|
// UNVERIFIED
|
||||||
} else if (auth === AuthState.UNVERIFIED) {
|
} else if (auth === AuthState.UNVERIFIED) {
|
||||||
this.subscriptions.forEach(sub => sub.unsubscribe())
|
this.subscriptions.forEach(sub => sub.unsubscribe())
|
||||||
this.subscriptions = []
|
this.subscriptions = []
|
||||||
@@ -151,20 +172,22 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async goToWebsite (): Promise<void> {
|
async goToWebsite(): Promise<void> {
|
||||||
let url: string
|
let url: string
|
||||||
if (this.config.isTor()) {
|
if (this.config.isTor()) {
|
||||||
url = 'http://privacy34kn4ez3y3nijweec6w4g54i3g54sdv7r5mr6soma3w4begyd.onion'
|
url =
|
||||||
|
'http://privacy34kn4ez3y3nijweec6w4g54i3g54sdv7r5mr6soma3w4begyd.onion'
|
||||||
} else {
|
} else {
|
||||||
url = 'https://start9.com'
|
url = 'https://start9.com'
|
||||||
}
|
}
|
||||||
window.open(url, '_blank', 'noreferrer')
|
window.open(url, '_blank', 'noreferrer')
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentAlertLogout () {
|
async presentAlertLogout() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Caution',
|
header: 'Caution',
|
||||||
message: 'Do you know your password? If you log out and forget your password, you may permanently lose access to your Embassy.',
|
message:
|
||||||
|
'Do you know your password? If you log out and forget your password, you may permanently lose access to your Embassy.',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Cancel',
|
text: 'Cancel',
|
||||||
@@ -184,97 +207,97 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// should wipe cache independant of actual BE logout
|
// should wipe cache independant of actual BE logout
|
||||||
private async logout () {
|
private async logout() {
|
||||||
this.embassyApi.logout({ })
|
this.embassyApi.logout({})
|
||||||
this.authService.setUnverified()
|
this.authService.setUnverified()
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchConnection (): Subscription {
|
private watchConnection(): Subscription {
|
||||||
return this.connectionService.watchFailure$()
|
return this.connectionService
|
||||||
.pipe(
|
.watchFailure$()
|
||||||
distinctUntilChanged(),
|
.pipe(distinctUntilChanged(), debounceTime(500))
|
||||||
debounceTime(500),
|
.subscribe(async connectionFailure => {
|
||||||
)
|
if (connectionFailure === ConnectionFailure.None) {
|
||||||
.subscribe(async connectionFailure => {
|
if (this.offlineToast) {
|
||||||
if (connectionFailure === ConnectionFailure.None) {
|
await this.offlineToast.dismiss()
|
||||||
if (this.offlineToast) {
|
this.offlineToast = undefined
|
||||||
await this.offlineToast.dismiss()
|
}
|
||||||
this.offlineToast = undefined
|
} else {
|
||||||
|
let message: string | IonicSafeString
|
||||||
|
let link: string
|
||||||
|
switch (connectionFailure) {
|
||||||
|
case ConnectionFailure.Network:
|
||||||
|
message = 'Phone or computer has no network connection.'
|
||||||
|
break
|
||||||
|
case ConnectionFailure.Tor:
|
||||||
|
message = 'Browser unable to connect over Tor.'
|
||||||
|
link =
|
||||||
|
'https://docs.start9.com/support/FAQ/troubleshooting.html#tor-failure'
|
||||||
|
break
|
||||||
|
case ConnectionFailure.Lan:
|
||||||
|
message = 'Embassy not found on Local Area Network.'
|
||||||
|
link =
|
||||||
|
'https://docs.start9.com/support/FAQ/troubleshooting.html#lan-failure'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
await this.presentToastOffline(message, link)
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
let message: string | IonicSafeString
|
|
||||||
let link: string
|
|
||||||
switch (connectionFailure) {
|
|
||||||
case ConnectionFailure.Network:
|
|
||||||
message = 'Phone or computer has no network connection.'
|
|
||||||
break
|
|
||||||
case ConnectionFailure.Tor:
|
|
||||||
message = 'Browser unable to connect over Tor.'
|
|
||||||
link = 'https://docs.start9.com/support/FAQ/troubleshooting.html#tor-failure'
|
|
||||||
break
|
|
||||||
case ConnectionFailure.Lan:
|
|
||||||
message = 'Embassy not found on Local Area Network.'
|
|
||||||
link = 'https://docs.start9.com/support/FAQ/troubleshooting.html#lan-failure'
|
|
||||||
break
|
|
||||||
}
|
|
||||||
await this.presentToastOffline(message, link)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchRouter (): Subscription {
|
private watchRouter(): Subscription {
|
||||||
return this.router.events
|
return this.router.events
|
||||||
.pipe(
|
.pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects))
|
||||||
filter((e: RoutesRecognized) => !!e.urlAfterRedirects),
|
.subscribe(e => {
|
||||||
)
|
const appPageIndex = this.appPages.findIndex(appPage =>
|
||||||
.subscribe(e => {
|
e.urlAfterRedirects.startsWith(appPage.url),
|
||||||
const appPageIndex = this.appPages.findIndex(
|
)
|
||||||
appPage => e.urlAfterRedirects.startsWith(appPage.url),
|
if (appPageIndex > -1) this.selectedIndex = appPageIndex
|
||||||
)
|
})
|
||||||
if (appPageIndex > -1) this.selectedIndex = appPageIndex
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchStatus (): Subscription {
|
private watchStatus(): Subscription {
|
||||||
return this.patch.watch$('server-info', 'status')
|
return this.patch.watch$('server-info', 'status').subscribe(status => {
|
||||||
.subscribe(status => {
|
|
||||||
if (status === ServerStatus.Updated && !this.updateToast) {
|
if (status === ServerStatus.Updated && !this.updateToast) {
|
||||||
this.presentToastUpdated()
|
this.presentToastUpdated()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchUpdateProgress (): Subscription {
|
private watchUpdateProgress(): Subscription {
|
||||||
return this.patch.watch$('server-info', 'update-progress')
|
return this.patch
|
||||||
.subscribe(progress => {
|
.watch$('server-info', 'update-progress')
|
||||||
this.osUpdateProgress = progress
|
.subscribe(progress => {
|
||||||
})
|
this.osUpdateProgress = progress
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchVersion (): Subscription {
|
private watchVersion(): Subscription {
|
||||||
return this.patch.watch$('server-info', 'version')
|
return this.patch.watch$('server-info', 'version').subscribe(version => {
|
||||||
.subscribe(version => {
|
|
||||||
if (this.emver.compare(this.config.version, version) !== 0) {
|
if (this.emver.compare(this.config.version, version) !== 0) {
|
||||||
this.presentAlertRefreshNeeded()
|
this.presentAlertRefreshNeeded()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchNotifications (): Subscription {
|
private watchNotifications(): Subscription {
|
||||||
let previous: number
|
let previous: number
|
||||||
return this.patch.watch$('server-info', 'unread-notification-count')
|
return this.patch
|
||||||
.subscribe(count => {
|
.watch$('server-info', 'unread-notification-count')
|
||||||
this.unreadCount = count
|
.subscribe(count => {
|
||||||
if (previous !== undefined && count > previous) this.presentToastNotifications()
|
this.unreadCount = count
|
||||||
previous = count
|
if (previous !== undefined && count > previous)
|
||||||
})
|
this.presentToastNotifications()
|
||||||
|
previous = count
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentAlertRefreshNeeded () {
|
private async presentAlertRefreshNeeded() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
backdropDismiss: false,
|
backdropDismiss: false,
|
||||||
header: 'Refresh Needed',
|
header: 'Refresh Needed',
|
||||||
message: 'Your user interface is cached and out of date. Hard refresh the page to get the latest UI.',
|
message:
|
||||||
|
'Your user interface is cached and out of date. Hard refresh the page to get the latest UI.',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Refresh Page',
|
text: 'Refresh Page',
|
||||||
@@ -288,12 +311,13 @@ export class AppComponent {
|
|||||||
await alert.present()
|
await alert.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentToastUpdated () {
|
private async presentToastUpdated() {
|
||||||
if (this.updateToast) return
|
if (this.updateToast) return
|
||||||
|
|
||||||
this.updateToast = await this.toastCtrl.create({
|
this.updateToast = await this.toastCtrl.create({
|
||||||
header: 'EOS download complete!',
|
header: 'EOS download complete!',
|
||||||
message: 'Restart your Embassy for these updates to take effect. It can take several minutes to come back online.',
|
message:
|
||||||
|
'Restart your Embassy for these updates to take effect. It can take several minutes to come back online.',
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
cssClass: 'success-toast',
|
cssClass: 'success-toast',
|
||||||
@@ -317,7 +341,7 @@ export class AppComponent {
|
|||||||
await this.updateToast.present()
|
await this.updateToast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentToastNotifications () {
|
private async presentToastNotifications() {
|
||||||
if (this.notificationToast) return
|
if (this.notificationToast) return
|
||||||
|
|
||||||
this.notificationToast = await this.toastCtrl.create({
|
this.notificationToast = await this.toastCtrl.create({
|
||||||
@@ -337,7 +361,9 @@ export class AppComponent {
|
|||||||
side: 'end',
|
side: 'end',
|
||||||
text: 'View',
|
text: 'View',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.router.navigate(['/notifications'], { queryParams: { toast: true } })
|
this.router.navigate(['/notifications'], {
|
||||||
|
queryParams: { toast: true },
|
||||||
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -345,7 +371,10 @@ export class AppComponent {
|
|||||||
await this.notificationToast.present()
|
await this.notificationToast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentToastOffline (message: string | IonicSafeString, link?: string) {
|
private async presentToastOffline(
|
||||||
|
message: string | IonicSafeString,
|
||||||
|
link?: string,
|
||||||
|
) {
|
||||||
if (this.offlineToast) {
|
if (this.offlineToast) {
|
||||||
this.offlineToast.message = message
|
this.offlineToast.message = message
|
||||||
return
|
return
|
||||||
@@ -362,16 +391,14 @@ export class AppComponent {
|
|||||||
]
|
]
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
buttons.push(
|
buttons.push({
|
||||||
{
|
side: 'end',
|
||||||
side: 'end',
|
text: 'View solutions',
|
||||||
text: 'View solutions',
|
handler: () => {
|
||||||
handler: () => {
|
window.open(link, '_blank', 'noreferrer')
|
||||||
window.open(link, '_blank', 'noreferrer')
|
return false
|
||||||
return false
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.offlineToast = await this.toastCtrl.create({
|
this.offlineToast = await this.toastCtrl.create({
|
||||||
@@ -385,7 +412,7 @@ export class AppComponent {
|
|||||||
await this.offlineToast.present()
|
await this.offlineToast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restart (): Promise<void> {
|
private async restart(): Promise<void> {
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
spinner: 'lines',
|
spinner: 'lines',
|
||||||
message: 'Restarting...',
|
message: 'Restarting...',
|
||||||
@@ -394,7 +421,7 @@ export class AppComponent {
|
|||||||
await loader.present()
|
await loader.present()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.embassyApi.restartServer({ })
|
await this.embassyApi.restartServer({})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errToast.present(e)
|
this.errToast.present(e)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -402,7 +429,7 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
splitPaneVisible (e: any) {
|
splitPaneVisible(e: any) {
|
||||||
this.splitPane.sidebarOpen$.next(e.detail.visible)
|
this.splitPane.sidebarOpen$.next(e.detail.visible)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { GlobalErrorHandler } from './services/global-error-handler.service'
|
|||||||
import { MockApiService } from './services/api/embassy-mock-api.service'
|
import { MockApiService } from './services/api/embassy-mock-api.service'
|
||||||
import { LiveApiService } from './services/api/embassy-live-api.service'
|
import { LiveApiService } from './services/api/embassy-live-api.service'
|
||||||
import { WorkspaceConfig } from '@shared'
|
import { WorkspaceConfig } from '@shared'
|
||||||
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||||
|
|
||||||
const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
|||||||
MarkdownPageModule,
|
MarkdownPageModule,
|
||||||
GenericInputComponentModule,
|
GenericInputComponentModule,
|
||||||
SharingModule,
|
SharingModule,
|
||||||
|
MonacoEditorModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
|
|||||||
@@ -9,20 +9,22 @@ import { getErrorMessage } from 'src/app/services/error-toast.service'
|
|||||||
styleUrls: ['./markdown.page.scss'],
|
styleUrls: ['./markdown.page.scss'],
|
||||||
})
|
})
|
||||||
export class MarkdownPage {
|
export class MarkdownPage {
|
||||||
@Input() contentUrl: string
|
@Input() contentUrl?: string
|
||||||
|
@Input() content?: string
|
||||||
@Input() title: string
|
@Input() title: string
|
||||||
content: string
|
|
||||||
loading = true
|
loading = true
|
||||||
loadingError: string | IonicSafeString
|
loadingError: string | IonicSafeString
|
||||||
|
|
||||||
constructor (
|
constructor(
|
||||||
private readonly modalCtrl: ModalController,
|
private readonly modalCtrl: ModalController,
|
||||||
private readonly embassyApi: ApiService,
|
private readonly embassyApi: ApiService,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit() {
|
||||||
try {
|
try {
|
||||||
this.content = await this.embassyApi.getStatic(this.contentUrl)
|
if (!this.content) {
|
||||||
|
this.content = await this.embassyApi.getStatic(this.contentUrl)
|
||||||
|
}
|
||||||
const links = document.links
|
const links = document.links
|
||||||
for (let i = 0, linksLength = links.length; i < linksLength; i++) {
|
for (let i = 0, linksLength = links.length; i < linksLength; i++) {
|
||||||
if (links[i].hostname != window.location.hostname) {
|
if (links[i].hostname != window.location.hostname) {
|
||||||
@@ -38,7 +40,7 @@ export class MarkdownPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async dismiss () {
|
async dismiss() {
|
||||||
return this.modalCtrl.dismiss(true)
|
return this.modalCtrl.dismiss(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { IonicModule } from '@ionic/angular'
|
||||||
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
|
import { DevConfigPage } from './dev-config.page'
|
||||||
|
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||||
|
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||||
|
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||||
|
import { FormsModule } from '@angular/forms'
|
||||||
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: DevConfigPage,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
BadgeMenuComponentModule,
|
||||||
|
SharingModule,
|
||||||
|
BackupReportPageModule,
|
||||||
|
FormsModule,
|
||||||
|
MonacoEditorModule,
|
||||||
|
],
|
||||||
|
declarations: [DevConfigPage],
|
||||||
|
})
|
||||||
|
export class DevConfigPageModule {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button defaultHref="/developer"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>Config</ion-title>
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<ion-button (click)="submit()">Preview</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<ngx-monaco-editor
|
||||||
|
[options]="editorOptions"
|
||||||
|
[(ngModel)]="code"
|
||||||
|
></ngx-monaco-editor>
|
||||||
|
</ion-content>
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { ModalController } from '@ionic/angular'
|
||||||
|
import * as yaml from 'js-yaml'
|
||||||
|
import { GenericFormPage } from '../../../modals/generic-form/generic-form.page'
|
||||||
|
import { ConfigSpec } from '../../../pkg-config/config-types'
|
||||||
|
import { ErrorToastService } from '../../../services/error-toast.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'dev-config',
|
||||||
|
templateUrl: 'dev-config.page.html',
|
||||||
|
styleUrls: ['dev-config.page.scss'],
|
||||||
|
})
|
||||||
|
export class DevConfigPage {
|
||||||
|
editorOptions = { theme: 'vs-dark', language: 'yaml' }
|
||||||
|
code: string
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly errToast: ErrorToastService,
|
||||||
|
private readonly modalCtrl: ModalController,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.code = yaml
|
||||||
|
.dump(SAMPLE_CODE)
|
||||||
|
.replace(/warning:/g, '# Optional\n warning:')
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
let doc: any
|
||||||
|
try {
|
||||||
|
doc = yaml.load(this.code)
|
||||||
|
} catch (e) {
|
||||||
|
this.errToast.present(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = await this.modalCtrl.create({
|
||||||
|
component: GenericFormPage,
|
||||||
|
componentProps: {
|
||||||
|
title: 'Config Sample',
|
||||||
|
spec: JSON.parse(JSON.stringify(doc, null, 2)),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'OK',
|
||||||
|
handler: () => {
|
||||||
|
return
|
||||||
|
},
|
||||||
|
isSubmit: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await modal.present()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SAMPLE_CODE: ConfigSpec = {
|
||||||
|
'sample-string': {
|
||||||
|
type: 'string',
|
||||||
|
name: 'Example String Input',
|
||||||
|
nullable: false,
|
||||||
|
masked: false,
|
||||||
|
copyable: false,
|
||||||
|
// optional
|
||||||
|
warning: null,
|
||||||
|
description: 'Example description for required string input.',
|
||||||
|
default: null,
|
||||||
|
placeholder: 'Enter string value',
|
||||||
|
pattern: '^[a-zA-Z0-9! _]+$',
|
||||||
|
'pattern-description': 'Must be alphanumeric (may contain underscore).',
|
||||||
|
},
|
||||||
|
'sample-number': {
|
||||||
|
type: 'number',
|
||||||
|
name: 'Example Number Input',
|
||||||
|
nullable: false,
|
||||||
|
range: '[5,1000000]',
|
||||||
|
integral: true,
|
||||||
|
// optional
|
||||||
|
warning: 'Example warning to display when changing this number value.',
|
||||||
|
units: 'ms',
|
||||||
|
description: 'Example description for optional number input.',
|
||||||
|
default: null,
|
||||||
|
placeholder: 'Enter number value',
|
||||||
|
},
|
||||||
|
'sample-boolean': {
|
||||||
|
type: 'boolean',
|
||||||
|
name: 'Example Boolean Toggle',
|
||||||
|
// optional
|
||||||
|
warning: null,
|
||||||
|
description: 'Example description for boolean toggle',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
'sample-enum': {
|
||||||
|
type: 'enum',
|
||||||
|
name: 'Example Enum Select',
|
||||||
|
values: ['red', 'blue', 'green'],
|
||||||
|
'value-names': {
|
||||||
|
red: 'Red',
|
||||||
|
blue: 'Blue',
|
||||||
|
green: 'Green',
|
||||||
|
},
|
||||||
|
// optional
|
||||||
|
warning: 'Example warning to display when changing this enum value.',
|
||||||
|
description: 'Example description for enum select',
|
||||||
|
default: 'red',
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { IonicModule } from '@ionic/angular'
|
||||||
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
|
import { DevInstructionsPage } from './dev-instructions.page'
|
||||||
|
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||||
|
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||||
|
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||||
|
import { FormsModule } from '@angular/forms'
|
||||||
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: DevInstructionsPage,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
BadgeMenuComponentModule,
|
||||||
|
SharingModule,
|
||||||
|
BackupReportPageModule,
|
||||||
|
FormsModule,
|
||||||
|
MonacoEditorModule,
|
||||||
|
],
|
||||||
|
declarations: [DevInstructionsPage],
|
||||||
|
})
|
||||||
|
export class DevInstructionsPageModule {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button defaultHref="/developer"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>Instructions</ion-title>
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<ion-button (click)="submit()">Preview</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<ngx-monaco-editor
|
||||||
|
[options]="editorOptions"
|
||||||
|
[(ngModel)]="code"
|
||||||
|
></ngx-monaco-editor>
|
||||||
|
</ion-content>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { ModalController } from '@ionic/angular'
|
||||||
|
import { MarkdownPage } from '../../../modals/markdown/markdown.page'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'dev-instructions',
|
||||||
|
templateUrl: 'dev-instructions.page.html',
|
||||||
|
styleUrls: ['dev-instructions.page.scss'],
|
||||||
|
})
|
||||||
|
export class DevInstructionsPage {
|
||||||
|
editorOptions = { theme: 'vs-dark', language: 'markdown' }
|
||||||
|
code: string = `# Create Instructions using Markdown! :)`
|
||||||
|
|
||||||
|
constructor(private readonly modalCtrl: ModalController) {}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
const modal = await this.modalCtrl.create({
|
||||||
|
componentProps: {
|
||||||
|
title: 'Instructions Sample',
|
||||||
|
content: this.code,
|
||||||
|
},
|
||||||
|
component: MarkdownPage,
|
||||||
|
})
|
||||||
|
|
||||||
|
await modal.present()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { IonicModule } from '@ionic/angular'
|
||||||
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
|
import { DeveloperPage } from './developer-list.page'
|
||||||
|
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||||
|
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||||
|
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: DeveloperPage,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
BadgeMenuComponentModule,
|
||||||
|
SharingModule,
|
||||||
|
BackupReportPageModule,
|
||||||
|
],
|
||||||
|
declarations: [DeveloperPage],
|
||||||
|
})
|
||||||
|
export class DeveloperPageModule {}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>Developer Tools</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<ion-item-divider>Components</ion-item-divider>
|
||||||
|
<ion-item button detail (click)="navToInstructions()">
|
||||||
|
<ion-icon slot="start" name="list-outline"></ion-icon>
|
||||||
|
<ion-label>
|
||||||
|
<h2>Instructions Generator</h2>
|
||||||
|
<p>Create instructions and see how they will appear to the end user</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item button detail (click)="navToConfig()">
|
||||||
|
<ion-icon slot="start" name="construct-outline"></ion-icon>
|
||||||
|
<ion-label>
|
||||||
|
<h2>Config Generator</h2>
|
||||||
|
<p>Edit the config with YAML and see it in real time</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-content>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { ActivatedRoute } from '@angular/router'
|
||||||
|
import { NavController } from '@ionic/angular'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'developer-list',
|
||||||
|
templateUrl: 'developer-list.page.html',
|
||||||
|
styleUrls: ['developer-list.page.scss'],
|
||||||
|
})
|
||||||
|
export class DeveloperPage {
|
||||||
|
constructor(
|
||||||
|
private readonly navCtrl: NavController,
|
||||||
|
private readonly route: ActivatedRoute,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
navToConfig() {
|
||||||
|
this.navCtrl.navigateForward(['config'], { relativeTo: this.route })
|
||||||
|
}
|
||||||
|
|
||||||
|
navToInstructions() {
|
||||||
|
this.navCtrl.navigateForward(['instructions'], { relativeTo: this.route })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { Routes, RouterModule } from '@angular/router'
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./developer-list/developer-list.module').then(
|
||||||
|
m => m.DeveloperPageModule,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'config',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./dev-config/dev-config.module').then(m => m.DevConfigPageModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'instructions',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./dev-instructions/dev-instructions.module').then(
|
||||||
|
m => m.DevInstructionsPageModule,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class DeveloperRoutingModule {}
|
||||||
@@ -3,22 +3,26 @@
|
|||||||
<ion-buttons slot="start">
|
<ion-buttons slot="start">
|
||||||
<ion-back-button defaultHref="embassy"></ion-back-button>
|
<ion-back-button defaultHref="embassy"></ion-back-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<ion-title>Preferences</ion-title>
|
<ion-title (click)="addClick()">Preferences</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content class="ion-padding-top">
|
<ion-content class="ion-padding-top">
|
||||||
|
|
||||||
<ion-item-group *ngIf="patch.data['server-info'] as server">
|
<ion-item-group *ngIf="patch.data['server-info'] as server">
|
||||||
<ion-item-divider>General</ion-item-divider>
|
<ion-item-divider>General</ion-item-divider>
|
||||||
<ion-item button (click)="presentModalName()">
|
<ion-item button (click)="presentModalName()">
|
||||||
<ion-label>{{ fields['name'].name }}</ion-label>
|
<ion-label>Device Name</ion-label>
|
||||||
<ion-note slot="end">{{ patch.data.ui.name || defaultName }}</ion-note>
|
<ion-note slot="end">{{ patch.data.ui.name || defaultName }}</ion-note>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<ion-item button (click)="serverConfig.presentAlert('share-stats', server['share-stats'])">
|
<ion-item
|
||||||
|
button
|
||||||
|
(click)="serverConfig.presentAlert('share-stats', server['share-stats'])"
|
||||||
|
>
|
||||||
<ion-label>Auto Report Bugs</ion-label>
|
<ion-label>Auto Report Bugs</ion-label>
|
||||||
<ion-note slot="end">{{ server['share-stats'] ? 'Enabled' : 'Disabled' }}</ion-note>
|
<ion-note slot="end"
|
||||||
|
>{{ server['share-stats'] ? 'Enabled' : 'Disabled' }}</ion-note
|
||||||
|
>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- <ion-item button (click)="presentModalValueEdit('password')">
|
<!-- <ion-item button (click)="presentModalValueEdit('password')">
|
||||||
@@ -27,15 +31,15 @@
|
|||||||
</ion-item> -->
|
</ion-item> -->
|
||||||
|
|
||||||
<ion-item-divider>Marketplace</ion-item-divider>
|
<ion-item-divider>Marketplace</ion-item-divider>
|
||||||
<ion-item button (click)="serverConfig.presentAlert('auto-check-updates', patch.data.ui['auto-check-updates'])">
|
<ion-item
|
||||||
|
button
|
||||||
|
(click)="serverConfig.presentAlert('auto-check-updates', patch.data.ui['auto-check-updates'])"
|
||||||
|
>
|
||||||
<ion-label>Auto Check for Updates</ion-label>
|
<ion-label>Auto Check for Updates</ion-label>
|
||||||
<ion-note slot="end">{{ patch.data.ui['auto-check-updates'] ? 'Enabled' : 'Disabled' }}</ion-note>
|
<ion-note slot="end"
|
||||||
|
>{{ patch.data.ui['auto-check-updates'] ? 'Enabled' : 'Disabled'
|
||||||
|
}}</ion-note
|
||||||
|
>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- <ion-item button (click)="presentModalValueEdit('packageMarketplace', server['package-marketplace'])">
|
|
||||||
<ion-label>Package Marketplace</ion-label>
|
|
||||||
<ion-note slot="end">{{ server['package-marketplace'] }}</ion-note>
|
|
||||||
</ion-item> -->
|
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
</ion-content>
|
||||||
</ion-content>
|
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
import { Component, ViewChild } from '@angular/core'
|
import { Component, ViewChild } from '@angular/core'
|
||||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
import { IonContent, LoadingController, ModalController } from '@ionic/angular'
|
import {
|
||||||
import { GenericInputComponent, GenericInputOptions } from 'src/app/modals/generic-input/generic-input.component'
|
IonContent,
|
||||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
LoadingController,
|
||||||
|
ModalController,
|
||||||
|
ToastController,
|
||||||
|
} from '@ionic/angular'
|
||||||
|
import {
|
||||||
|
GenericInputComponent,
|
||||||
|
GenericInputOptions,
|
||||||
|
} from 'src/app/modals/generic-input/generic-input.component'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||||
|
import { LocalStorageService } from '../../../services/local-storage.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'preferences',
|
selector: 'preferences',
|
||||||
@@ -13,26 +21,28 @@ import { ServerConfigService } from 'src/app/services/server-config.service'
|
|||||||
})
|
})
|
||||||
export class PreferencesPage {
|
export class PreferencesPage {
|
||||||
@ViewChild(IonContent) content: IonContent
|
@ViewChild(IonContent) content: IonContent
|
||||||
fields = fields
|
|
||||||
defaultName: string
|
defaultName: string
|
||||||
|
clicks = 0
|
||||||
|
|
||||||
constructor (
|
constructor(
|
||||||
private readonly loadingCtrl: LoadingController,
|
private readonly loadingCtrl: LoadingController,
|
||||||
private readonly modalCtrl: ModalController,
|
private readonly modalCtrl: ModalController,
|
||||||
private readonly api: ApiService,
|
private readonly api: ApiService,
|
||||||
public readonly serverConfig: ServerConfigService,
|
public readonly serverConfig: ServerConfigService,
|
||||||
|
private readonly toastCtrl: ToastController,
|
||||||
|
private readonly localStorageService: LocalStorageService,
|
||||||
public readonly patch: PatchDbService,
|
public readonly patch: PatchDbService,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit() {
|
||||||
this.defaultName = `Embassy-${this.patch.getData()['server-info'].id}`
|
this.defaultName = `Embassy-${this.patch.getData()['server-info'].id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit () {
|
ngAfterViewInit() {
|
||||||
this.content.scrollToPoint(undefined, 1)
|
this.content.scrollToPoint(undefined, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentModalName (): Promise<void> {
|
async presentModalName(): Promise<void> {
|
||||||
const options: GenericInputOptions = {
|
const options: GenericInputOptions = {
|
||||||
title: 'Edit Device Name',
|
title: 'Edit Device Name',
|
||||||
message: 'This is for your reference only.',
|
message: 'This is for your reference only.',
|
||||||
@@ -42,7 +52,8 @@ export class PreferencesPage {
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
initialValue: this.patch.getData().ui.name,
|
initialValue: this.patch.getData().ui.name,
|
||||||
buttonText: 'Save',
|
buttonText: 'Save',
|
||||||
submitFn: (value: string) => this.setDbValue('name', value || this.defaultName),
|
submitFn: (value: string) =>
|
||||||
|
this.setDbValue('name', value || this.defaultName),
|
||||||
}
|
}
|
||||||
|
|
||||||
const modal = await this.modalCtrl.create({
|
const modal = await this.modalCtrl.create({
|
||||||
@@ -55,7 +66,7 @@ export class PreferencesPage {
|
|||||||
await modal.present()
|
await modal.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setDbValue (key: string, value: string): Promise<void> {
|
async setDbValue(key: string, value: any): Promise<void> {
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
spinner: 'lines',
|
spinner: 'lines',
|
||||||
message: 'Saving...',
|
message: 'Saving...',
|
||||||
@@ -69,14 +80,22 @@ export class PreferencesPage {
|
|||||||
loader.dismiss()
|
loader.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const fields: ConfigSpec = {
|
async addClick() {
|
||||||
'name': {
|
this.clicks++
|
||||||
name: 'Device Name',
|
if (this.clicks >= 5) {
|
||||||
type: 'string',
|
this.clicks = 0
|
||||||
nullable: false,
|
const newVal = await this.localStorageService.toggleShowDevTools()
|
||||||
masked: false,
|
const toast = await this.toastCtrl.create({
|
||||||
copyable: false,
|
header: newVal ? 'Dev tools unlocked' : 'Dev tools hidden',
|
||||||
},
|
position: 'bottom',
|
||||||
|
duration: 1000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await toast.present()
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.clicks = Math.max(this.clicks - 1, 0)
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,36 @@
|
|||||||
export interface ConfigSpec { [key: string]: ValueSpec }
|
export interface ConfigSpec {
|
||||||
|
[key: string]: ValueSpec
|
||||||
|
}
|
||||||
|
|
||||||
export type ValueType = 'string' | 'number' | 'boolean' | 'enum' | 'list' | 'object' | 'pointer' | 'union'
|
export type ValueType =
|
||||||
|
| 'string'
|
||||||
|
| 'number'
|
||||||
|
| 'boolean'
|
||||||
|
| 'enum'
|
||||||
|
| 'list'
|
||||||
|
| 'object'
|
||||||
|
| 'pointer'
|
||||||
|
| 'union'
|
||||||
export type ValueSpec = ValueSpecOf<ValueType>
|
export type ValueSpec = ValueSpecOf<ValueType>
|
||||||
|
|
||||||
// core spec types. These types provide the metadata for performing validations
|
// core spec types. These types provide the metadata for performing validations
|
||||||
export type ValueSpecOf<T extends ValueType> =
|
export type ValueSpecOf<T extends ValueType> = T extends 'string'
|
||||||
T extends 'string' ? ValueSpecString :
|
? ValueSpecString
|
||||||
T extends 'number' ? ValueSpecNumber :
|
: T extends 'number'
|
||||||
T extends 'boolean' ? ValueSpecBoolean :
|
? ValueSpecNumber
|
||||||
T extends 'enum' ? ValueSpecEnum :
|
: T extends 'boolean'
|
||||||
T extends 'list' ? ValueSpecList :
|
? ValueSpecBoolean
|
||||||
T extends 'object' ? ValueSpecObject :
|
: T extends 'enum'
|
||||||
T extends 'pointer' ? ValueSpecPointer :
|
? ValueSpecEnum
|
||||||
T extends 'union' ? ValueSpecUnion :
|
: T extends 'list'
|
||||||
never
|
? ValueSpecList
|
||||||
|
: T extends 'object'
|
||||||
|
? ValueSpecObject
|
||||||
|
: T extends 'pointer'
|
||||||
|
? ValueSpecPointer
|
||||||
|
: T extends 'union'
|
||||||
|
? ValueSpecUnion
|
||||||
|
: never
|
||||||
|
|
||||||
export interface ValueSpecString extends ListValueSpecString, WithStandalone {
|
export interface ValueSpecString extends ListValueSpecString, WithStandalone {
|
||||||
type: 'string'
|
type: 'string'
|
||||||
@@ -62,20 +79,30 @@ export interface WithStandalone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no lists of booleans, lists, pointers
|
// no lists of booleans, lists, pointers
|
||||||
export type ListValueSpecType = 'string' | 'number' | 'enum' | 'object' | 'union'
|
export type ListValueSpecType =
|
||||||
|
| 'string'
|
||||||
|
| 'number'
|
||||||
|
| 'enum'
|
||||||
|
| 'object'
|
||||||
|
| 'union'
|
||||||
|
|
||||||
// represents a spec for the values of a list
|
// represents a spec for the values of a list
|
||||||
export type ListValueSpecOf<T extends ListValueSpecType> =
|
export type ListValueSpecOf<T extends ListValueSpecType> = T extends 'string'
|
||||||
T extends 'string' ? ListValueSpecString :
|
? ListValueSpecString
|
||||||
T extends 'number' ? ListValueSpecNumber :
|
: T extends 'number'
|
||||||
T extends 'enum' ? ListValueSpecEnum :
|
? ListValueSpecNumber
|
||||||
T extends 'object' ? ListValueSpecObject :
|
: T extends 'enum'
|
||||||
T extends 'union' ? ListValueSpecUnion :
|
? ListValueSpecEnum
|
||||||
never
|
: T extends 'object'
|
||||||
|
? ListValueSpecObject
|
||||||
|
: T extends 'union'
|
||||||
|
? ListValueSpecUnion
|
||||||
|
: never
|
||||||
|
|
||||||
// represents a spec for a list
|
// represents a spec for a list
|
||||||
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>
|
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>
|
||||||
export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStandalone {
|
export interface ValueSpecListOf<T extends ListValueSpecType>
|
||||||
|
extends WithStandalone {
|
||||||
type: 'list'
|
type: 'list'
|
||||||
subtype: T
|
subtype: T
|
||||||
spec: ListValueSpecOf<T>
|
spec: ListValueSpecOf<T>
|
||||||
@@ -84,7 +111,10 @@ export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStanda
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sometimes the type checker needs just a little bit of help
|
// sometimes the type checker needs just a little bit of help
|
||||||
export function isValueSpecListOf<S extends ListValueSpecType> (t: ValueSpecList, s: S): t is ValueSpecListOf<S> {
|
export function isValueSpecListOf<S extends ListValueSpecType>(
|
||||||
|
t: ValueSpecList,
|
||||||
|
s: S,
|
||||||
|
): t is ValueSpecListOf<S> {
|
||||||
return t.subtype === s
|
return t.subtype === s
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +135,6 @@ export interface ListValueSpecNumber {
|
|||||||
|
|
||||||
export interface ListValueSpecEnum {
|
export interface ListValueSpecEnum {
|
||||||
values: string[]
|
values: string[]
|
||||||
'values-set'?: Set<string>
|
|
||||||
'value-names': { [value: string]: string }
|
'value-names': { [value: string]: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,9 +158,10 @@ export interface UnionTagSpec {
|
|||||||
id: string // The name of the field containing one of the union variants
|
id: string // The name of the field containing one of the union variants
|
||||||
name: string
|
name: string
|
||||||
description?: string
|
description?: string
|
||||||
'variant-names': { // the name of each variant
|
'variant-names': {
|
||||||
|
// the name of each variant
|
||||||
[variant: string]: string
|
[variant: string]: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DefaultString = string | { charset: string, len: number }
|
export type DefaultString = string | { charset: string; len: number }
|
||||||
|
|||||||
@@ -2,7 +2,15 @@ import { Injectable } from '@angular/core'
|
|||||||
import { pauseFor } from '../../util/misc.util'
|
import { pauseFor } from '../../util/misc.util'
|
||||||
import { ApiService } from './embassy-api.service'
|
import { ApiService } from './embassy-api.service'
|
||||||
import { PatchOp, Update, Operation, RemoveOperation } from 'patch-db-client'
|
import { PatchOp, Update, Operation, RemoveOperation } from 'patch-db-client'
|
||||||
import { DataModel, DependencyErrorType, InstallProgress, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/services/patch-db/data-model'
|
import {
|
||||||
|
DataModel,
|
||||||
|
DependencyErrorType,
|
||||||
|
InstallProgress,
|
||||||
|
PackageDataEntry,
|
||||||
|
PackageMainStatus,
|
||||||
|
PackageState,
|
||||||
|
ServerStatus,
|
||||||
|
} from 'src/app/services/patch-db/data-model'
|
||||||
import { CifsBackupTarget, Log, RR, WithRevision } from './api.types'
|
import { CifsBackupTarget, Log, RR, WithRevision } from './api.types'
|
||||||
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
||||||
import { Mock } from './api.fixures'
|
import { Mock } from './api.fixures'
|
||||||
@@ -17,24 +25,22 @@ export class MockApiService extends ApiService {
|
|||||||
private readonly revertTime = 4000
|
private readonly revertTime = 4000
|
||||||
sequence: number
|
sequence: number
|
||||||
|
|
||||||
constructor (
|
constructor(private readonly bootstrapper: LocalStorageBootstrap) {
|
||||||
private readonly bootstrapper: LocalStorageBootstrap,
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStatic (url: string): Promise<string> {
|
async getStatic(url: string): Promise<string> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return markdown
|
return markdown
|
||||||
}
|
}
|
||||||
|
|
||||||
// db
|
// db
|
||||||
|
|
||||||
async getRevisions (since: number): Promise<RR.GetRevisionsRes> {
|
async getRevisions(since: number): Promise<RR.GetRevisionsRes> {
|
||||||
return this.getDump()
|
return this.getDump()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDump (): Promise<RR.GetDumpRes> {
|
async getDump(): Promise<RR.GetDumpRes> {
|
||||||
const cache = await this.bootstrapper.init()
|
const cache = await this.bootstrapper.init()
|
||||||
return {
|
return {
|
||||||
id: cache.sequence,
|
id: cache.sequence,
|
||||||
@@ -43,7 +49,7 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDbValueRaw (params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> {
|
async setDbValueRaw(params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const patch = [
|
const patch = [
|
||||||
{
|
{
|
||||||
@@ -57,7 +63,7 @@ export class MockApiService extends ApiService {
|
|||||||
|
|
||||||
// auth
|
// auth
|
||||||
|
|
||||||
async login (params: RR.LoginReq): Promise<RR.loginRes> {
|
async login(params: RR.LoginReq): Promise<RR.loginRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -67,24 +73,26 @@ export class MockApiService extends ApiService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async logout (params: RR.LogoutReq): Promise<RR.LogoutRes> {
|
async logout(params: RR.LogoutReq): Promise<RR.LogoutRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSessions (params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
|
async getSessions(params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.Sessions
|
return Mock.Sessions
|
||||||
}
|
}
|
||||||
|
|
||||||
async killSessions (params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
|
async killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// server
|
// server
|
||||||
|
|
||||||
async setShareStatsRaw (params: RR.SetShareStatsReq): Promise<RR.SetShareStatsRes> {
|
async setShareStatsRaw(
|
||||||
|
params: RR.SetShareStatsReq,
|
||||||
|
): Promise<RR.SetShareStatsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const patch = [
|
const patch = [
|
||||||
{
|
{
|
||||||
@@ -97,14 +105,20 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getServerLogs (params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
|
async getServerLogs(
|
||||||
|
params: RR.GetServerLogsReq,
|
||||||
|
): Promise<RR.GetServerLogsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
let entries: Log[]
|
let entries: Log[]
|
||||||
if (Math.random() < .2) {
|
if (Math.random() < 0.2) {
|
||||||
entries = Mock.ServerLogs
|
entries = Mock.ServerLogs
|
||||||
} else {
|
} else {
|
||||||
const arrLength = params.limit ? Math.ceil(params.limit / Mock.ServerLogs.length) : 10
|
const arrLength = params.limit
|
||||||
entries = new Array(arrLength).fill(Mock.ServerLogs).reduce((acc, val) => acc.concat(val), [])
|
? Math.ceil(params.limit / Mock.ServerLogs.length)
|
||||||
|
: 10
|
||||||
|
entries = new Array(arrLength)
|
||||||
|
.fill(Mock.ServerLogs)
|
||||||
|
.reduce((acc, val) => acc.concat(val), [])
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
entries,
|
entries,
|
||||||
@@ -113,17 +127,23 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getServerMetrics (params: RR.GetServerMetricsReq): Promise<RR.GetServerMetricsRes> {
|
async getServerMetrics(
|
||||||
|
params: RR.GetServerMetricsReq,
|
||||||
|
): Promise<RR.GetServerMetricsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.getServerMetrics()
|
return Mock.getServerMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPkgMetrics (params: RR.GetServerMetricsReq): Promise<RR.GetPackageMetricsRes> {
|
async getPkgMetrics(
|
||||||
|
params: RR.GetServerMetricsReq,
|
||||||
|
): Promise<RR.GetPackageMetricsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.getAppMetrics()
|
return Mock.getAppMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateServerRaw (params: RR.UpdateServerReq): Promise<RR.UpdateServerRes> {
|
async updateServerRaw(
|
||||||
|
params: RR.UpdateServerReq,
|
||||||
|
): Promise<RR.UpdateServerRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const initialProgress = {
|
const initialProgress = {
|
||||||
size: 10000,
|
size: 10000,
|
||||||
@@ -145,65 +165,77 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch, 'updating')
|
return this.withRevision(patch, 'updating')
|
||||||
}
|
}
|
||||||
|
|
||||||
async restartServer (params: RR.RestartServerReq): Promise<RR.RestartServerRes> {
|
async restartServer(
|
||||||
|
params: RR.RestartServerReq,
|
||||||
|
): Promise<RR.RestartServerRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async shutdownServer (params: RR.ShutdownServerReq): Promise<RR.ShutdownServerRes> {
|
async shutdownServer(
|
||||||
|
params: RR.ShutdownServerReq,
|
||||||
|
): Promise<RR.ShutdownServerRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async systemRebuild (params: RR.RestartServerReq): Promise<RR.RestartServerRes> {
|
async systemRebuild(
|
||||||
|
params: RR.RestartServerReq,
|
||||||
|
): Promise<RR.RestartServerRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// marketplace URLs
|
// marketplace URLs
|
||||||
|
|
||||||
async getEos (params: RR.GetMarketplaceEOSReq): Promise<RR.GetMarketplaceEOSRes> {
|
async getEos(
|
||||||
|
params: RR.GetMarketplaceEOSReq,
|
||||||
|
): Promise<RR.GetMarketplaceEOSRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.MarketplaceEos
|
return Mock.MarketplaceEos
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMarketplaceData (params: RR.GetMarketplaceDataReq): Promise<RR.GetMarketplaceDataRes> {
|
async getMarketplaceData(
|
||||||
|
params: RR.GetMarketplaceDataReq,
|
||||||
|
): Promise<RR.GetMarketplaceDataRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return {
|
return {
|
||||||
categories: ['featured', 'bitcoin', 'lightning', 'data', 'messaging', 'social', 'alt coin'],
|
categories: [
|
||||||
|
'featured',
|
||||||
|
'bitcoin',
|
||||||
|
'lightning',
|
||||||
|
'data',
|
||||||
|
'messaging',
|
||||||
|
'social',
|
||||||
|
'alt coin',
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMarketplacePkgs (params: RR.GetMarketplacePackagesReq): Promise<RR.GetMarketplacePackagesRes> {
|
async getMarketplacePkgs(
|
||||||
|
params: RR.GetMarketplacePackagesReq,
|
||||||
|
): Promise<RR.GetMarketplacePackagesRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.MarketplacePkgsList
|
return Mock.MarketplacePkgsList
|
||||||
}
|
}
|
||||||
|
|
||||||
async getReleaseNotes (params: RR.GetReleaseNotesReq): Promise<RR.GetReleaseNotesRes> {
|
async getReleaseNotes(
|
||||||
|
params: RR.GetReleaseNotesReq,
|
||||||
|
): Promise<RR.GetReleaseNotesRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.ReleaseNotes
|
return Mock.ReleaseNotes
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestVersion (params: RR.GetLatestVersionReq): Promise<RR.GetLatestVersionRes> {
|
async getLatestVersion(
|
||||||
|
params: RR.GetLatestVersionReq,
|
||||||
|
): Promise<RR.GetLatestVersionRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return params.ids.reduce((obj, id) => {
|
return params.ids.reduce((obj, id) => {
|
||||||
obj[id] = '1.3.0'
|
obj[id] = '1.3.0'
|
||||||
return obj
|
return obj
|
||||||
}, { })
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
// async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise<RR.SetPackageMarketplaceRes> {
|
|
||||||
// await pauseFor(2000)
|
|
||||||
// const patch = [
|
|
||||||
// {
|
|
||||||
// op: PatchOp.REPLACE,
|
|
||||||
// path: '/server-info/package-marketplace',
|
|
||||||
// value: params.url,
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// return this.http.rpcRequest<WithRevision<null>>({ method: 'db.patch', params: { patch } })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// password
|
// password
|
||||||
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
||||||
// await pauseFor(2000)
|
// await pauseFor(2000)
|
||||||
@@ -212,7 +244,9 @@ export class MockApiService extends ApiService {
|
|||||||
|
|
||||||
// notification
|
// notification
|
||||||
|
|
||||||
async getNotificationsRaw (params: RR.GetNotificationsReq): Promise<RR.GetNotificationsRes> {
|
async getNotificationsRaw(
|
||||||
|
params: RR.GetNotificationsReq,
|
||||||
|
): Promise<RR.GetNotificationsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const patch = [
|
const patch = [
|
||||||
{
|
{
|
||||||
@@ -225,72 +259,82 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch, Mock.Notifications)
|
return this.withRevision(patch, Mock.Notifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteNotification (params: RR.DeleteNotificationReq): Promise<RR.DeleteNotificationRes> {
|
async deleteNotification(
|
||||||
|
params: RR.DeleteNotificationReq,
|
||||||
|
): Promise<RR.DeleteNotificationRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAllNotifications (params: RR.DeleteAllNotificationsReq): Promise<RR.DeleteAllNotificationsRes> {
|
async deleteAllNotifications(
|
||||||
|
params: RR.DeleteAllNotificationsReq,
|
||||||
|
): Promise<RR.DeleteAllNotificationsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
|
|
||||||
async getWifi (params: RR.GetWifiReq): Promise < RR.GetWifiRes > {
|
async getWifi(params: RR.GetWifiReq): Promise<RR.GetWifiRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.Wifi
|
return Mock.Wifi
|
||||||
}
|
}
|
||||||
|
|
||||||
async setWifiCountry (params: RR.SetWifiCountryReq): Promise <RR.SetWifiCountryRes> {
|
async setWifiCountry(
|
||||||
|
params: RR.SetWifiCountryReq,
|
||||||
|
): Promise<RR.SetWifiCountryRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async addWifi (params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
|
async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectWifi (params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
|
async connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteWifi (params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
|
async deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// ssh
|
// ssh
|
||||||
|
|
||||||
async getSshKeys (params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
|
async getSshKeys(params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.SshKeys
|
return Mock.SshKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
async addSshKey (params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
|
async addSshKey(params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.SshKey
|
return Mock.SshKey
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSshKey (params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
|
async deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// backup
|
// backup
|
||||||
|
|
||||||
async getBackupTargets (params: RR.GetBackupTargetsReq): Promise<RR.GetBackupTargetsRes> {
|
async getBackupTargets(
|
||||||
|
params: RR.GetBackupTargetsReq,
|
||||||
|
): Promise<RR.GetBackupTargetsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.BackupTargets
|
return Mock.BackupTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
async addBackupTarget (params: RR.AddBackupTargetReq): Promise<RR.AddBackupTargetRes> {
|
async addBackupTarget(
|
||||||
|
params: RR.AddBackupTargetReq,
|
||||||
|
): Promise<RR.AddBackupTargetRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const { hostname, path, username } = params
|
const { hostname, path, username } = params
|
||||||
return {
|
return {
|
||||||
'latfgvwdbhjsndmk': {
|
latfgvwdbhjsndmk: {
|
||||||
type: 'cifs',
|
type: 'cifs',
|
||||||
hostname,
|
hostname,
|
||||||
path: path.replace(/\\/g, '/'),
|
path: path.replace(/\\/g, '/'),
|
||||||
@@ -301,12 +345,14 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateBackupTarget (params: RR.UpdateBackupTargetReq): Promise<RR.UpdateBackupTargetRes> {
|
async updateBackupTarget(
|
||||||
|
params: RR.UpdateBackupTargetReq,
|
||||||
|
): Promise<RR.UpdateBackupTargetRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const { id, hostname, path, username } = params
|
const { id, hostname, path, username } = params
|
||||||
return {
|
return {
|
||||||
[id]: {
|
[id]: {
|
||||||
...Mock.BackupTargets[id] as CifsBackupTarget,
|
...(Mock.BackupTargets[id] as CifsBackupTarget),
|
||||||
hostname,
|
hostname,
|
||||||
path,
|
path,
|
||||||
username,
|
username,
|
||||||
@@ -314,17 +360,23 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeBackupTarget (params: RR.RemoveBackupTargetReq): Promise<RR.RemoveBackupTargetRes> {
|
async removeBackupTarget(
|
||||||
|
params: RR.RemoveBackupTargetReq,
|
||||||
|
): Promise<RR.RemoveBackupTargetRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBackupInfo (params: RR.GetBackupInfoReq): Promise<RR.GetBackupInfoRes> {
|
async getBackupInfo(
|
||||||
|
params: RR.GetBackupInfoReq,
|
||||||
|
): Promise<RR.GetBackupInfoRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.BackupInfo
|
return Mock.BackupInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBackupRaw (params: RR.CreateBackupReq): Promise<RR.CreateBackupRes> {
|
async createBackupRaw(
|
||||||
|
params: RR.CreateBackupReq,
|
||||||
|
): Promise<RR.CreateBackupRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const path = '/server-info/status'
|
const path = '/server-info/status'
|
||||||
const ids = ['bitcoind', 'lnd']
|
const ids = ['bitcoind', 'lnd']
|
||||||
@@ -373,19 +425,27 @@ export class MockApiService extends ApiService {
|
|||||||
|
|
||||||
// package
|
// package
|
||||||
|
|
||||||
async getPackageProperties (params: RR.GetPackagePropertiesReq): Promise<RR.GetPackagePropertiesRes<2>['data']> {
|
async getPackageProperties(
|
||||||
|
params: RR.GetPackagePropertiesReq,
|
||||||
|
): Promise<RR.GetPackagePropertiesRes<2>['data']> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return parsePropertiesPermissive(Mock.PackageProperties)
|
return parsePropertiesPermissive(Mock.PackageProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPackageLogs (params: RR.GetPackageLogsReq): Promise<RR.GetPackageLogsRes> {
|
async getPackageLogs(
|
||||||
|
params: RR.GetPackageLogsReq,
|
||||||
|
): Promise<RR.GetPackageLogsRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
let entries
|
let entries
|
||||||
if (Math.random() < .2) {
|
if (Math.random() < 0.2) {
|
||||||
entries = Mock.PackageLogs
|
entries = Mock.PackageLogs
|
||||||
} else {
|
} else {
|
||||||
const arrLength = params.limit ? Math.ceil(params.limit / Mock.PackageLogs.length) : 10
|
const arrLength = params.limit
|
||||||
entries = new Array(arrLength).fill(Mock.PackageLogs).reduce((acc, val) => acc.concat(val), [])
|
? Math.ceil(params.limit / Mock.PackageLogs.length)
|
||||||
|
: 10
|
||||||
|
entries = new Array(arrLength)
|
||||||
|
.fill(Mock.PackageLogs)
|
||||||
|
.reduce((acc, val) => acc.concat(val), [])
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
entries,
|
entries,
|
||||||
@@ -394,7 +454,9 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async installPackageRaw (params: RR.InstallPackageReq): Promise<RR.InstallPackageRes> {
|
async installPackageRaw(
|
||||||
|
params: RR.InstallPackageReq,
|
||||||
|
): Promise<RR.InstallPackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const initialProgress: InstallProgress = {
|
const initialProgress: InstallProgress = {
|
||||||
size: 120,
|
size: 120,
|
||||||
@@ -427,12 +489,16 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async dryUpdatePackage (params: RR.DryUpdatePackageReq): Promise<RR.DryUpdatePackageRes> {
|
async dryUpdatePackage(
|
||||||
|
params: RR.DryUpdatePackageReq,
|
||||||
|
): Promise<RR.DryUpdatePackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return { }
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPackageConfig (params: RR.GetPackageConfigReq): Promise<RR.GetPackageConfigRes> {
|
async getPackageConfig(
|
||||||
|
params: RR.GetPackageConfigReq,
|
||||||
|
): Promise<RR.GetPackageConfigRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return {
|
return {
|
||||||
config: Mock.MockConfig,
|
config: Mock.MockConfig,
|
||||||
@@ -440,12 +506,16 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async drySetPackageConfig (params: RR.DrySetPackageConfigReq): Promise<RR.DrySetPackageConfigRes> {
|
async drySetPackageConfig(
|
||||||
|
params: RR.DrySetPackageConfigReq,
|
||||||
|
): Promise<RR.DrySetPackageConfigRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return { }
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setPackageConfigRaw (params: RR.SetPackageConfigReq): Promise<RR.SetPackageConfigRes> {
|
async setPackageConfigRaw(
|
||||||
|
params: RR.SetPackageConfigReq,
|
||||||
|
): Promise<RR.SetPackageConfigRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const patch = [
|
const patch = [
|
||||||
{
|
{
|
||||||
@@ -457,10 +527,11 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async restorePackagesRaw (params: RR.RestorePackagesReq): Promise<RR.RestorePackagesRes> {
|
async restorePackagesRaw(
|
||||||
|
params: RR.RestorePackagesReq,
|
||||||
|
): Promise<RR.RestorePackagesRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const patch: Operation[] = params.ids.map(id => {
|
const patch: Operation[] = params.ids.map(id => {
|
||||||
|
|
||||||
const initialProgress: InstallProgress = {
|
const initialProgress: InstallProgress = {
|
||||||
size: 120,
|
size: 120,
|
||||||
downloaded: 120,
|
downloaded: 120,
|
||||||
@@ -492,12 +563,16 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async executePackageAction (params: RR.ExecutePackageActionReq): Promise<RR.ExecutePackageActionRes> {
|
async executePackageAction(
|
||||||
|
params: RR.ExecutePackageActionReq,
|
||||||
|
): Promise<RR.ExecutePackageActionRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return Mock.ActionResponse
|
return Mock.ActionResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
async startPackageRaw (params: RR.StartPackageReq): Promise<RR.StartPackageRes> {
|
async startPackageRaw(
|
||||||
|
params: RR.StartPackageReq,
|
||||||
|
): Promise<RR.StartPackageRes> {
|
||||||
const path = `/package-data/${params.id}/installed/status/main`
|
const path = `/package-data/${params.id}/installed/status/main`
|
||||||
|
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
@@ -551,11 +626,11 @@ export class MockApiService extends ApiService {
|
|||||||
message: 'Bitcoin is syncing from genesis',
|
message: 'Bitcoin is syncing from genesis',
|
||||||
},
|
},
|
||||||
'p2p-interface': {
|
'p2p-interface': {
|
||||||
result: 'success',
|
result: 'success',
|
||||||
},
|
},
|
||||||
'rpc-interface': {
|
'rpc-interface': {
|
||||||
result: 'failure',
|
result: 'failure',
|
||||||
error: 'RPC interface unreachable.',
|
error: 'RPC interface unreachable.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -574,10 +649,12 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(originalPatch)
|
return this.withRevision(originalPatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async dryStopPackage (params: RR.DryStopPackageReq): Promise<RR.DryStopPackageRes> {
|
async dryStopPackage(
|
||||||
|
params: RR.DryStopPackageReq,
|
||||||
|
): Promise<RR.DryStopPackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return {
|
return {
|
||||||
'lnd': {
|
lnd: {
|
||||||
dependency: 'bitcoind',
|
dependency: 'bitcoind',
|
||||||
error: {
|
error: {
|
||||||
type: DependencyErrorType.NotRunning,
|
type: DependencyErrorType.NotRunning,
|
||||||
@@ -586,7 +663,7 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopPackageRaw (params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
async stopPackageRaw(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const path = `/package-data/${params.id}/installed/status/main`
|
const path = `/package-data/${params.id}/installed/status/main`
|
||||||
|
|
||||||
@@ -610,19 +687,23 @@ export class MockApiService extends ApiService {
|
|||||||
{
|
{
|
||||||
op: PatchOp.REPLACE,
|
op: PatchOp.REPLACE,
|
||||||
path: path + '/health',
|
path: path + '/health',
|
||||||
value: { },
|
value: {},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async dryUninstallPackage (params: RR.DryUninstallPackageReq): Promise<RR.DryUninstallPackageRes> {
|
async dryUninstallPackage(
|
||||||
|
params: RR.DryUninstallPackageReq,
|
||||||
|
): Promise<RR.DryUninstallPackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return { }
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async uninstallPackageRaw (params: RR.UninstallPackageReq): Promise<RR.UninstallPackageRes> {
|
async uninstallPackageRaw(
|
||||||
|
params: RR.UninstallPackageReq,
|
||||||
|
): Promise<RR.UninstallPackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
@@ -646,7 +727,9 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteRecoveredPackageRaw (params: RR.DeleteRecoveredPackageReq): Promise<RR.DeleteRecoveredPackageRes> {
|
async deleteRecoveredPackageRaw(
|
||||||
|
params: RR.DeleteRecoveredPackageReq,
|
||||||
|
): Promise<RR.DeleteRecoveredPackageRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
const patch = [
|
const patch = [
|
||||||
{
|
{
|
||||||
@@ -657,7 +740,9 @@ export class MockApiService extends ApiService {
|
|||||||
return this.withRevision(patch)
|
return this.withRevision(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
async dryConfigureDependency (params: RR.DryConfigureDependencyReq): Promise<RR.DryConfigureDependencyRes> {
|
async dryConfigureDependency(
|
||||||
|
params: RR.DryConfigureDependencyReq,
|
||||||
|
): Promise<RR.DryConfigureDependencyRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return {
|
return {
|
||||||
'old-config': Mock.MockConfig,
|
'old-config': Mock.MockConfig,
|
||||||
@@ -666,11 +751,14 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateProgress (id: string, initialProgress: InstallProgress): Promise<void> {
|
private async updateProgress(
|
||||||
|
id: string,
|
||||||
|
initialProgress: InstallProgress,
|
||||||
|
): Promise<void> {
|
||||||
const phases = [
|
const phases = [
|
||||||
{ progress: 'downloaded', completion: 'download-complete'},
|
{ progress: 'downloaded', completion: 'download-complete' },
|
||||||
{ progress: 'validated', completion: 'validation-complete'},
|
{ progress: 'validated', completion: 'validation-complete' },
|
||||||
{ progress: 'unpacked', completion: 'unpack-complete'},
|
{ progress: 'unpacked', completion: 'unpack-complete' },
|
||||||
]
|
]
|
||||||
for (let phase of phases) {
|
for (let phase of phases) {
|
||||||
let i = initialProgress[phase.progress]
|
let i = initialProgress[phase.progress]
|
||||||
@@ -709,7 +797,7 @@ export class MockApiService extends ApiService {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateOSProgress (size: number) {
|
private async updateOSProgress(size: number) {
|
||||||
let downloaded = 0
|
let downloaded = 0
|
||||||
while (downloaded < size) {
|
while (downloaded < size) {
|
||||||
await pauseFor(250)
|
await pauseFor(250)
|
||||||
@@ -759,7 +847,7 @@ export class MockApiService extends ApiService {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateMock (patch: Operation[]): Promise<void> {
|
private async updateMock(patch: Operation[]): Promise<void> {
|
||||||
if (!this.sequence) {
|
if (!this.sequence) {
|
||||||
const { sequence } = await this.bootstrapper.init()
|
const { sequence } = await this.bootstrapper.init()
|
||||||
this.sequence = sequence
|
this.sequence = sequence
|
||||||
@@ -772,7 +860,10 @@ export class MockApiService extends ApiService {
|
|||||||
this.mockPatch$.next(revision)
|
this.mockPatch$.next(revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async withRevision<T> (patch: Operation[], response: T = null): Promise<WithRevision<T>> {
|
private async withRevision<T>(
|
||||||
|
patch: Operation[],
|
||||||
|
response: T = null,
|
||||||
|
): Promise<WithRevision<T>> {
|
||||||
if (!this.sequence) {
|
if (!this.sequence) {
|
||||||
const { sequence } = await this.bootstrapper.init()
|
const { sequence } = await this.bootstrapper.init()
|
||||||
this.sequence = sequence
|
this.sequence = sequence
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Storage } from '@ionic/storage-angular'
|
||||||
|
import { BehaviorSubject } from 'rxjs'
|
||||||
|
const SHOW_DEV_TOOLS = 'SHOW_DEV_TOOLS'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class LocalStorageService {
|
||||||
|
showDevTools$: BehaviorSubject<boolean> = new BehaviorSubject(false)
|
||||||
|
constructor(private readonly storage: Storage) {}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
const val = await this.storage.get(SHOW_DEV_TOOLS)
|
||||||
|
this.showDevTools$.next(!!val)
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleShowDevTools(): Promise<boolean> {
|
||||||
|
const newVal = !(await this.storage.get(SHOW_DEV_TOOLS))
|
||||||
|
await this.storage.set(SHOW_DEV_TOOLS, newVal)
|
||||||
|
this.showDevTools$.next(newVal)
|
||||||
|
return newVal
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,9 +40,9 @@ export enum ServerStatus {
|
|||||||
BackingUp = 'backing-up',
|
BackingUp = 'backing-up',
|
||||||
}
|
}
|
||||||
export interface RecoveredPackageDataEntry {
|
export interface RecoveredPackageDataEntry {
|
||||||
title: string,
|
title: string
|
||||||
icon: URL,
|
icon: URL
|
||||||
version: string,
|
version: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PackageDataEntry {
|
export interface PackageDataEntry {
|
||||||
@@ -53,8 +53,8 @@ export interface PackageDataEntry {
|
|||||||
icon: URL
|
icon: URL
|
||||||
}
|
}
|
||||||
manifest: Manifest
|
manifest: Manifest
|
||||||
installed?: InstalledPackageDataEntry, // exists when: installed, updating
|
installed?: InstalledPackageDataEntry // exists when: installed, updating
|
||||||
'install-progress'?: InstallProgress, // exists when: installing, updating
|
'install-progress'?: InstallProgress // exists when: installing, updating
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InstallProgress {
|
export interface InstallProgress {
|
||||||
@@ -69,7 +69,7 @@ export interface InstallProgress {
|
|||||||
|
|
||||||
export interface InstalledPackageDataEntry {
|
export interface InstalledPackageDataEntry {
|
||||||
status: Status
|
status: Status
|
||||||
manifest: Manifest,
|
manifest: Manifest
|
||||||
'last-backup': string | null
|
'last-backup': string | null
|
||||||
'system-pointers': any[]
|
'system-pointers': any[]
|
||||||
'current-dependents': { [id: string]: CurrentDependencyInfo }
|
'current-dependents': { [id: string]: CurrentDependencyInfo }
|
||||||
@@ -81,7 +81,7 @@ export interface InstalledPackageDataEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
'interface-addresses': {
|
'interface-addresses': {
|
||||||
[id: string]: { 'tor-address': string, 'lan-address': string }
|
[id: string]: { 'tor-address': string; 'lan-address': string }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,10 @@ export interface Manifest {
|
|||||||
stop: string | null
|
stop: string | null
|
||||||
}
|
}
|
||||||
main: ActionImpl
|
main: ActionImpl
|
||||||
'health-checks': Record<string, ActionImpl & { name: string, description: string }>
|
'health-checks': Record<
|
||||||
|
string,
|
||||||
|
ActionImpl & { name: string; description: string }
|
||||||
|
>
|
||||||
config: ConfigActions | null
|
config: ConfigActions | null
|
||||||
volumes: Record<string, Volume>
|
volumes: Record<string, Volume>
|
||||||
'min-os-version': string
|
'min-os-version': string
|
||||||
@@ -209,7 +212,7 @@ export interface TorConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type LanConfig = {
|
export type LanConfig = {
|
||||||
[port: number]: { ssl: boolean, mapping: number }
|
[port: number]: { ssl: boolean; mapping: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackupActions {
|
export interface BackupActions {
|
||||||
@@ -237,7 +240,12 @@ export interface Status {
|
|||||||
'dependency-errors': { [id: string]: DependencyError | null }
|
'dependency-errors': { [id: string]: DependencyError | null }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MainStatus = MainStatusStopped | MainStatusStopping | MainStatusStarting | MainStatusRunning | MainStatusBackingUp
|
export type MainStatus =
|
||||||
|
| MainStatusStopped
|
||||||
|
| MainStatusStopping
|
||||||
|
| MainStatusStarting
|
||||||
|
| MainStatusRunning
|
||||||
|
| MainStatusBackingUp
|
||||||
|
|
||||||
export interface MainStatusStopped {
|
export interface MainStatusStopped {
|
||||||
status: PackageMainStatus.Stopped
|
status: PackageMainStatus.Stopped
|
||||||
@@ -270,11 +278,12 @@ export enum PackageMainStatus {
|
|||||||
BackingUp = 'backing-up',
|
BackingUp = 'backing-up',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HealthCheckResult = HealthCheckResultStarting |
|
export type HealthCheckResult =
|
||||||
HealthCheckResultLoading |
|
| HealthCheckResultStarting
|
||||||
HealthCheckResultDisabled |
|
| HealthCheckResultLoading
|
||||||
HealthCheckResultSuccess |
|
| HealthCheckResultDisabled
|
||||||
HealthCheckResultFailure
|
| HealthCheckResultSuccess
|
||||||
|
| HealthCheckResultFailure
|
||||||
|
|
||||||
export enum HealthResult {
|
export enum HealthResult {
|
||||||
Starting = 'starting',
|
Starting = 'starting',
|
||||||
@@ -306,12 +315,13 @@ export interface HealthCheckResultFailure {
|
|||||||
error: string
|
error: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DependencyError = DependencyErrorNotInstalled |
|
export type DependencyError =
|
||||||
DependencyErrorNotRunning |
|
| DependencyErrorNotInstalled
|
||||||
DependencyErrorIncorrectVersion |
|
| DependencyErrorNotRunning
|
||||||
DependencyErrorConfigUnsatisfied |
|
| DependencyErrorIncorrectVersion
|
||||||
DependencyErrorHealthChecksFailed |
|
| DependencyErrorConfigUnsatisfied
|
||||||
DependencyErrorTransitive
|
| DependencyErrorHealthChecksFailed
|
||||||
|
| DependencyErrorTransitive
|
||||||
|
|
||||||
export enum DependencyErrorType {
|
export enum DependencyErrorType {
|
||||||
NotInstalled = 'not-installed',
|
NotInstalled = 'not-installed',
|
||||||
@@ -357,18 +367,21 @@ export interface DependencyInfo {
|
|||||||
|
|
||||||
export interface DependencyEntry {
|
export interface DependencyEntry {
|
||||||
version: string
|
version: string
|
||||||
requirement: {
|
requirement:
|
||||||
type: 'opt-in'
|
| {
|
||||||
how: string
|
type: 'opt-in'
|
||||||
} | {
|
how: string
|
||||||
type: 'opt-out'
|
}
|
||||||
how: string
|
| {
|
||||||
} | {
|
type: 'opt-out'
|
||||||
type: 'required'
|
how: string
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'required'
|
||||||
|
}
|
||||||
description: string | null
|
description: string | null
|
||||||
config: {
|
config: {
|
||||||
check: ActionImpl,
|
check: ActionImpl
|
||||||
'auto-configure': ActionImpl
|
'auto-configure': ActionImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ import { ErrorToastService } from './error-toast.service'
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ServerConfigService {
|
export class ServerConfigService {
|
||||||
|
constructor(
|
||||||
constructor (
|
|
||||||
private readonly loadingCtrl: LoadingController,
|
private readonly loadingCtrl: LoadingController,
|
||||||
private readonly errToast: ErrorToastService,
|
private readonly errToast: ErrorToastService,
|
||||||
private readonly alertCtrl: AlertController,
|
private readonly alertCtrl: AlertController,
|
||||||
private readonly embassyApi: ApiService,
|
private readonly embassyApi: ApiService,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
async presentAlert (key: string, current?: any): Promise<HTMLIonAlertElement> {
|
async presentAlert(key: string, current?: any): Promise<HTMLIonAlertElement> {
|
||||||
const spec = serverConfig[key]
|
const spec = serverConfig[key]
|
||||||
|
|
||||||
let inputs: AlertInput[]
|
let inputs: AlertInput[]
|
||||||
@@ -100,14 +99,11 @@ export class ServerConfigService {
|
|||||||
|
|
||||||
saveFns: { [key: string]: (val: any) => Promise<any> } = {
|
saveFns: { [key: string]: (val: any) => Promise<any> } = {
|
||||||
'auto-check-updates': async (enabled: boolean) => {
|
'auto-check-updates': async (enabled: boolean) => {
|
||||||
return this.embassyApi.setDbValue({ pointer: '/auto-check-updates', value: enabled })
|
return this.embassyApi.setDbValue({
|
||||||
|
pointer: '/auto-check-updates',
|
||||||
|
value: enabled,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// 'eos-marketplace': async () => {
|
|
||||||
// return this.embassyApi.setEosMarketplace()
|
|
||||||
// },
|
|
||||||
// 'package-marketplace': async (url: string) => {
|
|
||||||
// return this.embassyApi.setPackageMarketplace({ url })
|
|
||||||
// },
|
|
||||||
'share-stats': async (enabled: boolean) => {
|
'share-stats': async (enabled: boolean) => {
|
||||||
return this.embassyApi.setShareStats({ value: enabled })
|
return this.embassyApi.setShareStats({ value: enabled })
|
||||||
},
|
},
|
||||||
@@ -121,31 +117,16 @@ export const serverConfig: ConfigSpec = {
|
|||||||
'auto-check-updates': {
|
'auto-check-updates': {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
name: 'Auto Check for Updates',
|
name: 'Auto Check for Updates',
|
||||||
description: 'If enabled, EmbassyOS will automatically check for updates of itself and any installed services. Updating will still require your approval and action. Updates will never be performed automatically.',
|
description:
|
||||||
|
'If enabled, EmbassyOS will automatically check for updates of itself and any installed services. Updating will still require your approval and action. Updates will never be performed automatically.',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
// 'eos-marketplace': {
|
|
||||||
// type: 'boolean',
|
|
||||||
// name: 'Tor Only Marketplace',
|
|
||||||
// description: `Use Start9's Tor (instead of clearnet) Marketplace.`,
|
|
||||||
// warning: 'This will result in higher latency and slower download times.',
|
|
||||||
// default: false,
|
|
||||||
// },
|
|
||||||
// 'package-marketplace': {
|
|
||||||
// type: 'string',
|
|
||||||
// name: 'Package Marketplace',
|
|
||||||
// description: `Use for alternative embassy marketplace. Leave empty to use start9's marketplace.`,
|
|
||||||
// nullable: true,
|
|
||||||
// // @TODO regex for URL
|
|
||||||
// // pattern: '',
|
|
||||||
// 'pattern-description': 'Must be a valid URL.',
|
|
||||||
// masked: false,
|
|
||||||
// copyable: false,
|
|
||||||
// },
|
|
||||||
'share-stats': {
|
'share-stats': {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
name: 'Report Bugs',
|
name: 'Report Bugs',
|
||||||
description: new IonicSafeString(`If enabled, generic error codes will be anonymously transmitted over Tor to the Start9 team. This helps us identify and fix bugs quickly. <a href="https://docs.start9.com/user-manual/general/user-preferences/report-bugs.html" target="_blank" rel="noreferrer">Read more</a> `) as any,
|
description: new IonicSafeString(
|
||||||
|
`If enabled, generic error codes will be anonymously transmitted over Tor to the Start9 team. This helps us identify and fix bugs quickly. <a href="https://docs.start9.com/user-manual/general/user-preferences/report-bugs.html" target="_blank" rel="noreferrer">Read more</a> `,
|
||||||
|
) as any,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
// password: {
|
// password: {
|
||||||
|
|||||||
Reference in New Issue
Block a user