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",
|
||||
"input": "node_modules/ionicons/dist/ionicons/svg",
|
||||
"output": "./svg"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/monaco-editor",
|
||||
"output": "assets/monaco-editor/"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
|
||||
188
frontend/package-lock.json
generated
188
frontend/package-lock.json
generated
@@ -18,10 +18,9 @@
|
||||
"@angular/router": "^13.2.0",
|
||||
"@ionic/angular": "^6.0.3",
|
||||
"@ionic/storage-angular": "^3.0.6",
|
||||
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||
"@start9labs/argon2": "^0.1.0",
|
||||
"@start9labs/emver": "^0.1.5",
|
||||
"@types/aes-js": "^3.1.1",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"aes-js": "^3.1.2",
|
||||
"ajv": "^6.12.6",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
@@ -29,7 +28,9 @@
|
||||
"dompurify": "^2.3.3",
|
||||
"fast-json-patch": "^3.1.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"js-yaml": "^4.1.0",
|
||||
"marked": "^4.0.0",
|
||||
"monaco-editor": "^0.32.0",
|
||||
"mustache": "^4.2.0",
|
||||
"ng-qrcode": "^6.0.0",
|
||||
"patch-db-client": "file: ../../../patch-db/client",
|
||||
@@ -45,10 +46,13 @@
|
||||
"@angular/compiler-cli": "^13.2.0",
|
||||
"@angular/language-service": "^13.2.0",
|
||||
"@ionic/cli": "^6.18.1",
|
||||
"@types/aes-js": "^3.1.1",
|
||||
"@types/dompurify": "^2.3.3",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/marked": "^4.0.0",
|
||||
"@types/mustache": "^4.1.2",
|
||||
"@types/node": "^16.9.1",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"husky": "^4.3.8",
|
||||
"lint-staged": "^12.1.2",
|
||||
@@ -2900,6 +2904,28 @@
|
||||
"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": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
||||
@@ -2918,6 +2944,18 @@
|
||||
"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": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.0.tgz",
|
||||
@@ -3267,7 +3305,8 @@
|
||||
"node_modules/@types/aes-js": {
|
||||
"version": "3.1.1",
|
||||
"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": {
|
||||
"version": "1.19.2",
|
||||
@@ -3374,6 +3413,12 @@
|
||||
"@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": {
|
||||
"version": "7.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||
@@ -3401,7 +3446,8 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.11.21",
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
@@ -3413,6 +3459,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
|
||||
"integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -3925,13 +3972,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "2.1.2",
|
||||
@@ -8154,13 +8197,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"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,
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
@@ -9312,6 +9353,11 @@
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.1.1",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
@@ -15914,6 +15982,27 @@
|
||||
"get-package-type": "^0.1.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"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": {
|
||||
@@ -15928,6 +16017,14 @@
|
||||
"integrity": "sha512-fuIOnc81C5iRNevb/XPiM8Khp9bVjreydRQ37rt0C/dY0PAW1DRvEM3WrKX/5rStS5lbgwS0FCgqSndh9tvK5w==",
|
||||
"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": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.0.tgz",
|
||||
@@ -16191,7 +16288,8 @@
|
||||
"@types/aes-js": {
|
||||
"version": "3.1.1",
|
||||
"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": {
|
||||
"version": "1.19.2",
|
||||
@@ -16298,6 +16396,12 @@
|
||||
"@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": {
|
||||
"version": "7.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||
@@ -16325,7 +16429,8 @@
|
||||
"@types/node": {
|
||||
"version": "16.11.21",
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
@@ -16337,6 +16442,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
|
||||
"integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -16782,13 +16888,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "2.1.2",
|
||||
@@ -19908,13 +20010,11 @@
|
||||
"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,
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
"argparse": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
@@ -20792,6 +20892,11 @@
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
@@ -23492,6 +23597,15 @@
|
||||
"tsutils": "^2.29.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"
|
||||
}
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "1.1.1",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
|
||||
@@ -30,10 +30,9 @@
|
||||
"@angular/router": "^13.2.0",
|
||||
"@ionic/angular": "^6.0.3",
|
||||
"@ionic/storage-angular": "^3.0.6",
|
||||
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||
"@start9labs/argon2": "^0.1.0",
|
||||
"@start9labs/emver": "^0.1.5",
|
||||
"@types/aes-js": "^3.1.1",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"aes-js": "^3.1.2",
|
||||
"ajv": "^6.12.6",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
@@ -41,7 +40,9 @@
|
||||
"dompurify": "^2.3.3",
|
||||
"fast-json-patch": "^3.1.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"js-yaml": "^4.1.0",
|
||||
"marked": "^4.0.0",
|
||||
"monaco-editor": "^0.32.0",
|
||||
"mustache": "^4.2.0",
|
||||
"ng-qrcode": "^6.0.0",
|
||||
"patch-db-client": "file: ../../../patch-db/client",
|
||||
@@ -57,10 +58,13 @@
|
||||
"@angular/compiler-cli": "^13.2.0",
|
||||
"@angular/language-service": "^13.2.0",
|
||||
"@ionic/cli": "^6.18.1",
|
||||
"@types/aes-js": "^3.1.1",
|
||||
"@types/dompurify": "^2.3.3",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/marked": "^4.0.0",
|
||||
"@types/mustache": "^4.1.2",
|
||||
"@types/node": "^16.9.1",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"husky": "^4.3.8",
|
||||
"lint-staged": "^12.1.2",
|
||||
|
||||
@@ -12,30 +12,52 @@ const routes: Routes = [
|
||||
{
|
||||
path: 'login',
|
||||
canActivate: [UnauthGuard],
|
||||
loadChildren: () => import('./pages/login/login.module').then(m => m.LoginPageModule),
|
||||
loadChildren: () =>
|
||||
import('./pages/login/login.module').then(m => m.LoginPageModule),
|
||||
},
|
||||
{
|
||||
path: 'embassy',
|
||||
canActivate: [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',
|
||||
canActivate: [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',
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () => import('./pages/notifications/notifications.module').then(m => m.NotificationsPageModule),
|
||||
loadChildren: () =>
|
||||
import('./pages/notifications/notifications.module').then(
|
||||
m => m.NotificationsPageModule,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'services',
|
||||
canActivate: [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],
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {}
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
<ion-app>
|
||||
<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-content color="light" scrollY="false">
|
||||
<div style="text-align: center;" class="ion-padding">
|
||||
<img style="width: 45%; cursor: pointer;" src="assets/img/logo.png" (click)="goToWebsite()">
|
||||
<div style="text-align: center" class="ion-padding">
|
||||
<img
|
||||
style="width: 45%; cursor: pointer"
|
||||
src="assets/img/logo.png"
|
||||
(click)="goToWebsite()"
|
||||
/>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<ion-item-group style="padding: 30px 0px;">
|
||||
<ion-menu-toggle auto-hide="false" *ngFor="let page of appPages; let i = index">
|
||||
<ion-item-group style="padding: 30px 0px">
|
||||
<ion-menu-toggle
|
||||
auto-hide="false"
|
||||
*ngFor="let page of appPages; let i = index"
|
||||
>
|
||||
<ion-item
|
||||
style="padding-left: 10px;"
|
||||
style="padding-left: 10px"
|
||||
color="transparent"
|
||||
button
|
||||
(click)="selectedIndex = i"
|
||||
@@ -18,38 +29,69 @@
|
||||
[routerLink]="[page.url]"
|
||||
lines="none"
|
||||
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
|
||||
style="font-family: 'Montserrat';"
|
||||
style="font-family: 'Montserrat'"
|
||||
[class]="selectedIndex === i ? 'bold' : 'dim'"
|
||||
>
|
||||
{{ page.title }}
|
||||
</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-menu-toggle>
|
||||
</ion-item-group>
|
||||
<div style="text-align: center; height: 75px; position:absolute; bottom:0; right:0; width: 100%;">
|
||||
<div class="divider" style="margin-bottom: 10px;"></div>
|
||||
<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-item button lines="none" style="--background: transparent; margin-bottom: 86px; text-align: center;" fill="clear" (click)="presentAlertLogout()">
|
||||
<ion-label><ion-text
|
||||
style="font-family: 'Montserrat';"
|
||||
color="dark"
|
||||
>
|
||||
<ion-item
|
||||
button
|
||||
lines="none"
|
||||
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
|
||||
</ion-text></ion-label>
|
||||
</ion-text></ion-label
|
||||
>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
<ion-router-outlet id="main-content"></ion-router-outlet>
|
||||
|
||||
</ion-split-pane>
|
||||
|
||||
<section id="preload" style="display: none;">
|
||||
<section id="preload" style="display: none">
|
||||
<!-- 3rd party components -->
|
||||
<qr-code value="hello"></qr-code>
|
||||
|
||||
@@ -66,7 +108,8 @@
|
||||
<ion-icon name="checkmark"></ion-icon>
|
||||
<ion-icon name="chevron-down"></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="cloud-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="grid-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="information-circle-outline"></ion-icon>
|
||||
<ion-icon name="key-outline"></ion-icon>
|
||||
@@ -119,7 +163,7 @@
|
||||
<ion-icon name="trash-outline"></ion-icon>
|
||||
<ion-icon name="warning-outline"></ion-icon>
|
||||
<ion-icon name="wifi"></ion-icon>
|
||||
|
||||
|
||||
<!-- Ionic components -->
|
||||
<ion-action-sheet></ion-action-sheet>
|
||||
<ion-alert></ion-alert>
|
||||
@@ -141,7 +185,9 @@
|
||||
<ion-refresher slot="fixed"></ion-refresher>
|
||||
<ion-refresher-content pullingContent="lines"></ion-refresher-content>
|
||||
<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-input></ion-input>
|
||||
<ion-item></ion-item>
|
||||
@@ -172,26 +218,38 @@
|
||||
<ion-footer
|
||||
[ngStyle]="{
|
||||
'max-height': osUpdateProgress ? '100px' : '0px',
|
||||
'overflow': 'hidden',
|
||||
overflow: 'hidden',
|
||||
'transition-property': 'max-height',
|
||||
'transition-duration': '1s',
|
||||
'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-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>
|
||||
<div style="padding: 0 16px 16px 16px;">
|
||||
<div style="padding: 0 16px 16px 16px">
|
||||
<ion-progress-bar
|
||||
color="secondary"
|
||||
[value]="osUpdateProgress && osUpdateProgress.downloaded / osUpdateProgress.size"
|
||||
[value]="
|
||||
osUpdateProgress &&
|
||||
osUpdateProgress.downloaded / osUpdateProgress.size
|
||||
"
|
||||
></ion-progress-bar>
|
||||
</div>
|
||||
</ion-list>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
</ion-app>
|
||||
|
||||
|
||||
|
||||
@@ -3,19 +3,33 @@ import { Storage } from '@ionic/storage-angular'
|
||||
import { AuthService, AuthState } from './services/auth.service'
|
||||
import { ApiService } from './services/api/embassy-api.service'
|
||||
import { Router, RoutesRecognized } from '@angular/router'
|
||||
import { debounceTime, distinctUntilChanged, filter, finalize, take, takeWhile } from 'rxjs/operators'
|
||||
import { AlertController, IonicSafeString, LoadingController, ToastController } from '@ionic/angular'
|
||||
import {
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
take,
|
||||
} from 'rxjs/operators'
|
||||
import {
|
||||
AlertController,
|
||||
IonicSafeString,
|
||||
LoadingController,
|
||||
ToastController,
|
||||
} from '@ionic/angular'
|
||||
import { Emver } from './services/emver.service'
|
||||
import { SplitPaneTracker } from './services/split-pane.service'
|
||||
import { ToastButton } from '@ionic/core'
|
||||
import { PatchDbService } from './services/patch-db/patch-db.service'
|
||||
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 { 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 { Subscription } from 'rxjs'
|
||||
import { LocalStorageService } from './services/local-storage.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -25,7 +39,7 @@ import { Subscription } from 'rxjs'
|
||||
export class AppComponent {
|
||||
@HostListener('document:keydown.enter', ['$event'])
|
||||
@debounce()
|
||||
handleKeyboardEvent () {
|
||||
handleKeyboardEvent() {
|
||||
const elems = document.getElementsByClassName('enter-click')
|
||||
const elem = elems[elems.length - 1] as HTMLButtonElement
|
||||
if (!elem || elem.classList.contains('no-click') || elem.disabled) return
|
||||
@@ -41,7 +55,7 @@ export class AppComponent {
|
||||
serverName: string
|
||||
unreadCount: number
|
||||
subscriptions: Subscription[] = []
|
||||
osUpdateProgress: { size: number, downloaded: number }
|
||||
osUpdateProgress: { size: number; downloaded: number }
|
||||
appPages = [
|
||||
{
|
||||
title: 'Services',
|
||||
@@ -63,9 +77,14 @@ export class AppComponent {
|
||||
url: '/notifications',
|
||||
icon: 'notifications-outline',
|
||||
},
|
||||
{
|
||||
title: 'Developer Tools',
|
||||
url: '/developer',
|
||||
icon: 'hammer-outline',
|
||||
},
|
||||
]
|
||||
|
||||
constructor (
|
||||
constructor(
|
||||
private readonly storage: Storage,
|
||||
private readonly authService: AuthService,
|
||||
private readonly router: Router,
|
||||
@@ -77,23 +96,24 @@ export class AppComponent {
|
||||
private readonly startupAlertsService: StartupAlertsService,
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly patch: PatchDbService,
|
||||
private readonly config: ConfigService,
|
||||
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.authService.init()
|
||||
await this.localStorageService.init()
|
||||
|
||||
this.router.initialNavigation()
|
||||
|
||||
// watch auth
|
||||
this.authService.watch$()
|
||||
.subscribe(async auth => {
|
||||
this.authService.watch$().subscribe(async auth => {
|
||||
// VERIFIED
|
||||
if (auth === AuthState.VERIFIED) {
|
||||
await this.patch.start()
|
||||
@@ -114,26 +134,27 @@ export class AppComponent {
|
||||
// watch status to display/hide maintenance page
|
||||
])
|
||||
|
||||
this.patch.watch$()
|
||||
.pipe(
|
||||
filter(obj => !isEmptyObject(obj)),
|
||||
take(1),
|
||||
)
|
||||
.subscribe(_ => {
|
||||
this.subscriptions = this.subscriptions.concat([
|
||||
// watch status to present toast for updated state
|
||||
this.watchStatus(),
|
||||
// watch update-progress to present progress bar when server is updating
|
||||
this.watchUpdateProgress(),
|
||||
// watch version to refresh browser window
|
||||
this.watchVersion(),
|
||||
// watch unread notification count to display toast
|
||||
this.watchNotifications(),
|
||||
// run startup alerts
|
||||
this.startupAlertsService.runChecks(),
|
||||
])
|
||||
})
|
||||
// UNVERIFIED
|
||||
this.patch
|
||||
.watch$()
|
||||
.pipe(
|
||||
filter(obj => !isEmptyObject(obj)),
|
||||
take(1),
|
||||
)
|
||||
.subscribe(_ => {
|
||||
this.subscriptions = this.subscriptions.concat([
|
||||
// watch status to present toast for updated state
|
||||
this.watchStatus(),
|
||||
// watch update-progress to present progress bar when server is updating
|
||||
this.watchUpdateProgress(),
|
||||
// watch version to refresh browser window
|
||||
this.watchVersion(),
|
||||
// watch unread notification count to display toast
|
||||
this.watchNotifications(),
|
||||
// run startup alerts
|
||||
this.startupAlertsService.runChecks(),
|
||||
])
|
||||
})
|
||||
// UNVERIFIED
|
||||
} else if (auth === AuthState.UNVERIFIED) {
|
||||
this.subscriptions.forEach(sub => sub.unsubscribe())
|
||||
this.subscriptions = []
|
||||
@@ -151,20 +172,22 @@ export class AppComponent {
|
||||
})
|
||||
}
|
||||
|
||||
async goToWebsite (): Promise<void> {
|
||||
async goToWebsite(): Promise<void> {
|
||||
let url: string
|
||||
if (this.config.isTor()) {
|
||||
url = 'http://privacy34kn4ez3y3nijweec6w4g54i3g54sdv7r5mr6soma3w4begyd.onion'
|
||||
url =
|
||||
'http://privacy34kn4ez3y3nijweec6w4g54i3g54sdv7r5mr6soma3w4begyd.onion'
|
||||
} else {
|
||||
url = 'https://start9.com'
|
||||
}
|
||||
window.open(url, '_blank', 'noreferrer')
|
||||
}
|
||||
|
||||
async presentAlertLogout () {
|
||||
async presentAlertLogout() {
|
||||
const alert = await this.alertCtrl.create({
|
||||
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: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
@@ -184,97 +207,97 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
// should wipe cache independant of actual BE logout
|
||||
private async logout () {
|
||||
this.embassyApi.logout({ })
|
||||
private async logout() {
|
||||
this.embassyApi.logout({})
|
||||
this.authService.setUnverified()
|
||||
}
|
||||
|
||||
private watchConnection (): Subscription {
|
||||
return this.connectionService.watchFailure$()
|
||||
.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(500),
|
||||
)
|
||||
.subscribe(async connectionFailure => {
|
||||
if (connectionFailure === ConnectionFailure.None) {
|
||||
if (this.offlineToast) {
|
||||
await this.offlineToast.dismiss()
|
||||
this.offlineToast = undefined
|
||||
private watchConnection(): Subscription {
|
||||
return this.connectionService
|
||||
.watchFailure$()
|
||||
.pipe(distinctUntilChanged(), debounceTime(500))
|
||||
.subscribe(async connectionFailure => {
|
||||
if (connectionFailure === ConnectionFailure.None) {
|
||||
if (this.offlineToast) {
|
||||
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
|
||||
.pipe(
|
||||
filter((e: RoutesRecognized) => !!e.urlAfterRedirects),
|
||||
)
|
||||
.subscribe(e => {
|
||||
const appPageIndex = this.appPages.findIndex(
|
||||
appPage => e.urlAfterRedirects.startsWith(appPage.url),
|
||||
)
|
||||
if (appPageIndex > -1) this.selectedIndex = appPageIndex
|
||||
})
|
||||
.pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects))
|
||||
.subscribe(e => {
|
||||
const appPageIndex = this.appPages.findIndex(appPage =>
|
||||
e.urlAfterRedirects.startsWith(appPage.url),
|
||||
)
|
||||
if (appPageIndex > -1) this.selectedIndex = appPageIndex
|
||||
})
|
||||
}
|
||||
|
||||
private watchStatus (): Subscription {
|
||||
return this.patch.watch$('server-info', 'status')
|
||||
.subscribe(status => {
|
||||
private watchStatus(): Subscription {
|
||||
return this.patch.watch$('server-info', 'status').subscribe(status => {
|
||||
if (status === ServerStatus.Updated && !this.updateToast) {
|
||||
this.presentToastUpdated()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private watchUpdateProgress (): Subscription {
|
||||
return this.patch.watch$('server-info', 'update-progress')
|
||||
.subscribe(progress => {
|
||||
this.osUpdateProgress = progress
|
||||
})
|
||||
private watchUpdateProgress(): Subscription {
|
||||
return this.patch
|
||||
.watch$('server-info', 'update-progress')
|
||||
.subscribe(progress => {
|
||||
this.osUpdateProgress = progress
|
||||
})
|
||||
}
|
||||
|
||||
private watchVersion (): Subscription {
|
||||
return this.patch.watch$('server-info', 'version')
|
||||
.subscribe(version => {
|
||||
private watchVersion(): Subscription {
|
||||
return this.patch.watch$('server-info', 'version').subscribe(version => {
|
||||
if (this.emver.compare(this.config.version, version) !== 0) {
|
||||
this.presentAlertRefreshNeeded()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private watchNotifications (): Subscription {
|
||||
private watchNotifications(): Subscription {
|
||||
let previous: number
|
||||
return this.patch.watch$('server-info', 'unread-notification-count')
|
||||
.subscribe(count => {
|
||||
this.unreadCount = count
|
||||
if (previous !== undefined && count > previous) this.presentToastNotifications()
|
||||
previous = count
|
||||
})
|
||||
return this.patch
|
||||
.watch$('server-info', 'unread-notification-count')
|
||||
.subscribe(count => {
|
||||
this.unreadCount = count
|
||||
if (previous !== undefined && count > previous)
|
||||
this.presentToastNotifications()
|
||||
previous = count
|
||||
})
|
||||
}
|
||||
|
||||
private async presentAlertRefreshNeeded () {
|
||||
private async presentAlertRefreshNeeded() {
|
||||
const alert = await this.alertCtrl.create({
|
||||
backdropDismiss: false,
|
||||
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: [
|
||||
{
|
||||
text: 'Refresh Page',
|
||||
@@ -288,12 +311,13 @@ export class AppComponent {
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
private async presentToastUpdated () {
|
||||
private async presentToastUpdated() {
|
||||
if (this.updateToast) return
|
||||
|
||||
this.updateToast = await this.toastCtrl.create({
|
||||
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',
|
||||
duration: 0,
|
||||
cssClass: 'success-toast',
|
||||
@@ -317,7 +341,7 @@ export class AppComponent {
|
||||
await this.updateToast.present()
|
||||
}
|
||||
|
||||
private async presentToastNotifications () {
|
||||
private async presentToastNotifications() {
|
||||
if (this.notificationToast) return
|
||||
|
||||
this.notificationToast = await this.toastCtrl.create({
|
||||
@@ -337,7 +361,9 @@ export class AppComponent {
|
||||
side: 'end',
|
||||
text: 'View',
|
||||
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()
|
||||
}
|
||||
|
||||
private async presentToastOffline (message: string | IonicSafeString, link?: string) {
|
||||
private async presentToastOffline(
|
||||
message: string | IonicSafeString,
|
||||
link?: string,
|
||||
) {
|
||||
if (this.offlineToast) {
|
||||
this.offlineToast.message = message
|
||||
return
|
||||
@@ -362,16 +391,14 @@ export class AppComponent {
|
||||
]
|
||||
|
||||
if (link) {
|
||||
buttons.push(
|
||||
{
|
||||
side: 'end',
|
||||
text: 'View solutions',
|
||||
handler: () => {
|
||||
window.open(link, '_blank', 'noreferrer')
|
||||
return false
|
||||
},
|
||||
buttons.push({
|
||||
side: 'end',
|
||||
text: 'View solutions',
|
||||
handler: () => {
|
||||
window.open(link, '_blank', 'noreferrer')
|
||||
return false
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
this.offlineToast = await this.toastCtrl.create({
|
||||
@@ -385,7 +412,7 @@ export class AppComponent {
|
||||
await this.offlineToast.present()
|
||||
}
|
||||
|
||||
private async restart (): Promise<void> {
|
||||
private async restart(): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Restarting...',
|
||||
@@ -394,7 +421,7 @@ export class AppComponent {
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.restartServer({ })
|
||||
await this.embassyApi.restartServer({})
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
@@ -402,7 +429,7 @@ export class AppComponent {
|
||||
}
|
||||
}
|
||||
|
||||
splitPaneVisible (e: any) {
|
||||
splitPaneVisible(e: any) {
|
||||
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 { LiveApiService } from './services/api/embassy-live-api.service'
|
||||
import { WorkspaceConfig } from '@shared'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
|
||||
const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||
|
||||
@@ -47,6 +48,7 @@ const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||
MarkdownPageModule,
|
||||
GenericInputComponentModule,
|
||||
SharingModule,
|
||||
MonacoEditorModule,
|
||||
],
|
||||
providers: [
|
||||
FormBuilder,
|
||||
|
||||
@@ -9,20 +9,22 @@ import { getErrorMessage } from 'src/app/services/error-toast.service'
|
||||
styleUrls: ['./markdown.page.scss'],
|
||||
})
|
||||
export class MarkdownPage {
|
||||
@Input() contentUrl: string
|
||||
@Input() contentUrl?: string
|
||||
@Input() content?: string
|
||||
@Input() title: string
|
||||
content: string
|
||||
loading = true
|
||||
loadingError: string | IonicSafeString
|
||||
|
||||
constructor (
|
||||
constructor(
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly embassyApi: ApiService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async ngOnInit () {
|
||||
async ngOnInit() {
|
||||
try {
|
||||
this.content = await this.embassyApi.getStatic(this.contentUrl)
|
||||
if (!this.content) {
|
||||
this.content = await this.embassyApi.getStatic(this.contentUrl)
|
||||
}
|
||||
const links = document.links
|
||||
for (let i = 0, linksLength = links.length; i < linksLength; i++) {
|
||||
if (links[i].hostname != window.location.hostname) {
|
||||
@@ -38,7 +40,7 @@ export class MarkdownPage {
|
||||
}
|
||||
}
|
||||
|
||||
async dismiss () {
|
||||
async dismiss() {
|
||||
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-back-button defaultHref="embassy"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Preferences</ion-title>
|
||||
<ion-title (click)="addClick()">Preferences</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
|
||||
<ion-item-group *ngIf="patch.data['server-info'] as server">
|
||||
<ion-item-divider>General</ion-item-divider>
|
||||
<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-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-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 button (click)="presentModalValueEdit('password')">
|
||||
@@ -27,15 +31,15 @@
|
||||
</ion-item> -->
|
||||
|
||||
<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-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 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-content>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { Component, ViewChild } from '@angular/core'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { IonContent, LoadingController, ModalController } from '@ionic/angular'
|
||||
import { GenericInputComponent, GenericInputOptions } from 'src/app/modals/generic-input/generic-input.component'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import {
|
||||
IonContent,
|
||||
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 { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
import { LocalStorageService } from '../../../services/local-storage.service'
|
||||
|
||||
@Component({
|
||||
selector: 'preferences',
|
||||
@@ -13,26 +21,28 @@ import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
})
|
||||
export class PreferencesPage {
|
||||
@ViewChild(IonContent) content: IonContent
|
||||
fields = fields
|
||||
defaultName: string
|
||||
clicks = 0
|
||||
|
||||
constructor (
|
||||
constructor(
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly api: ApiService,
|
||||
public readonly serverConfig: ServerConfigService,
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly localStorageService: LocalStorageService,
|
||||
public readonly patch: PatchDbService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
ngOnInit () {
|
||||
ngOnInit() {
|
||||
this.defaultName = `Embassy-${this.patch.getData()['server-info'].id}`
|
||||
}
|
||||
|
||||
ngAfterViewInit () {
|
||||
ngAfterViewInit() {
|
||||
this.content.scrollToPoint(undefined, 1)
|
||||
}
|
||||
|
||||
async presentModalName (): Promise<void> {
|
||||
async presentModalName(): Promise<void> {
|
||||
const options: GenericInputOptions = {
|
||||
title: 'Edit Device Name',
|
||||
message: 'This is for your reference only.',
|
||||
@@ -42,7 +52,8 @@ export class PreferencesPage {
|
||||
nullable: true,
|
||||
initialValue: this.patch.getData().ui.name,
|
||||
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({
|
||||
@@ -55,7 +66,7 @@ export class PreferencesPage {
|
||||
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({
|
||||
spinner: 'lines',
|
||||
message: 'Saving...',
|
||||
@@ -69,14 +80,22 @@ export class PreferencesPage {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fields: ConfigSpec = {
|
||||
'name': {
|
||||
name: 'Device Name',
|
||||
type: 'string',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
},
|
||||
async addClick() {
|
||||
this.clicks++
|
||||
if (this.clicks >= 5) {
|
||||
this.clicks = 0
|
||||
const newVal = await this.localStorageService.toggleShowDevTools()
|
||||
const toast = await this.toastCtrl.create({
|
||||
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>
|
||||
|
||||
// core spec types. These types provide the metadata for performing validations
|
||||
export type ValueSpecOf<T extends ValueType> =
|
||||
T extends 'string' ? ValueSpecString :
|
||||
T extends 'number' ? ValueSpecNumber :
|
||||
T extends 'boolean' ? ValueSpecBoolean :
|
||||
T extends 'enum' ? ValueSpecEnum :
|
||||
T extends 'list' ? ValueSpecList :
|
||||
T extends 'object' ? ValueSpecObject :
|
||||
T extends 'pointer' ? ValueSpecPointer :
|
||||
T extends 'union' ? ValueSpecUnion :
|
||||
never
|
||||
export type ValueSpecOf<T extends ValueType> = T extends 'string'
|
||||
? ValueSpecString
|
||||
: T extends 'number'
|
||||
? ValueSpecNumber
|
||||
: T extends 'boolean'
|
||||
? ValueSpecBoolean
|
||||
: T extends 'enum'
|
||||
? ValueSpecEnum
|
||||
: T extends 'list'
|
||||
? ValueSpecList
|
||||
: T extends 'object'
|
||||
? ValueSpecObject
|
||||
: T extends 'pointer'
|
||||
? ValueSpecPointer
|
||||
: T extends 'union'
|
||||
? ValueSpecUnion
|
||||
: never
|
||||
|
||||
export interface ValueSpecString extends ListValueSpecString, WithStandalone {
|
||||
type: 'string'
|
||||
@@ -62,20 +79,30 @@ export interface WithStandalone {
|
||||
}
|
||||
|
||||
// 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
|
||||
export type ListValueSpecOf<T extends ListValueSpecType> =
|
||||
T extends 'string' ? ListValueSpecString :
|
||||
T extends 'number' ? ListValueSpecNumber :
|
||||
T extends 'enum' ? ListValueSpecEnum :
|
||||
T extends 'object' ? ListValueSpecObject :
|
||||
T extends 'union' ? ListValueSpecUnion :
|
||||
never
|
||||
export type ListValueSpecOf<T extends ListValueSpecType> = T extends 'string'
|
||||
? ListValueSpecString
|
||||
: T extends 'number'
|
||||
? ListValueSpecNumber
|
||||
: T extends 'enum'
|
||||
? ListValueSpecEnum
|
||||
: T extends 'object'
|
||||
? ListValueSpecObject
|
||||
: T extends 'union'
|
||||
? ListValueSpecUnion
|
||||
: never
|
||||
|
||||
// represents a spec for a list
|
||||
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>
|
||||
export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStandalone {
|
||||
export interface ValueSpecListOf<T extends ListValueSpecType>
|
||||
extends WithStandalone {
|
||||
type: 'list'
|
||||
subtype: 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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -105,7 +135,6 @@ export interface ListValueSpecNumber {
|
||||
|
||||
export interface ListValueSpecEnum {
|
||||
values: string[]
|
||||
'values-set'?: Set<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
|
||||
name: string
|
||||
description?: string
|
||||
'variant-names': { // the name of each variant
|
||||
'variant-names': {
|
||||
// the name of each variant
|
||||
[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 { ApiService } from './embassy-api.service'
|
||||
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 { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
||||
import { Mock } from './api.fixures'
|
||||
@@ -17,24 +25,22 @@ export class MockApiService extends ApiService {
|
||||
private readonly revertTime = 4000
|
||||
sequence: number
|
||||
|
||||
constructor (
|
||||
private readonly bootstrapper: LocalStorageBootstrap,
|
||||
) {
|
||||
constructor(private readonly bootstrapper: LocalStorageBootstrap) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getStatic (url: string): Promise<string> {
|
||||
async getStatic(url: string): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return markdown
|
||||
}
|
||||
|
||||
// db
|
||||
|
||||
async getRevisions (since: number): Promise<RR.GetRevisionsRes> {
|
||||
async getRevisions(since: number): Promise<RR.GetRevisionsRes> {
|
||||
return this.getDump()
|
||||
}
|
||||
|
||||
async getDump (): Promise<RR.GetDumpRes> {
|
||||
async getDump(): Promise<RR.GetDumpRes> {
|
||||
const cache = await this.bootstrapper.init()
|
||||
return {
|
||||
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)
|
||||
const patch = [
|
||||
{
|
||||
@@ -57,7 +63,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// auth
|
||||
|
||||
async login (params: RR.LoginReq): Promise<RR.loginRes> {
|
||||
async login(params: RR.LoginReq): Promise<RR.loginRes> {
|
||||
await pauseFor(2000)
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -67,24 +73,26 @@ export class MockApiService extends ApiService {
|
||||
return null
|
||||
}
|
||||
|
||||
async logout (params: RR.LogoutReq): Promise<RR.LogoutRes> {
|
||||
async logout(params: RR.LogoutReq): Promise<RR.LogoutRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
async getSessions (params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
|
||||
async getSessions(params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.Sessions
|
||||
}
|
||||
|
||||
async killSessions (params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
|
||||
async killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
// server
|
||||
|
||||
async setShareStatsRaw (params: RR.SetShareStatsReq): Promise<RR.SetShareStatsRes> {
|
||||
async setShareStatsRaw(
|
||||
params: RR.SetShareStatsReq,
|
||||
): Promise<RR.SetShareStatsRes> {
|
||||
await pauseFor(2000)
|
||||
const patch = [
|
||||
{
|
||||
@@ -97,14 +105,20 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async getServerLogs (params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
|
||||
async getServerLogs(
|
||||
params: RR.GetServerLogsReq,
|
||||
): Promise<RR.GetServerLogsRes> {
|
||||
await pauseFor(2000)
|
||||
let entries: Log[]
|
||||
if (Math.random() < .2) {
|
||||
if (Math.random() < 0.2) {
|
||||
entries = Mock.ServerLogs
|
||||
} else {
|
||||
const arrLength = params.limit ? Math.ceil(params.limit / Mock.ServerLogs.length) : 10
|
||||
entries = new Array(arrLength).fill(Mock.ServerLogs).reduce((acc, val) => acc.concat(val), [])
|
||||
const arrLength = params.limit
|
||||
? Math.ceil(params.limit / Mock.ServerLogs.length)
|
||||
: 10
|
||||
entries = new Array(arrLength)
|
||||
.fill(Mock.ServerLogs)
|
||||
.reduce((acc, val) => acc.concat(val), [])
|
||||
}
|
||||
return {
|
||||
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)
|
||||
return Mock.getServerMetrics()
|
||||
}
|
||||
|
||||
async getPkgMetrics (params: RR.GetServerMetricsReq): Promise<RR.GetPackageMetricsRes> {
|
||||
async getPkgMetrics(
|
||||
params: RR.GetServerMetricsReq,
|
||||
): Promise<RR.GetPackageMetricsRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.getAppMetrics()
|
||||
}
|
||||
|
||||
async updateServerRaw (params: RR.UpdateServerReq): Promise<RR.UpdateServerRes> {
|
||||
async updateServerRaw(
|
||||
params: RR.UpdateServerReq,
|
||||
): Promise<RR.UpdateServerRes> {
|
||||
await pauseFor(2000)
|
||||
const initialProgress = {
|
||||
size: 10000,
|
||||
@@ -145,65 +165,77 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch, 'updating')
|
||||
}
|
||||
|
||||
async restartServer (params: RR.RestartServerReq): Promise<RR.RestartServerRes> {
|
||||
async restartServer(
|
||||
params: RR.RestartServerReq,
|
||||
): Promise<RR.RestartServerRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
async shutdownServer (params: RR.ShutdownServerReq): Promise<RR.ShutdownServerRes> {
|
||||
async shutdownServer(
|
||||
params: RR.ShutdownServerReq,
|
||||
): Promise<RR.ShutdownServerRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
async systemRebuild (params: RR.RestartServerReq): Promise<RR.RestartServerRes> {
|
||||
async systemRebuild(
|
||||
params: RR.RestartServerReq,
|
||||
): Promise<RR.RestartServerRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
async getEos (params: RR.GetMarketplaceEOSReq): Promise<RR.GetMarketplaceEOSRes> {
|
||||
async getEos(
|
||||
params: RR.GetMarketplaceEOSReq,
|
||||
): Promise<RR.GetMarketplaceEOSRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.MarketplaceEos
|
||||
}
|
||||
|
||||
async getMarketplaceData (params: RR.GetMarketplaceDataReq): Promise<RR.GetMarketplaceDataRes> {
|
||||
async getMarketplaceData(
|
||||
params: RR.GetMarketplaceDataReq,
|
||||
): Promise<RR.GetMarketplaceDataRes> {
|
||||
await pauseFor(2000)
|
||||
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)
|
||||
return Mock.MarketplacePkgsList
|
||||
}
|
||||
|
||||
async getReleaseNotes (params: RR.GetReleaseNotesReq): Promise<RR.GetReleaseNotesRes> {
|
||||
async getReleaseNotes(
|
||||
params: RR.GetReleaseNotesReq,
|
||||
): Promise<RR.GetReleaseNotesRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.ReleaseNotes
|
||||
}
|
||||
|
||||
async getLatestVersion (params: RR.GetLatestVersionReq): Promise<RR.GetLatestVersionRes> {
|
||||
async getLatestVersion(
|
||||
params: RR.GetLatestVersionReq,
|
||||
): Promise<RR.GetLatestVersionRes> {
|
||||
await pauseFor(2000)
|
||||
return params.ids.reduce((obj, id) => {
|
||||
obj[id] = '1.3.0'
|
||||
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
|
||||
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
||||
// await pauseFor(2000)
|
||||
@@ -212,7 +244,9 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// notification
|
||||
|
||||
async getNotificationsRaw (params: RR.GetNotificationsReq): Promise<RR.GetNotificationsRes> {
|
||||
async getNotificationsRaw(
|
||||
params: RR.GetNotificationsReq,
|
||||
): Promise<RR.GetNotificationsRes> {
|
||||
await pauseFor(2000)
|
||||
const patch = [
|
||||
{
|
||||
@@ -225,72 +259,82 @@ export class MockApiService extends ApiService {
|
||||
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)
|
||||
return null
|
||||
}
|
||||
|
||||
async deleteAllNotifications (params: RR.DeleteAllNotificationsReq): Promise<RR.DeleteAllNotificationsRes> {
|
||||
async deleteAllNotifications(
|
||||
params: RR.DeleteAllNotificationsReq,
|
||||
): Promise<RR.DeleteAllNotificationsRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
// wifi
|
||||
|
||||
async getWifi (params: RR.GetWifiReq): Promise < RR.GetWifiRes > {
|
||||
async getWifi(params: RR.GetWifiReq): Promise<RR.GetWifiRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.Wifi
|
||||
}
|
||||
|
||||
async setWifiCountry (params: RR.SetWifiCountryReq): Promise <RR.SetWifiCountryRes> {
|
||||
async setWifiCountry(
|
||||
params: RR.SetWifiCountryReq,
|
||||
): Promise<RR.SetWifiCountryRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
async addWifi (params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
|
||||
async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
async connectWifi (params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
|
||||
async connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
async deleteWifi (params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
|
||||
async deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
// ssh
|
||||
|
||||
async getSshKeys (params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
|
||||
async getSshKeys(params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.SshKeys
|
||||
}
|
||||
|
||||
async addSshKey (params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
|
||||
async addSshKey(params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.SshKey
|
||||
}
|
||||
|
||||
async deleteSshKey (params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
|
||||
async deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
|
||||
await pauseFor(2000)
|
||||
return null
|
||||
}
|
||||
|
||||
// backup
|
||||
|
||||
async getBackupTargets (params: RR.GetBackupTargetsReq): Promise<RR.GetBackupTargetsRes> {
|
||||
async getBackupTargets(
|
||||
params: RR.GetBackupTargetsReq,
|
||||
): Promise<RR.GetBackupTargetsRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.BackupTargets
|
||||
}
|
||||
|
||||
async addBackupTarget (params: RR.AddBackupTargetReq): Promise<RR.AddBackupTargetRes> {
|
||||
async addBackupTarget(
|
||||
params: RR.AddBackupTargetReq,
|
||||
): Promise<RR.AddBackupTargetRes> {
|
||||
await pauseFor(2000)
|
||||
const { hostname, path, username } = params
|
||||
return {
|
||||
'latfgvwdbhjsndmk': {
|
||||
latfgvwdbhjsndmk: {
|
||||
type: 'cifs',
|
||||
hostname,
|
||||
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)
|
||||
const { id, hostname, path, username } = params
|
||||
return {
|
||||
[id]: {
|
||||
...Mock.BackupTargets[id] as CifsBackupTarget,
|
||||
...(Mock.BackupTargets[id] as CifsBackupTarget),
|
||||
hostname,
|
||||
path,
|
||||
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)
|
||||
return null
|
||||
}
|
||||
|
||||
async getBackupInfo (params: RR.GetBackupInfoReq): Promise<RR.GetBackupInfoRes> {
|
||||
async getBackupInfo(
|
||||
params: RR.GetBackupInfoReq,
|
||||
): Promise<RR.GetBackupInfoRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.BackupInfo
|
||||
}
|
||||
|
||||
async createBackupRaw (params: RR.CreateBackupReq): Promise<RR.CreateBackupRes> {
|
||||
async createBackupRaw(
|
||||
params: RR.CreateBackupReq,
|
||||
): Promise<RR.CreateBackupRes> {
|
||||
await pauseFor(2000)
|
||||
const path = '/server-info/status'
|
||||
const ids = ['bitcoind', 'lnd']
|
||||
@@ -373,19 +425,27 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// package
|
||||
|
||||
async getPackageProperties (params: RR.GetPackagePropertiesReq): Promise<RR.GetPackagePropertiesRes<2>['data']> {
|
||||
async getPackageProperties(
|
||||
params: RR.GetPackagePropertiesReq,
|
||||
): Promise<RR.GetPackagePropertiesRes<2>['data']> {
|
||||
await pauseFor(2000)
|
||||
return parsePropertiesPermissive(Mock.PackageProperties)
|
||||
}
|
||||
|
||||
async getPackageLogs (params: RR.GetPackageLogsReq): Promise<RR.GetPackageLogsRes> {
|
||||
async getPackageLogs(
|
||||
params: RR.GetPackageLogsReq,
|
||||
): Promise<RR.GetPackageLogsRes> {
|
||||
await pauseFor(2000)
|
||||
let entries
|
||||
if (Math.random() < .2) {
|
||||
entries = Mock.PackageLogs
|
||||
if (Math.random() < 0.2) {
|
||||
entries = Mock.PackageLogs
|
||||
} else {
|
||||
const arrLength = params.limit ? Math.ceil(params.limit / Mock.PackageLogs.length) : 10
|
||||
entries = new Array(arrLength).fill(Mock.PackageLogs).reduce((acc, val) => acc.concat(val), [])
|
||||
const arrLength = params.limit
|
||||
? Math.ceil(params.limit / Mock.PackageLogs.length)
|
||||
: 10
|
||||
entries = new Array(arrLength)
|
||||
.fill(Mock.PackageLogs)
|
||||
.reduce((acc, val) => acc.concat(val), [])
|
||||
}
|
||||
return {
|
||||
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)
|
||||
const initialProgress: InstallProgress = {
|
||||
size: 120,
|
||||
@@ -427,12 +489,16 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async dryUpdatePackage (params: RR.DryUpdatePackageReq): Promise<RR.DryUpdatePackageRes> {
|
||||
async dryUpdatePackage(
|
||||
params: RR.DryUpdatePackageReq,
|
||||
): Promise<RR.DryUpdatePackageRes> {
|
||||
await pauseFor(2000)
|
||||
return { }
|
||||
return {}
|
||||
}
|
||||
|
||||
async getPackageConfig (params: RR.GetPackageConfigReq): Promise<RR.GetPackageConfigRes> {
|
||||
async getPackageConfig(
|
||||
params: RR.GetPackageConfigReq,
|
||||
): Promise<RR.GetPackageConfigRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
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)
|
||||
return { }
|
||||
return {}
|
||||
}
|
||||
|
||||
async setPackageConfigRaw (params: RR.SetPackageConfigReq): Promise<RR.SetPackageConfigRes> {
|
||||
async setPackageConfigRaw(
|
||||
params: RR.SetPackageConfigReq,
|
||||
): Promise<RR.SetPackageConfigRes> {
|
||||
await pauseFor(2000)
|
||||
const patch = [
|
||||
{
|
||||
@@ -457,10 +527,11 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async restorePackagesRaw (params: RR.RestorePackagesReq): Promise<RR.RestorePackagesRes> {
|
||||
async restorePackagesRaw(
|
||||
params: RR.RestorePackagesReq,
|
||||
): Promise<RR.RestorePackagesRes> {
|
||||
await pauseFor(2000)
|
||||
const patch: Operation[] = params.ids.map(id => {
|
||||
|
||||
const initialProgress: InstallProgress = {
|
||||
size: 120,
|
||||
downloaded: 120,
|
||||
@@ -492,12 +563,16 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async executePackageAction (params: RR.ExecutePackageActionReq): Promise<RR.ExecutePackageActionRes> {
|
||||
async executePackageAction(
|
||||
params: RR.ExecutePackageActionReq,
|
||||
): Promise<RR.ExecutePackageActionRes> {
|
||||
await pauseFor(2000)
|
||||
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`
|
||||
|
||||
await pauseFor(2000)
|
||||
@@ -551,11 +626,11 @@ export class MockApiService extends ApiService {
|
||||
message: 'Bitcoin is syncing from genesis',
|
||||
},
|
||||
'p2p-interface': {
|
||||
result: 'success',
|
||||
result: 'success',
|
||||
},
|
||||
'rpc-interface': {
|
||||
result: 'failure',
|
||||
error: 'RPC interface unreachable.',
|
||||
result: 'failure',
|
||||
error: 'RPC interface unreachable.',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -574,10 +649,12 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(originalPatch)
|
||||
}
|
||||
|
||||
async dryStopPackage (params: RR.DryStopPackageReq): Promise<RR.DryStopPackageRes> {
|
||||
async dryStopPackage(
|
||||
params: RR.DryStopPackageReq,
|
||||
): Promise<RR.DryStopPackageRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'lnd': {
|
||||
lnd: {
|
||||
dependency: 'bitcoind',
|
||||
error: {
|
||||
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)
|
||||
const path = `/package-data/${params.id}/installed/status/main`
|
||||
|
||||
@@ -610,19 +687,23 @@ export class MockApiService extends ApiService {
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: { },
|
||||
value: {},
|
||||
},
|
||||
]
|
||||
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async dryUninstallPackage (params: RR.DryUninstallPackageReq): Promise<RR.DryUninstallPackageRes> {
|
||||
async dryUninstallPackage(
|
||||
params: RR.DryUninstallPackageReq,
|
||||
): Promise<RR.DryUninstallPackageRes> {
|
||||
await pauseFor(2000)
|
||||
return { }
|
||||
return {}
|
||||
}
|
||||
|
||||
async uninstallPackageRaw (params: RR.UninstallPackageReq): Promise<RR.UninstallPackageRes> {
|
||||
async uninstallPackageRaw(
|
||||
params: RR.UninstallPackageReq,
|
||||
): Promise<RR.UninstallPackageRes> {
|
||||
await pauseFor(2000)
|
||||
|
||||
setTimeout(async () => {
|
||||
@@ -646,7 +727,9 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async deleteRecoveredPackageRaw (params: RR.DeleteRecoveredPackageReq): Promise<RR.DeleteRecoveredPackageRes> {
|
||||
async deleteRecoveredPackageRaw(
|
||||
params: RR.DeleteRecoveredPackageReq,
|
||||
): Promise<RR.DeleteRecoveredPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const patch = [
|
||||
{
|
||||
@@ -657,7 +740,9 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async dryConfigureDependency (params: RR.DryConfigureDependencyReq): Promise<RR.DryConfigureDependencyRes> {
|
||||
async dryConfigureDependency(
|
||||
params: RR.DryConfigureDependencyReq,
|
||||
): Promise<RR.DryConfigureDependencyRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'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 = [
|
||||
{ progress: 'downloaded', completion: 'download-complete'},
|
||||
{ progress: 'validated', completion: 'validation-complete'},
|
||||
{ progress: 'unpacked', completion: 'unpack-complete'},
|
||||
{ progress: 'downloaded', completion: 'download-complete' },
|
||||
{ progress: 'validated', completion: 'validation-complete' },
|
||||
{ progress: 'unpacked', completion: 'unpack-complete' },
|
||||
]
|
||||
for (let phase of phases) {
|
||||
let i = initialProgress[phase.progress]
|
||||
@@ -709,7 +797,7 @@ export class MockApiService extends ApiService {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private async updateOSProgress (size: number) {
|
||||
private async updateOSProgress(size: number) {
|
||||
let downloaded = 0
|
||||
while (downloaded < size) {
|
||||
await pauseFor(250)
|
||||
@@ -759,7 +847,7 @@ export class MockApiService extends ApiService {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private async updateMock (patch: Operation[]): Promise<void> {
|
||||
private async updateMock(patch: Operation[]): Promise<void> {
|
||||
if (!this.sequence) {
|
||||
const { sequence } = await this.bootstrapper.init()
|
||||
this.sequence = sequence
|
||||
@@ -772,7 +860,10 @@ export class MockApiService extends ApiService {
|
||||
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) {
|
||||
const { sequence } = await this.bootstrapper.init()
|
||||
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',
|
||||
}
|
||||
export interface RecoveredPackageDataEntry {
|
||||
title: string,
|
||||
icon: URL,
|
||||
version: string,
|
||||
title: string
|
||||
icon: URL
|
||||
version: string
|
||||
}
|
||||
|
||||
export interface PackageDataEntry {
|
||||
@@ -53,8 +53,8 @@ export interface PackageDataEntry {
|
||||
icon: URL
|
||||
}
|
||||
manifest: Manifest
|
||||
installed?: InstalledPackageDataEntry, // exists when: installed, updating
|
||||
'install-progress'?: InstallProgress, // exists when: installing, updating
|
||||
installed?: InstalledPackageDataEntry // exists when: installed, updating
|
||||
'install-progress'?: InstallProgress // exists when: installing, updating
|
||||
}
|
||||
|
||||
export interface InstallProgress {
|
||||
@@ -69,7 +69,7 @@ export interface InstallProgress {
|
||||
|
||||
export interface InstalledPackageDataEntry {
|
||||
status: Status
|
||||
manifest: Manifest,
|
||||
manifest: Manifest
|
||||
'last-backup': string | null
|
||||
'system-pointers': any[]
|
||||
'current-dependents': { [id: string]: CurrentDependencyInfo }
|
||||
@@ -81,7 +81,7 @@ export interface InstalledPackageDataEntry {
|
||||
}
|
||||
}
|
||||
'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
|
||||
}
|
||||
main: ActionImpl
|
||||
'health-checks': Record<string, ActionImpl & { name: string, description: string }>
|
||||
'health-checks': Record<
|
||||
string,
|
||||
ActionImpl & { name: string; description: string }
|
||||
>
|
||||
config: ConfigActions | null
|
||||
volumes: Record<string, Volume>
|
||||
'min-os-version': string
|
||||
@@ -209,7 +212,7 @@ export interface TorConfig {
|
||||
}
|
||||
|
||||
export type LanConfig = {
|
||||
[port: number]: { ssl: boolean, mapping: number }
|
||||
[port: number]: { ssl: boolean; mapping: number }
|
||||
}
|
||||
|
||||
export interface BackupActions {
|
||||
@@ -237,7 +240,12 @@ export interface Status {
|
||||
'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 {
|
||||
status: PackageMainStatus.Stopped
|
||||
@@ -270,11 +278,12 @@ export enum PackageMainStatus {
|
||||
BackingUp = 'backing-up',
|
||||
}
|
||||
|
||||
export type HealthCheckResult = HealthCheckResultStarting |
|
||||
HealthCheckResultLoading |
|
||||
HealthCheckResultDisabled |
|
||||
HealthCheckResultSuccess |
|
||||
HealthCheckResultFailure
|
||||
export type HealthCheckResult =
|
||||
| HealthCheckResultStarting
|
||||
| HealthCheckResultLoading
|
||||
| HealthCheckResultDisabled
|
||||
| HealthCheckResultSuccess
|
||||
| HealthCheckResultFailure
|
||||
|
||||
export enum HealthResult {
|
||||
Starting = 'starting',
|
||||
@@ -306,12 +315,13 @@ export interface HealthCheckResultFailure {
|
||||
error: string
|
||||
}
|
||||
|
||||
export type DependencyError = DependencyErrorNotInstalled |
|
||||
DependencyErrorNotRunning |
|
||||
DependencyErrorIncorrectVersion |
|
||||
DependencyErrorConfigUnsatisfied |
|
||||
DependencyErrorHealthChecksFailed |
|
||||
DependencyErrorTransitive
|
||||
export type DependencyError =
|
||||
| DependencyErrorNotInstalled
|
||||
| DependencyErrorNotRunning
|
||||
| DependencyErrorIncorrectVersion
|
||||
| DependencyErrorConfigUnsatisfied
|
||||
| DependencyErrorHealthChecksFailed
|
||||
| DependencyErrorTransitive
|
||||
|
||||
export enum DependencyErrorType {
|
||||
NotInstalled = 'not-installed',
|
||||
@@ -357,18 +367,21 @@ export interface DependencyInfo {
|
||||
|
||||
export interface DependencyEntry {
|
||||
version: string
|
||||
requirement: {
|
||||
type: 'opt-in'
|
||||
how: string
|
||||
} | {
|
||||
type: 'opt-out'
|
||||
how: string
|
||||
} | {
|
||||
type: 'required'
|
||||
}
|
||||
requirement:
|
||||
| {
|
||||
type: 'opt-in'
|
||||
how: string
|
||||
}
|
||||
| {
|
||||
type: 'opt-out'
|
||||
how: string
|
||||
}
|
||||
| {
|
||||
type: 'required'
|
||||
}
|
||||
description: string | null
|
||||
config: {
|
||||
check: ActionImpl,
|
||||
check: ActionImpl
|
||||
'auto-configure': ActionImpl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,14 @@ import { ErrorToastService } from './error-toast.service'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ServerConfigService {
|
||||
|
||||
constructor (
|
||||
constructor(
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly embassyApi: ApiService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async presentAlert (key: string, current?: any): Promise<HTMLIonAlertElement> {
|
||||
async presentAlert(key: string, current?: any): Promise<HTMLIonAlertElement> {
|
||||
const spec = serverConfig[key]
|
||||
|
||||
let inputs: AlertInput[]
|
||||
@@ -100,14 +99,11 @@ export class ServerConfigService {
|
||||
|
||||
saveFns: { [key: string]: (val: any) => Promise<any> } = {
|
||||
'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) => {
|
||||
return this.embassyApi.setShareStats({ value: enabled })
|
||||
},
|
||||
@@ -121,31 +117,16 @@ export const serverConfig: ConfigSpec = {
|
||||
'auto-check-updates': {
|
||||
type: 'boolean',
|
||||
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,
|
||||
},
|
||||
// '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': {
|
||||
type: 'boolean',
|
||||
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,
|
||||
},
|
||||
// password: {
|
||||
|
||||
Reference in New Issue
Block a user