mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
fix: finish porting minor changes to major (#2799)
This commit is contained in:
486
web/package-lock.json
generated
486
web/package-lock.json
generated
@@ -62,7 +62,7 @@
|
||||
"pbkdf2": "^3.1.2",
|
||||
"rxjs": "^7.5.6",
|
||||
"swiper": "^8.2.4",
|
||||
"ts-matches": "^5.5.1",
|
||||
"ts-matches": "^6.1.0",
|
||||
"tslib": "^2.8.1",
|
||||
"uuid": "^8.3.2",
|
||||
"zone.js": "^0.14.2"
|
||||
@@ -886,9 +886,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz",
|
||||
"integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz",
|
||||
"integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -972,20 +972,6 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz",
|
||||
"integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.25.9",
|
||||
"@babel/types": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz",
|
||||
@@ -1059,14 +1045,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-regexp-features-plugin": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz",
|
||||
"integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz",
|
||||
"integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
"regexpu-core": "^6.1.1",
|
||||
"regexpu-core": "^6.2.0",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1247,20 +1233,6 @@
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-simple-access": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz",
|
||||
"integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.25.9",
|
||||
"@babel/types": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
|
||||
@@ -1348,13 +1320,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
|
||||
"integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
|
||||
"integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.0"
|
||||
"@babel/types": "^7.26.3"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -1916,13 +1888,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-exponentiation-operator": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz",
|
||||
"integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
|
||||
"integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9",
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2065,15 +2036,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-modules-commonjs": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz",
|
||||
"integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz",
|
||||
"integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
"@babel/helper-simple-access": "^7.25.9"
|
||||
"@babel/helper-module-transforms": "^7.26.0",
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -2692,17 +2662,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz",
|
||||
"integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==",
|
||||
"version": "7.26.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz",
|
||||
"integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.25.9",
|
||||
"@babel/generator": "^7.25.9",
|
||||
"@babel/parser": "^7.25.9",
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.3",
|
||||
"@babel/parser": "^7.26.3",
|
||||
"@babel/template": "^7.25.9",
|
||||
"@babel/types": "^7.25.9",
|
||||
"@babel/types": "^7.26.3",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -2711,14 +2681,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/@babel/generator": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
|
||||
"integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
|
||||
"integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.2",
|
||||
"@babel/types": "^7.26.0",
|
||||
"@babel/parser": "^7.26.3",
|
||||
"@babel/types": "^7.26.3",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -2741,9 +2711,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
|
||||
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
|
||||
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4030,9 +4000,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz",
|
||||
"integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz",
|
||||
"integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -4044,9 +4014,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz",
|
||||
"integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz",
|
||||
"integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4058,9 +4028,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz",
|
||||
"integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz",
|
||||
"integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4072,9 +4042,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz",
|
||||
"integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz",
|
||||
"integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4086,9 +4056,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz",
|
||||
"integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz",
|
||||
"integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4100,9 +4070,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz",
|
||||
"integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz",
|
||||
"integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4114,9 +4084,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz",
|
||||
"integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz",
|
||||
"integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -4128,9 +4098,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz",
|
||||
"integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz",
|
||||
"integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -4142,9 +4112,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz",
|
||||
"integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz",
|
||||
"integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4156,9 +4126,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz",
|
||||
"integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz",
|
||||
"integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4169,10 +4139,24 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz",
|
||||
"integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz",
|
||||
"integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz",
|
||||
"integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -4184,9 +4168,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz",
|
||||
"integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz",
|
||||
"integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -4198,9 +4182,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz",
|
||||
"integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz",
|
||||
"integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -4212,9 +4196,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz",
|
||||
"integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz",
|
||||
"integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4226,9 +4210,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz",
|
||||
"integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz",
|
||||
"integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4240,9 +4224,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz",
|
||||
"integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz",
|
||||
"integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4254,9 +4238,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz",
|
||||
"integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz",
|
||||
"integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -4268,9 +4252,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz",
|
||||
"integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz",
|
||||
"integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4282,9 +4266,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/wasm-node": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.27.4.tgz",
|
||||
"integrity": "sha512-Q1b1A1RAP4Pp4qwU59n4819nJ4v4CDgBbY1/FbC1pW5PmHHI36yyqDMB0BW/F+3lLDt0KDd+t7tBrki9oSEg/w==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.28.1.tgz",
|
||||
"integrity": "sha512-t4ckEC09V3wbe0r6T4fGjq85lEbvGcGxn7QYYgjHyKNzZaQU5kFqr4FsavXYHRiVNYq8m+dRhdGjpfcC9UzzPg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4554,9 +4538,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/i18n": {
|
||||
"version": "4.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.16.0.tgz",
|
||||
"integrity": "sha512-Vdb7eyuKiOlf4vvBJVvpOso1PbQH5L3xXZQlXbhdnejfHdWKrt0TwekXTBQsJcWxYPlaKQomwOxozE1avoespg==",
|
||||
"version": "4.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.17.0.tgz",
|
||||
"integrity": "sha512-5uV0u6bVKq5Il2xuEQzMtx15LSNRzxVIXhUQOY+Q3dPe3V2IaE9lSEjAaR+vEp1U7uFgo0lPc+S3A1BdkEMMsA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -4973,9 +4957,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.19.66",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.66.tgz",
|
||||
"integrity": "sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg==",
|
||||
"version": "18.19.67",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.67.tgz",
|
||||
"integrity": "sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5408,14 +5392,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
|
||||
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
@@ -6094,17 +6075,16 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.1"
|
||||
"set-function-length": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -6113,6 +6093,20 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.0.tgz",
|
||||
"integrity": "sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -6133,9 +6127,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001684",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz",
|
||||
"integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==",
|
||||
"version": "1.0.30001687",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz",
|
||||
"integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -6245,9 +6239,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cipher-base": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.5.tgz",
|
||||
"integrity": "sha512-xq7ICKB4TMHUx7Tz1L9O2SGKOhYMOTR32oir45Bq28/AQTpHogKgHcoYFSdRbMtddl+ozNXfXY9jWcgYKmde0w==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
|
||||
"integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
@@ -6876,9 +6870,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7228,6 +7222,21 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz",
|
||||
"integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||
@@ -7250,9 +7259,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.65",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz",
|
||||
"integrity": "sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==",
|
||||
"version": "1.5.71",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz",
|
||||
"integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -7372,14 +7381,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@@ -7624,9 +7630,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7649,7 +7655,7 @@
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
@@ -7664,6 +7670,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
@@ -8044,17 +8054,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz",
|
||||
"integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"dunder-proto": "^1.0.0",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -8174,13 +8187,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -8238,23 +8251,10 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -9432,9 +9432,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/libphonenumber-js": {
|
||||
"version": "1.11.15",
|
||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.15.tgz",
|
||||
"integrity": "sha512-M7+rtYi9l5RvMmHyjyoF3BHHUpXTYdJ0PezZGHNs0GyW1lO+K7jxlXpbdIb7a56h0nqLYdjIw+E+z0ciGaJP7g==",
|
||||
"version": "1.11.16",
|
||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.16.tgz",
|
||||
"integrity": "sha512-Noyazmt0yOvnG0OeRY45Cd1ur8G7Z0HWVkuCuKe+yysGNxPQwBAODBQQ40j0AIagi9ZWurfmmZWNlpg4h4W+XQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
@@ -10586,9 +10586,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -10852,9 +10852,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz",
|
||||
"integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==",
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz",
|
||||
"integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -11694,9 +11694,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -12082,9 +12082,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.0.tgz",
|
||||
"integrity": "sha512-/OXNZcLyWkfo13ofOW5M7SLh+k5pnIs07owXK2teFpnfaOEcycnSy7HQxldaVX1ZP/7Q8oO1eDuQJNwbomQq5Q==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -12829,9 +12829,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz",
|
||||
"integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz",
|
||||
"integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -12845,24 +12845,25 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.27.4",
|
||||
"@rollup/rollup-android-arm64": "4.27.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.27.4",
|
||||
"@rollup/rollup-darwin-x64": "4.27.4",
|
||||
"@rollup/rollup-freebsd-arm64": "4.27.4",
|
||||
"@rollup/rollup-freebsd-x64": "4.27.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.27.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.27.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.27.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.27.4",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.27.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.27.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.27.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.27.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.27.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.27.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.27.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.27.4",
|
||||
"@rollup/rollup-android-arm-eabi": "4.28.1",
|
||||
"@rollup/rollup-android-arm64": "4.28.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.28.1",
|
||||
"@rollup/rollup-darwin-x64": "4.28.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.28.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.28.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.28.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.28.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.28.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.28.1",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.28.1",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.28.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.28.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.28.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.28.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.28.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.28.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.28.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.28.1",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -13365,11 +13366,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shell-quote": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
|
||||
"integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
|
||||
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -13514,13 +13518,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socks-proxy-agent": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
|
||||
"integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
|
||||
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.1",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"socks": "^2.8.3"
|
||||
},
|
||||
@@ -14182,9 +14186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-matches": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.6.1.tgz",
|
||||
"integrity": "sha512-1QXWQUa14MCgbz7vMg7i7eVPhMKB/5w8808nkN2sfnDkbG9nWYr9IwuTxX+h99yyawHYS53DewShA2RYCbSW4Q==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.2.1.tgz",
|
||||
"integrity": "sha512-qdnMgTHsGCEGGK6QiaNMY2vD9eQtRp2Q+pAxcOAzxHJKDKTBYsc1ISTg1zp8H2+EmtCB0eko/1TwYUA5/mUGug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ts-morph": {
|
||||
@@ -15265,18 +15269,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.96.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz",
|
||||
"integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==",
|
||||
"version": "5.97.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@webassemblyjs/ast": "^1.12.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.12.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.12.1",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
"acorn": "^8.14.0",
|
||||
"browserslist": "^4.24.0",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"pbkdf2": "^3.1.2",
|
||||
"rxjs": "^7.5.6",
|
||||
"swiper": "^8.2.4",
|
||||
"ts-matches": "^5.5.1",
|
||||
"ts-matches": "^6.1.0",
|
||||
"tslib": "^2.8.1",
|
||||
"uuid": "^8.3.2",
|
||||
"zone.js": "^0.14.2"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<tui-root>
|
||||
<main>
|
||||
<img class="logo" src="assets/img/icon.png" alt="Start9" />
|
||||
<section tuiCardLarge tuiSurface="elevated" class="card">
|
||||
<section tuiCardLarge tuiSurface="floating" class="card">
|
||||
<header class="header">
|
||||
@if (selected) {
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
size="m"
|
||||
class="back"
|
||||
iconStart="@tui.chevron-left"
|
||||
|
||||
@@ -3,10 +3,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { Exver, MarkdownPipeModule } from '@start9labs/shared'
|
||||
import { TuiButton, TuiDialogContext, TuiLoader } from '@taiga-ui/core'
|
||||
import { TuiAccordion } from '@taiga-ui/kit'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { MarketplacePkg } from '../../src/types'
|
||||
|
||||
@Component({
|
||||
@@ -35,7 +32,7 @@ import { MarketplacePkg } from '../../src/types'
|
||||
export class ReleaseNotesComponent {
|
||||
private readonly exver = inject(Exver)
|
||||
private readonly pkg =
|
||||
inject<TuiDialogContext<void, MarketplacePkg>>(POLYMORPHEUS_CONTEXT).data
|
||||
injectContext<TuiDialogContext<void, MarketplacePkg>>().data
|
||||
|
||||
readonly notes = Object.entries(this.pkg.otherVersions)
|
||||
.filter(
|
||||
|
||||
@@ -22,7 +22,7 @@ import { PolymorpheusContent } from '@taiga-ui/polymorpheus'
|
||||
>
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
iconStart="@tui.chevron-left"
|
||||
title="Previous"
|
||||
type="button"
|
||||
@@ -60,7 +60,7 @@ import { PolymorpheusContent } from '@taiga-ui/polymorpheus'
|
||||
</tui-carousel>
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
type="button"
|
||||
iconStart="@tui.chevron-right"
|
||||
title="Next"
|
||||
|
||||
@@ -17,10 +17,7 @@ import {
|
||||
} from '@taiga-ui/core'
|
||||
import { TUI_VALIDATION_ERRORS, TuiFieldErrorPipe } from '@taiga-ui/kit'
|
||||
import { TuiInputModule, TuiInputPasswordModule } from '@taiga-ui/legacy'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { SERVERS, ServersResponse } from 'src/app/components/servers.component'
|
||||
import { ApiService } from 'src/app/services/api.service'
|
||||
|
||||
@@ -108,8 +105,7 @@ export class CifsComponent {
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly context =
|
||||
inject<TuiDialogContext<CifsResponse>>(POLYMORPHEUS_CONTEXT)
|
||||
private readonly context = injectContext<TuiDialogContext<CifsResponse>>()
|
||||
|
||||
readonly form = new FormGroup({
|
||||
hostname: new FormControl('', {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { TuiInputPasswordModule } from '@taiga-ui/legacy'
|
||||
import { Component, inject } from '@angular/core'
|
||||
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import * as argon2 from '@start9labs/argon2'
|
||||
import { ErrorService } from '@start9labs/shared'
|
||||
import { TuiDialogContext, TuiError, TuiButton } from '@taiga-ui/core'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { TuiButton, TuiDialogContext, TuiError } from '@taiga-ui/core'
|
||||
import { TuiInputPasswordModule } from '@taiga-ui/legacy'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
|
||||
interface DialogData {
|
||||
passwordHash?: string
|
||||
@@ -67,7 +64,7 @@ interface DialogData {
|
||||
export class PasswordComponent {
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly context =
|
||||
inject<TuiDialogContext<string, DialogData>>(POLYMORPHEUS_CONTEXT)
|
||||
injectContext<TuiDialogContext<string, DialogData>>()
|
||||
|
||||
readonly storageDrive = this.context.data.storageDrive
|
||||
readonly password = new FormControl('', { nonNullable: true })
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Component, inject } from '@angular/core'
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerComponent } from '@start9labs/shared'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { PasswordDirective } from 'src/app/components/password.directive'
|
||||
import { StartOSDiskInfoWithId } from 'src/app/services/api.service'
|
||||
|
||||
@@ -27,8 +24,7 @@ export interface ServersResponse {
|
||||
imports: [ServerComponent, PasswordDirective],
|
||||
})
|
||||
export class ServersComponent {
|
||||
readonly context =
|
||||
inject<TuiDialogContext<ServersResponse, Data>>(POLYMORPHEUS_CONTEXT)
|
||||
readonly context = injectContext<TuiDialogContext<ServersResponse, Data>>()
|
||||
|
||||
select(password: string, serverId: string) {
|
||||
this.context.completeWith({ serverId, password })
|
||||
|
||||
@@ -18,7 +18,7 @@ import { StateService } from 'src/app/services/state.service'
|
||||
@if (recover) {
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
class="back"
|
||||
iconStart="@tui.chevron-left"
|
||||
(click)="recover = false"
|
||||
|
||||
@@ -40,7 +40,7 @@ import { StateService } from 'src/app/services/state.service'
|
||||
<h3>You can now safely unplug your old StartOS data drive</h3>
|
||||
}
|
||||
|
||||
<button tuiCardLarge tuiSurface="elevated" (click)="download()">
|
||||
<button tuiCardLarge tuiSurface="floating" (click)="download()">
|
||||
<strong class="caps">Download address info</strong>
|
||||
<span>
|
||||
start.local was for setup purposes only. It will no longer work.
|
||||
@@ -53,7 +53,7 @@ import { StateService } from 'src/app/services/state.service'
|
||||
|
||||
<a
|
||||
tuiCardLarge
|
||||
tuiSurface="elevated"
|
||||
tuiSurface="floating"
|
||||
target="_blank"
|
||||
[attr.href]="disableLogin ? null : lanAddress"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { TuiLoader } from '@taiga-ui/core'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusContent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusContent } from '@taiga-ui/polymorpheus'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@@ -13,6 +10,5 @@ import {
|
||||
imports: [TuiLoader],
|
||||
})
|
||||
export class LoadingComponent {
|
||||
readonly content: PolymorpheusContent =
|
||||
inject(POLYMORPHEUS_CONTEXT)['content']
|
||||
readonly content = injectContext<{ content: PolymorpheusContent }>().content
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Component, Inject } from '@angular/core'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import {
|
||||
catchError,
|
||||
ignoreElements,
|
||||
@@ -42,3 +45,5 @@ export class MarkdownComponent {
|
||||
return this.context.label || ''
|
||||
}
|
||||
}
|
||||
|
||||
export const MARKDOWN = new PolymorpheusComponent(MarkdownComponent)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { inject } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
|
||||
export function getPkgId({ snapshot }: ActivatedRoute): string {
|
||||
const pkgId = snapshot.paramMap.get('pkgId')
|
||||
export function getPkgId(): string {
|
||||
const pkgId = inject(ActivatedRoute).snapshot.paramMap.get('pkgId')
|
||||
|
||||
if (!pkgId) {
|
||||
throw new Error('pkgId is missing from route params')
|
||||
|
||||
@@ -32,11 +32,6 @@
|
||||
color: var(--tui-status-negative);
|
||||
}
|
||||
|
||||
[tuiAppearance][data-appearance='flat'],
|
||||
[tuiAppearance][data-appearance='outline'] {
|
||||
color: var(--tui-text-primary);
|
||||
}
|
||||
|
||||
[tuiAppearance][data-appearance='primary'] {
|
||||
@include appearance-disabled {
|
||||
background: #eaecee;
|
||||
|
||||
@@ -36,14 +36,14 @@ import { BackupReport } from 'src/app/services/api/api.types'
|
||||
})
|
||||
export class BackupsReportModal {
|
||||
private readonly context =
|
||||
inject<TuiDialogContext<void, { report: BackupReport; timestamp: string }>>(
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
)
|
||||
inject<
|
||||
TuiDialogContext<void, { content: BackupReport; timestamp: string }>
|
||||
>(POLYMORPHEUS_CONTEXT)
|
||||
|
||||
readonly system = this.getSystem()
|
||||
|
||||
get report(): BackupReport {
|
||||
return this.context.data.report
|
||||
return this.context.data.content
|
||||
}
|
||||
|
||||
get timestamp(): string {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { NgIf } from '@angular/common'
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { TuiIconModule, TuiTitleModule } from '@taiga-ui/experimental'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
|
||||
import { StoreIconComponent } from './store-icon.component'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: '[registry]',
|
||||
template: `
|
||||
<store-icon
|
||||
[url]="registry.url"
|
||||
[marketplace]="marketplace"
|
||||
size="40px"
|
||||
></store-icon>
|
||||
<div tuiTitle>
|
||||
{{ registry.name }}
|
||||
<div tuiSubtitle>{{ registry.url }}</div>
|
||||
</div>
|
||||
<tui-icon
|
||||
*ngIf="registry.selected; else content"
|
||||
icon="@tui.check"
|
||||
[style.color]="'var(--tui-positive)'"
|
||||
/>
|
||||
<ng-template #content><ng-content></ng-content></ng-template>
|
||||
`,
|
||||
styles: [':host { border-radius: 0.25rem; width: stretch; }'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [NgIf, StoreIconComponent, TuiIconModule, TuiTitleModule],
|
||||
})
|
||||
export class MarketplaceRegistryComponent {
|
||||
readonly marketplace = inject(ConfigService).marketplace
|
||||
|
||||
@Input()
|
||||
registry!: { url: string; selected: boolean; name?: string }
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { NgIf } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { sameUrl } from '@start9labs/shared'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'store-icon',
|
||||
template: `
|
||||
<img
|
||||
*ngIf="icon; else noIcon"
|
||||
[style.border-radius.%]="100"
|
||||
[style.max-width]="size || '100%'"
|
||||
[src]="icon"
|
||||
alt="Marketplace Icon"
|
||||
/>
|
||||
<ng-template #noIcon>
|
||||
<img
|
||||
[style.max-width]="size || '100%'"
|
||||
[style.border-radius]="0"
|
||||
src="assets/img/storefront-outline.png"
|
||||
alt="Marketplace Icon"
|
||||
/>
|
||||
</ng-template>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [NgIf],
|
||||
})
|
||||
export class StoreIconComponent {
|
||||
@Input()
|
||||
url = ''
|
||||
@Input()
|
||||
size?: string
|
||||
@Input()
|
||||
marketplace!: any
|
||||
|
||||
get icon() {
|
||||
const { start9, community } = this.marketplace
|
||||
|
||||
if (sameUrl(this.url, start9)) {
|
||||
return 'assets/img/icon_transparent.png'
|
||||
} else if (sameUrl(this.url, community)) {
|
||||
return 'assets/img/community-store.png'
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<ng-container *ngIf="actionRequests.critical.length">
|
||||
<ion-item-divider>Required Actions</ion-item-divider>
|
||||
<ion-item
|
||||
*ngFor="let request of actionRequests.critical"
|
||||
button
|
||||
(click)="handleAction(request)"
|
||||
>
|
||||
<ion-icon slot="start" name="warning-outline" color="warning"></ion-icon>
|
||||
<ion-label>
|
||||
<h2 class="highlighted">{{ request.actionName }}</h2>
|
||||
<p *ngIf="request.dependency" class="dependency">
|
||||
<span class="light">Service:</span>
|
||||
<img [src]="request.dependency.icon" alt="" />
|
||||
{{ request.dependency.title }}
|
||||
</p>
|
||||
<p>
|
||||
<span class="light">Reason:</span>
|
||||
{{ request.reason || 'no reason provided' }}
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="actionRequests.important.length">
|
||||
<ion-item-divider>Requested Actions</ion-item-divider>
|
||||
<ion-item
|
||||
*ngFor="let request of actionRequests.important"
|
||||
button
|
||||
(click)="handleAction(request)"
|
||||
>
|
||||
<ion-icon slot="start" name="play-outline" color="warning"></ion-icon>
|
||||
<ion-label>
|
||||
<h2 class="highlighted">{{ request.actionName }}</h2>
|
||||
<p *ngIf="request.dependency" class="dependency">
|
||||
<span class="light">Service:</span>
|
||||
<img [src]="request.dependency.icon" alt="" />
|
||||
{{ request.dependency.title }}
|
||||
</p>
|
||||
<p>
|
||||
<span class="light">Reason:</span>
|
||||
{{ request.reason || 'no reason provided' }}
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
@@ -1,16 +0,0 @@
|
||||
.light {
|
||||
color: var(--ion-color-dark);
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
color: var(--ion-color-dark);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dependency {
|
||||
display: inline-flex;
|
||||
img {
|
||||
max-width: 16px;
|
||||
margin: 0 2px 0 5px;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { ActionService } from 'src/app/services/action.service'
|
||||
import { getDepDetails } from 'src/app/util/dep-info'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-action-requests',
|
||||
templateUrl: './app-show-action-requests.component.html',
|
||||
styleUrls: ['./app-show-action-requests.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppShowActionRequestsComponent {
|
||||
@Input()
|
||||
allPkgs!: Record<string, T.PackageDataEntry>
|
||||
|
||||
@Input()
|
||||
pkg!: T.PackageDataEntry
|
||||
|
||||
@Input()
|
||||
manifest!: T.Manifest
|
||||
|
||||
get actionRequests() {
|
||||
const critical: (T.ActionRequest & {
|
||||
actionName: string
|
||||
dependency: {
|
||||
title: string
|
||||
icon: string
|
||||
} | null
|
||||
})[] = []
|
||||
const important: (T.ActionRequest & {
|
||||
actionName: string
|
||||
dependency: {
|
||||
title: string
|
||||
icon: string
|
||||
} | null
|
||||
})[] = []
|
||||
|
||||
Object.values(this.pkg.requestedActions)
|
||||
.filter(r => r.active)
|
||||
.forEach(r => {
|
||||
const self = r.request.packageId === this.manifest.id
|
||||
const toReturn = {
|
||||
...r.request,
|
||||
actionName: self
|
||||
? this.pkg.actions[r.request.actionId].name
|
||||
: this.allPkgs[r.request.packageId]?.actions[r.request.actionId]
|
||||
.name || 'Unknown Action',
|
||||
dependency: self
|
||||
? null
|
||||
: getDepDetails(this.pkg, this.allPkgs, r.request.packageId),
|
||||
}
|
||||
|
||||
if (r.request.severity === 'critical') {
|
||||
critical.push(toReturn)
|
||||
} else {
|
||||
important.push(toReturn)
|
||||
}
|
||||
})
|
||||
|
||||
return { critical, important }
|
||||
}
|
||||
|
||||
constructor(private readonly actionService: ActionService) {}
|
||||
|
||||
async handleAction(request: T.ActionRequest) {
|
||||
const self = request.packageId === this.manifest.id
|
||||
this.actionService.present({
|
||||
pkgInfo: {
|
||||
id: request.packageId,
|
||||
title: self
|
||||
? this.manifest.title
|
||||
: getDepDetails(this.pkg, this.allPkgs, request.packageId).title,
|
||||
mainStatus: self
|
||||
? this.pkg.status.main
|
||||
: this.allPkgs[request.packageId].status.main,
|
||||
icon: self
|
||||
? this.pkg.icon
|
||||
: getDepDetails(this.pkg, this.allPkgs, request.packageId).icon,
|
||||
},
|
||||
actionInfo: {
|
||||
id: request.actionId,
|
||||
metadata:
|
||||
request.packageId === this.manifest.id
|
||||
? this.pkg.actions[request.actionId]
|
||||
: this.allPkgs[request.packageId].actions[request.actionId],
|
||||
},
|
||||
requestInfo: {
|
||||
request,
|
||||
dependentId:
|
||||
request.packageId === this.manifest.id ? undefined : this.manifest.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<ion-item-divider>Message</ion-item-divider>
|
||||
<div class="code-block ion-margin">
|
||||
<code>
|
||||
<ion-text color="warning">{{ error.message }}</ion-text>
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<ion-item-divider>Actions</ion-item-divider>
|
||||
<div class="ion-margin">
|
||||
<p>
|
||||
<b>Rebuild Container</b>
|
||||
is harmless action that and only takes a few seconds to complete. It will
|
||||
likely resolve this issue.
|
||||
<b>Uninstall Service</b>
|
||||
is a dangerous action that will remove the service from StartOS and wipe all
|
||||
its data.
|
||||
</p>
|
||||
<ion-button class="ion-margin-end" (click)="rebuild()">
|
||||
Rebuild Container
|
||||
</ion-button>
|
||||
<ion-button (click)="tryUninstall()" color="danger">
|
||||
Uninstall Service
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="error.debug">
|
||||
<ion-item-divider>Full Stack Trace</ion-item-divider>
|
||||
<div class="code-block ion-margin">
|
||||
<code>{{ error.message }}</code>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -1,45 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { ToastController } from '@ionic/angular'
|
||||
import { copyToClipboard } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { StandardActionsService } from 'src/app/services/standard-actions.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-error',
|
||||
templateUrl: 'app-show-error.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppShowErrorComponent {
|
||||
@Input()
|
||||
manifest!: T.Manifest
|
||||
|
||||
@Input()
|
||||
error!: T.MainStatus & { main: 'error' }
|
||||
|
||||
constructor(
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly standardActionsService: StandardActionsService,
|
||||
) {}
|
||||
|
||||
async copy(text: string): Promise<void> {
|
||||
const success = await copyToClipboard(text)
|
||||
const message = success
|
||||
? 'Copied to clipboard!'
|
||||
: 'Failed to copy to clipboard.'
|
||||
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: message,
|
||||
position: 'bottom',
|
||||
duration: 1000,
|
||||
})
|
||||
await toast.present()
|
||||
}
|
||||
|
||||
async rebuild() {
|
||||
return this.standardActionsService.rebuild(this.manifest.id)
|
||||
}
|
||||
|
||||
async tryUninstall() {
|
||||
return this.standardActionsService.tryUninstall(this.manifest)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<div
|
||||
*ngIf="!caTrusted; else trusted"
|
||||
tuiCardLarge
|
||||
tuiSurface="elevated"
|
||||
tuiSurface="floating"
|
||||
class="card"
|
||||
>
|
||||
<tui-icon icon="@tui.lock" [style.font-size.rem]="4" />
|
||||
@@ -72,7 +72,7 @@
|
||||
<button
|
||||
tuiButton
|
||||
size="s"
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
iconEnd="@tui.external-link"
|
||||
(click)="launchHttps()"
|
||||
[disabled]="caTrusted"
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
|
||||
<ng-template #trusted>
|
||||
<div tuiCardLarge tuiSurface="elevated" class="card">
|
||||
<div tuiCardLarge tuiSurface="floating" class="card">
|
||||
<tui-icon
|
||||
icon="@tui.shield"
|
||||
tuiAppearance="icon-success"
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface FormContext<T> {
|
||||
<button
|
||||
*ngIf="button.handler; else link"
|
||||
tuiButton
|
||||
[appearance]="last ? 'primary' : 'flat'"
|
||||
[appearance]="last ? 'primary' : 'flat-grayscale'"
|
||||
[type]="last ? 'submit' : 'button'"
|
||||
(click)="onClick(button.handler)"
|
||||
>
|
||||
@@ -60,7 +60,7 @@ export interface FormContext<T> {
|
||||
<ng-template #link>
|
||||
<a
|
||||
tuiButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
[routerLink]="button.link"
|
||||
(click)="close()"
|
||||
>
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
[(ngModel)]="value"
|
||||
(click.stop)="(0)"
|
||||
/>
|
||||
<tui-icon icon="@tui.paint" tuiAppearance="icon" class="icon" />
|
||||
<tui-icon icon="@tui.paint-bucket" tuiAppearance="icon" class="icon" />
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
position: absolute;
|
||||
height: 0.3rem;
|
||||
width: 1.4rem;
|
||||
bottom: 0.125rem;
|
||||
bottom: -0.25rem;
|
||||
background: currentColor;
|
||||
border-radius: 0.125rem;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<button
|
||||
tuiButton
|
||||
type="button"
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
size="s"
|
||||
(click)="completeWith(false)"
|
||||
>
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
OnDestroy,
|
||||
} from '@angular/core'
|
||||
import { pauseFor } from '@start9labs/shared'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
|
||||
import { TuiDialogContext, TuiButton } from '@taiga-ui/core'
|
||||
import { TuiButton, TuiDialogContext } from '@taiga-ui/core'
|
||||
import { injectContext } from '@taiga-ui/polymorpheus'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@@ -44,8 +44,7 @@ import { TuiDialogContext, TuiButton } from '@taiga-ui/core'
|
||||
})
|
||||
export class HeaderSnekComponent implements AfterViewInit, OnDestroy {
|
||||
private readonly document = inject(DOCUMENT)
|
||||
private readonly dialog =
|
||||
inject<TuiDialogContext<number, number>>(POLYMORPHEUS_CONTEXT)
|
||||
private readonly dialog = injectContext<TuiDialogContext<number, number>>()
|
||||
|
||||
highScore: number = this.dialog.data
|
||||
score = 0
|
||||
|
||||
@@ -25,7 +25,7 @@ import { AddressDetails } from './interface.utils'
|
||||
*ngIf="network$ | async as network"
|
||||
clearnetAddresses
|
||||
tuiCardLarge="compact"
|
||||
tuiSurface="elevated"
|
||||
tuiSurface="floating"
|
||||
[network]="network"
|
||||
[addresses]="serviceInterface.addresses.clearnet"
|
||||
>
|
||||
@@ -46,7 +46,7 @@ import { AddressDetails } from './interface.utils'
|
||||
<app-address-group
|
||||
torAddresses
|
||||
tuiCardLarge="compact"
|
||||
tuiSurface="elevated"
|
||||
tuiSurface="floating"
|
||||
[addresses]="serviceInterface.addresses.tor"
|
||||
>
|
||||
<em>
|
||||
@@ -66,7 +66,7 @@ import { AddressDetails } from './interface.utils'
|
||||
<app-address-group
|
||||
localAddresses
|
||||
tuiCardLarge="compact"
|
||||
tuiSurface="elevated"
|
||||
tuiSurface="floating"
|
||||
[addresses]="serviceInterface.addresses.local"
|
||||
>
|
||||
<em>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<footer class="footer">
|
||||
<button
|
||||
tuiButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
iconStart="@tui.circle-arrow-down"
|
||||
(click)="setScroll(true); scrollToBottom()"
|
||||
>
|
||||
@@ -57,7 +57,7 @@
|
||||
</button>
|
||||
<button
|
||||
tuiButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
iconStart="@tui.download"
|
||||
[logsDownload]="fetchLogs"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
|
||||
import { injectContext } from '@taiga-ui/polymorpheus'
|
||||
import { QrCodeModule } from 'ng-qrcode'
|
||||
|
||||
@Component({
|
||||
@@ -11,6 +11,5 @@ import { QrCodeModule } from 'ng-qrcode'
|
||||
imports: [QrCodeModule],
|
||||
})
|
||||
export class QRModal {
|
||||
readonly context =
|
||||
inject<TuiDialogContext<void, string>>(POLYMORPHEUS_CONTEXT)
|
||||
readonly context = injectContext<TuiDialogContext<void, string>>()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TuiLet } from '@taiga-ui/cdk'
|
||||
import { TuiButton, tuiButtonOptionsProvider } from '@taiga-ui/core'
|
||||
import { map } from 'rxjs'
|
||||
import { UILaunchComponent } from 'src/app/routes/portal/routes/dashboard/ui.component'
|
||||
import { ActionsService } from 'src/app/services/actions.service'
|
||||
import { ControlsService } from 'src/app/services/controls.service'
|
||||
import { DepErrorService } from 'src/app/services/dep-error.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
@@ -26,7 +26,7 @@ const RUNNING = ['running', 'starting', 'restarting']
|
||||
<button
|
||||
tuiIconButton
|
||||
iconStart="@tui.square"
|
||||
(click)="actions.stop(manifest())"
|
||||
(click)="controls.stop(manifest())"
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
@@ -35,7 +35,7 @@ const RUNNING = ['running', 'starting', 'restarting']
|
||||
tuiIconButton
|
||||
iconStart="@tui.rotate-cw"
|
||||
[disabled]="status().primary !== 'running'"
|
||||
(click)="actions.restart(manifest())"
|
||||
(click)="controls.restart(manifest())"
|
||||
>
|
||||
Restart
|
||||
</button>
|
||||
@@ -45,18 +45,10 @@ const RUNNING = ['running', 'starting', 'restarting']
|
||||
tuiIconButton
|
||||
iconStart="@tui.play"
|
||||
[disabled]="status().primary !== 'stopped'"
|
||||
(click)="actions.start(manifest(), !!hasUnmet)"
|
||||
(click)="controls.start(manifest(), !!hasUnmet)"
|
||||
>
|
||||
Start
|
||||
</button>
|
||||
|
||||
<button
|
||||
tuiIconButton
|
||||
iconStart="@tui.wrench"
|
||||
(click)="actions.configure(manifest())"
|
||||
>
|
||||
Configure
|
||||
</button>
|
||||
}
|
||||
|
||||
<app-ui-launch [pkg]="pkg()" />
|
||||
@@ -80,10 +72,9 @@ const RUNNING = ['running', 'starting', 'restarting']
|
||||
})
|
||||
export class ControlsComponent {
|
||||
private readonly errors = inject(DepErrorService)
|
||||
readonly actions = inject(ActionsService)
|
||||
|
||||
readonly controls = inject(ControlsService)
|
||||
readonly pkg = input.required<PackageDataEntry>()
|
||||
|
||||
readonly status = computed(() => renderPkgStatus(this.pkg()))
|
||||
readonly running = computed(() => RUNNING.includes(this.status().primary))
|
||||
readonly manifest = computed(() => getManifest(this.pkg()))
|
||||
|
||||
@@ -21,7 +21,7 @@ import { DepErrorService } from 'src/app/services/dep-error.service'
|
||||
<th>Name</th>
|
||||
<th>Version</th>
|
||||
<th [style.width.rem]="13">Status</th>
|
||||
<th [style.width.rem]="8" [style.text-indent.rem]="1">Controls</th>
|
||||
<th [style.width.rem]="8" [style.text-indent.rem]="1.5">Controls</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -58,14 +58,7 @@ export class StatusComponent {
|
||||
hasDepErrors = false
|
||||
|
||||
get healthy(): boolean {
|
||||
const status = this.getStatus(this.pkg)
|
||||
|
||||
return (
|
||||
!this.hasDepErrors && // no deps error
|
||||
// @TODO Matt how do we handle this now?
|
||||
// !!this.pkg.status.configured && // no config needed
|
||||
status.health !== 'failure' // no health issues
|
||||
)
|
||||
return !this.hasDepErrors && this.getStatus(this.pkg).health !== 'failure'
|
||||
}
|
||||
|
||||
get loading(): boolean {
|
||||
@@ -87,9 +80,8 @@ export class StatusComponent {
|
||||
return 'Running'
|
||||
case 'stopped':
|
||||
return 'Stopped'
|
||||
// @TODO Matt just dropping this?
|
||||
// case 'needsConfig':
|
||||
// return 'Needs Config'
|
||||
case 'actionRequired':
|
||||
return 'Action Required'
|
||||
case 'updating':
|
||||
return 'Updating...'
|
||||
case 'stopping':
|
||||
@@ -113,9 +105,8 @@ export class StatusComponent {
|
||||
switch (this.getStatus(this.pkg).primary) {
|
||||
case 'running':
|
||||
return 'var(--tui-status-positive)'
|
||||
// @TODO Matt just dropping this?
|
||||
// case 'needsConfig':
|
||||
// return 'var(--tui-status-warning)'
|
||||
case 'actionRequired':
|
||||
return 'var(--tui-status-warning)'
|
||||
case 'installing':
|
||||
case 'updating':
|
||||
case 'stopping':
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
HostListener,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiIcon, TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { ActionService } from 'src/app/services/action.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { getDepDetails } from 'src/app/utils/dep-info'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
|
||||
export type ActionRequest = T.ActionRequest & {
|
||||
actionName: string
|
||||
dependency: {
|
||||
title: string
|
||||
icon: string
|
||||
} | null
|
||||
}
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'button[actionRequest]',
|
||||
template: `
|
||||
<tui-icon class="g-warning" [icon]="icon" />
|
||||
<span tuiTitle>
|
||||
<strong>{{ actionRequest.actionName }}</strong>
|
||||
@if (actionRequest.dependency) {
|
||||
<span tuiSubtitle>
|
||||
<strong>Service:</strong>
|
||||
<img
|
||||
alt=""
|
||||
[src]="actionRequest.dependency.icon"
|
||||
[style.width.rem]="1"
|
||||
/>
|
||||
{{ actionRequest.dependency.title }}
|
||||
</span>
|
||||
}
|
||||
<span tuiSubtitle>
|
||||
{{ actionRequest.reason || 'no reason provided' }}
|
||||
</span>
|
||||
</span>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TuiIcon, TuiTitle],
|
||||
hostDirectives: [TuiCell],
|
||||
})
|
||||
export class ServiceActionRequestComponent {
|
||||
private readonly actionService = inject(ActionService)
|
||||
|
||||
@Input({ required: true })
|
||||
actionRequest!: ActionRequest
|
||||
|
||||
@Input({ required: true })
|
||||
pkg!: PackageDataEntry
|
||||
|
||||
@Input({ required: true })
|
||||
allPkgs!: Record<string, PackageDataEntry>
|
||||
|
||||
get icon(): string {
|
||||
return this.actionRequest.severity === 'critical'
|
||||
? '@tui.triangle-alert'
|
||||
: '@tui.play'
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
async handleAction() {
|
||||
const { id, title } = getManifest(this.pkg)
|
||||
const { actionId, packageId } = this.actionRequest
|
||||
const details = getDepDetails(this.pkg, this.allPkgs, packageId)
|
||||
const self = packageId === id
|
||||
|
||||
this.actionService.present({
|
||||
pkgInfo: {
|
||||
id: packageId,
|
||||
title: self ? title : details.title,
|
||||
mainStatus: self
|
||||
? this.pkg.status.main
|
||||
: this.allPkgs[packageId].status.main,
|
||||
icon: self ? this.pkg.icon : details.icon,
|
||||
},
|
||||
actionInfo: {
|
||||
id: actionId,
|
||||
metadata: self
|
||||
? this.pkg.actions[actionId]
|
||||
: this.allPkgs[packageId].actions[actionId],
|
||||
},
|
||||
requestInfo: {
|
||||
request: this.actionRequest,
|
||||
dependentId: self ? undefined : id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,41 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { TuiIcon } from '@taiga-ui/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiIcon, TuiTitle } from '@taiga-ui/core'
|
||||
|
||||
interface ActionItem {
|
||||
readonly icon: string
|
||||
readonly name: string
|
||||
readonly description: string
|
||||
readonly icon?: string
|
||||
readonly visibility?: T.ActionVisibility
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[action]',
|
||||
template: `
|
||||
<tui-icon [icon]="action.icon" />
|
||||
<div>
|
||||
<tui-icon [icon]="action.icon || '@tui.circle-play'" />
|
||||
<div tuiTitle>
|
||||
<strong>{{ action.name }}</strong>
|
||||
<div>{{ action.description }}</div>
|
||||
<div tuiSubtitle>{{ action.description }}</div>
|
||||
@if (disabled) {
|
||||
<div tuiSubtitle class="g-warning">{{ disabled }}</div>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [TuiIcon],
|
||||
imports: [TuiIcon, TuiTitle],
|
||||
host: {
|
||||
'[disabled]': '!!disabled',
|
||||
},
|
||||
})
|
||||
export class ServiceActionComponent {
|
||||
@Input({ required: true })
|
||||
action!: ActionItem
|
||||
|
||||
get disabled() {
|
||||
return (
|
||||
typeof this.action.visibility === 'object' &&
|
||||
this.action.visibility.disabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { T } from '@start9labs/start-sdk'
|
||||
import { tuiPure } from '@taiga-ui/cdk'
|
||||
import { tuiButtonOptionsProvider } from '@taiga-ui/core'
|
||||
import { DependencyInfo } from 'src/app/routes/portal/routes/service/types/dependency-info'
|
||||
import { ActionsService } from 'src/app/services/actions.service'
|
||||
import { ControlsService } from '../../../../../services/controls.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { PackageStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
@@ -49,17 +49,6 @@ const STOPPABLE = ['running', 'starting', 'restarting']
|
||||
Start
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (canConfigure) {
|
||||
<button
|
||||
tuiButton
|
||||
appearance="secondary-warning"
|
||||
iconStart="@tui.wrench"
|
||||
(click)="actions.configure(manifest)"
|
||||
>
|
||||
Configure
|
||||
</button>
|
||||
}
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
@@ -84,7 +73,7 @@ export class ServiceActionsComponent {
|
||||
status: PackageStatus
|
||||
}
|
||||
|
||||
readonly actions = inject(ActionsService)
|
||||
readonly actions = inject(ControlsService)
|
||||
|
||||
get manifest(): T.Manifest {
|
||||
return getManifest(this.service.pkg)
|
||||
@@ -95,19 +84,13 @@ export class ServiceActionsComponent {
|
||||
}
|
||||
|
||||
get canStart(): boolean {
|
||||
return this.service.status.primary === 'stopped' && !this.canConfigure
|
||||
return this.service.status.primary === 'stopped'
|
||||
}
|
||||
|
||||
get canRestart(): boolean {
|
||||
return this.service.status.primary === 'running'
|
||||
}
|
||||
|
||||
get canConfigure(): boolean {
|
||||
// @TODO Matt should we just drop this?
|
||||
// return !this.service.pkg.status.configured
|
||||
return false
|
||||
}
|
||||
|
||||
@tuiPure
|
||||
hasUnmet(dependencies: readonly DependencyInfo[]): boolean {
|
||||
return dependencies.some(dep => !!dep.errorText)
|
||||
|
||||
@@ -6,14 +6,8 @@ import { ServiceDependencyComponent } from './dependency.component'
|
||||
selector: 'service-dependencies',
|
||||
template: `
|
||||
@for (dep of dependencies; track $index) {
|
||||
<button
|
||||
class="g-action"
|
||||
[serviceDependency]="dep"
|
||||
(click)="dep.action()"
|
||||
></button>
|
||||
}
|
||||
|
||||
@if (!dependencies.length) {
|
||||
<button [serviceDependency]="dep"></button>
|
||||
} @empty {
|
||||
No dependencies
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { TuiIcon } from '@taiga-ui/core'
|
||||
import { TuiIcon, TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { DependencyInfo } from '../types/dependency-info'
|
||||
|
||||
@Component({
|
||||
selector: '[serviceDependency]',
|
||||
template: `
|
||||
<img [src]="dep.icon" alt="" />
|
||||
<span [style.flex]="1">
|
||||
<span tuiTitle>
|
||||
<strong>
|
||||
@if (dep.errorText) {
|
||||
<tui-icon icon="@tui.triangle-alert" [style.color]="color" />
|
||||
}
|
||||
{{ dep.title }}
|
||||
</strong>
|
||||
<div>{{ dep.version }}</div>
|
||||
<div [style.color]="color">{{ dep.errorText || 'Satisfied' }}</div>
|
||||
<span tuiSubtitle>{{ dep.version }}</span>
|
||||
<span tuiSubtitle="" [style.color]="color">
|
||||
{{ dep.errorText || 'Satisfied' }}
|
||||
</span>
|
||||
</span>
|
||||
@if (dep.actionText) {
|
||||
<div>
|
||||
<span>
|
||||
{{ dep.actionText }}
|
||||
<tui-icon icon="@tui.arrow-right" />
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
`,
|
||||
styles: [
|
||||
@@ -38,7 +41,11 @@ import { DependencyInfo } from '../types/dependency-info'
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [TuiIcon],
|
||||
host: {
|
||||
'(click)': 'dep.action()',
|
||||
},
|
||||
imports: [TuiIcon, TuiTitle],
|
||||
hostDirectives: [TuiCell],
|
||||
})
|
||||
export class ServiceDependencyComponent {
|
||||
@Input({ required: true, alias: 'serviceDependency' })
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { copyToClipboard } from '@start9labs/shared'
|
||||
import {
|
||||
TuiAlertService,
|
||||
TuiButton,
|
||||
TuiDialogService,
|
||||
TuiIcon,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiLineClamp, TuiTooltip } from '@taiga-ui/kit'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { StandardActionsService } from 'src/app/services/standard-actions.service'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'service-error',
|
||||
template: `
|
||||
<tui-line-clamp
|
||||
style="pointer-events: none; margin: 1rem 0 -1rem; color: var(--tui-status-negative);"
|
||||
[linesLimit]="2"
|
||||
[content]="error?.message"
|
||||
(overflownChange)="overflow = $event"
|
||||
/>
|
||||
<h4 class="g-title">
|
||||
<span [style.display]="'flex'">
|
||||
Actions
|
||||
<tui-icon [style.margin-left.rem]="0.25" [tuiTooltip]="hint" />
|
||||
</span>
|
||||
</h4>
|
||||
<ng-template #hint>
|
||||
<div>
|
||||
<b>Rebuild Container</b>
|
||||
is harmless action that and only takes a few seconds to complete. It
|
||||
will likely resolve this issue.
|
||||
</div>
|
||||
<b>Uninstall Service</b>
|
||||
is a dangerous action that will remove the service from StartOS and wipe
|
||||
all its data.
|
||||
</ng-template>
|
||||
<p style="display: flex; flex-wrap: wrap; gap: 1rem">
|
||||
<button tuiButton (click)="rebuild()">Rebuild Container</button>
|
||||
<button tuiButton appearance="negative" (click)="uninstall()">
|
||||
Uninstall Service
|
||||
</button>
|
||||
@if (overflow) {
|
||||
<button tuiButton appearance="secondary-grayscale" (click)="show()">
|
||||
View full message
|
||||
</button>
|
||||
}
|
||||
</p>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TuiButton, TuiIcon, TuiTooltip, TuiLineClamp],
|
||||
})
|
||||
export class ServiceErrorComponent {
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly alerts = inject(TuiAlertService)
|
||||
private readonly service = inject(StandardActionsService)
|
||||
|
||||
@Input({ required: true })
|
||||
pkg!: PackageDataEntry
|
||||
|
||||
overflow = false
|
||||
|
||||
get error() {
|
||||
return this.pkg.status.main === 'error' ? this.pkg.status : null
|
||||
}
|
||||
|
||||
async copy(text: string): Promise<void> {
|
||||
const success = await copyToClipboard(text)
|
||||
|
||||
this.alerts
|
||||
.open(success ? 'Copied to clipboard!' : 'Failed to copy to clipboard.', {
|
||||
appearance: success ? 'positive' : 'negative',
|
||||
})
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
rebuild() {
|
||||
this.service.rebuild(getManifest(this.pkg).id)
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
this.service.uninstall(getManifest(this.pkg))
|
||||
}
|
||||
|
||||
show() {
|
||||
this.dialogs
|
||||
.open(this.error?.message, { label: 'Service error' })
|
||||
.subscribe()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiIcon, TuiLoader } from '@taiga-ui/core'
|
||||
import { TuiIcon, TuiLoader, TuiTitle } from '@taiga-ui/core'
|
||||
|
||||
@Component({
|
||||
selector: 'service-health-check',
|
||||
@@ -17,12 +17,12 @@ import { TuiIcon, TuiLoader } from '@taiga-ui/core'
|
||||
[style.color]="color"
|
||||
/>
|
||||
}
|
||||
<div>
|
||||
<span tuiTitle>
|
||||
<strong [class.tui-skeleton]="!connected">{{ check.name }}</strong>
|
||||
<div [class.tui-skeleton]="!connected" [style.color]="color">
|
||||
<span tuiSubtitle [class.tui-skeleton]="!connected" [style.color]="color">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
@@ -38,7 +38,7 @@ import { TuiIcon, TuiLoader } from '@taiga-ui/core'
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [TuiLoader, TuiIcon],
|
||||
imports: [TuiLoader, TuiIcon, TuiTitle],
|
||||
})
|
||||
export class ServiceHealthCheckComponent {
|
||||
@Input({ required: true })
|
||||
|
||||
@@ -14,7 +14,6 @@ import { ConnectionService } from 'src/app/services/connection.service'
|
||||
template: `
|
||||
@for (check of checks; track $index) {
|
||||
<service-health-check
|
||||
class="g-action"
|
||||
[check]="check"
|
||||
[connected]="!!(connected$ | async)"
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TuiLet } from '@taiga-ui/cdk'
|
||||
import { TuiLoader, TuiIcon, TuiButton } from '@taiga-ui/core'
|
||||
import { TuiLoader, TuiIcon, TuiButton, TuiTitle } from '@taiga-ui/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { map, timer } from 'rxjs'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
@@ -23,26 +24,31 @@ import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe'
|
||||
} @else {
|
||||
<tui-icon icon="@tui.circle-x" class="g-error" />
|
||||
}
|
||||
<div [style.flex]="1">
|
||||
<span tuiTitle>
|
||||
<strong>{{ info.name }}</strong>
|
||||
<div>{{ info.description }}</div>
|
||||
<span tuiSubtitle>{{ info.description }}</span>
|
||||
@if (check) {
|
||||
<div class="g-error">
|
||||
<b>Health check failed:</b>
|
||||
{{ check }}
|
||||
</div>
|
||||
<span tuiSubtitle class="g-error">
|
||||
<span>
|
||||
<b>Health check failed:</b>
|
||||
{{ check }}
|
||||
</span>
|
||||
</span>
|
||||
} @else {
|
||||
<div [style.color]="info.color">{{ info.typeDetail }}</div>
|
||||
<span tuiSubtitle [style.color]="info.color">
|
||||
{{ info.typeDetail }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
@if (info.type === 'ui') {
|
||||
<a
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
appearance="flat-grayscale"
|
||||
iconStart="@tui.external-link"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title="Open"
|
||||
size="m"
|
||||
[style.border-radius.%]="100"
|
||||
[attr.href]="href"
|
||||
(click.stop)="(0)"
|
||||
@@ -52,7 +58,8 @@ import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe'
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [CommonModule, TuiButton, TuiLet, TuiLoader, TuiIcon],
|
||||
imports: [CommonModule, TuiButton, TuiLet, TuiLoader, TuiIcon, TuiTitle],
|
||||
hostDirectives: [TuiCell],
|
||||
})
|
||||
export class ServiceInterfaceListItemComponent {
|
||||
private readonly config = inject(ConfigService)
|
||||
@@ -73,7 +80,7 @@ export class ServiceInterfaceListItemComponent {
|
||||
|
||||
get href(): string | null {
|
||||
return this.disabled
|
||||
? null
|
||||
? 'null'
|
||||
: this.config.launchableAddress(this.info, this.pkg.hosts)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import { ServiceInterfaceListItemComponent } from './interface-list-item.compone
|
||||
template: `
|
||||
@for (info of pkg | interfaceInfo; track $index) {
|
||||
<a
|
||||
class="g-action"
|
||||
serviceInterfaceListItem
|
||||
[info]="info"
|
||||
[pkg]="pkg"
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import { TuiIcon } from '@taiga-ui/core'
|
||||
import { TuiIcon, TuiTitle } from '@taiga-ui/core'
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { ServiceMenu } from '../pipes/to-menu.pipe'
|
||||
|
||||
@Component({
|
||||
selector: '[serviceMenuItem]',
|
||||
template: `
|
||||
<tui-icon [icon]="menu.icon" />
|
||||
<div [style.flex]="1">
|
||||
<span tuiTitle [style.flex]="1">
|
||||
<strong>{{ menu.name }}</strong>
|
||||
<div>
|
||||
<span tuiSubtitle>
|
||||
{{ menu.description }}
|
||||
<ng-content />
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<ng-content />
|
||||
</span>
|
||||
<tui-icon icon="@tui.chevron-right" />
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [TuiIcon],
|
||||
imports: [TuiIcon, TuiTitle],
|
||||
hostDirectives: [TuiCell],
|
||||
})
|
||||
export class ServiceMenuItemComponent {
|
||||
@Input({ required: true, alias: 'serviceMenuItem' })
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { ToMenuPipe } from '../pipes/to-menu.pipe'
|
||||
import { ServiceMenuItemComponent } from './menu-item.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
|
||||
@Component({
|
||||
selector: 'service-menu',
|
||||
@@ -10,19 +10,16 @@ import { RouterLink } from '@angular/router'
|
||||
@for (menu of pkg | toMenu; track $index) {
|
||||
@if (menu.routerLink) {
|
||||
<a
|
||||
class="g-action"
|
||||
[serviceMenuItem]="menu"
|
||||
[routerLink]="menu.routerLink"
|
||||
[queryParams]="menu.params || {}"
|
||||
></a>
|
||||
} @else {
|
||||
<button
|
||||
class="g-action"
|
||||
[serviceMenuItem]="menu"
|
||||
(click)="menu.action?.()"
|
||||
>
|
||||
<button [serviceMenuItem]="menu" (click)="menu.action?.()">
|
||||
@if (menu.name === 'Outbound Proxy') {
|
||||
<div [style.color]="color">{{ pkg.outboundProxy || 'None' }}</div>
|
||||
<div tuiSubtitle [style.color]="color">
|
||||
{{ pkg.outboundProxy || 'None' }}
|
||||
</div>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import { InstallingProgressPipe } from 'src/app/routes/portal/routes/service/pip
|
||||
selector: '[progress]',
|
||||
template: `
|
||||
<ng-content />
|
||||
@if (progress | installingProgress; as decimal) {
|
||||
: {{ decimal * 100 }}%
|
||||
@if (progress | installingProgress; as percent) {
|
||||
: {{ percent }}%
|
||||
<progress
|
||||
tuiProgressBar
|
||||
size="xs"
|
||||
@@ -17,7 +17,7 @@ import { InstallingProgressPipe } from 'src/app/routes/portal/routes/service/pip
|
||||
? 'var(--tui-text-positive)'
|
||||
: 'var(--tui-text-action)'
|
||||
"
|
||||
[value]="decimal * 100"
|
||||
[value]="percent / 100"
|
||||
></progress>
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { CopyService } from '@start9labs/shared'
|
||||
import { TuiButton, TuiLabel, TuiTitle } from '@taiga-ui/core'
|
||||
import { mask } from 'src/app/utils/mask'
|
||||
|
||||
@Component({
|
||||
selector: 'service-property',
|
||||
template: `
|
||||
<label [style.flex]="1" tuiTitle>
|
||||
<span tuiSubtitle>{{ label }}</span>
|
||||
{{ masked ? mask : value }}
|
||||
</label>
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
[iconStart]="masked ? '@tui.eye' : '@tui.eye-off'"
|
||||
(click)="masked = !masked"
|
||||
>
|
||||
Toggle
|
||||
</button>
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
iconStart="@tui.copy"
|
||||
(click)="copyService.copy(value)"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: flex;
|
||||
padding: 0.5rem 0;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
box-shadow: 0 1px var(--tui-background-neutral-1);
|
||||
}
|
||||
}
|
||||
`,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [TuiButton, TuiLabel, TuiTitle],
|
||||
})
|
||||
export class ServicePropertyComponent {
|
||||
@Input()
|
||||
label = ''
|
||||
|
||||
@Input()
|
||||
value = ''
|
||||
|
||||
masked = true
|
||||
|
||||
readonly copyService = inject(CopyService)
|
||||
|
||||
get mask(): string {
|
||||
return mask(this.value, 64)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import { AsyncPipe } from '@angular/common'
|
||||
import { Component, inject } from '@angular/core'
|
||||
import { getErrorMessage } from '@start9labs/shared'
|
||||
import { T, utils } from '@start9labs/start-sdk'
|
||||
import {
|
||||
TuiButton,
|
||||
TuiDialogContext,
|
||||
TuiDialogService,
|
||||
TuiLoader,
|
||||
TuiNotification,
|
||||
} from '@taiga-ui/core'
|
||||
import { TUI_CONFIRM, TuiConfirmData } from '@taiga-ui/kit'
|
||||
import { injectContext } from '@taiga-ui/polymorpheus'
|
||||
import * as json from 'fast-json-patch'
|
||||
import { compare } from 'fast-json-patch'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { catchError, defer, EMPTY, endWith, firstValueFrom, map } from 'rxjs'
|
||||
import {
|
||||
ActionButton,
|
||||
FormComponent,
|
||||
} from 'src/app/routes/portal/components/form.component'
|
||||
import { ActionRequestInfoComponent } from 'src/app/routes/portal/modals/config-dep.component'
|
||||
import { ActionService } from 'src/app/services/action.service'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { getAllPackages, getManifest } from 'src/app/utils/get-package-data'
|
||||
import { InvalidService } from '../../../components/form/invalid.service'
|
||||
|
||||
export type PackageActionData = {
|
||||
pkgInfo: {
|
||||
id: string
|
||||
title: string
|
||||
icon: string
|
||||
mainStatus: T.MainStatus['main']
|
||||
}
|
||||
actionInfo: {
|
||||
id: string
|
||||
metadata: T.ActionMetadata
|
||||
}
|
||||
requestInfo?: {
|
||||
dependentId?: string
|
||||
request: T.ActionRequest
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div class="service-title">
|
||||
<img [src]="pkgInfo.icon" alt="" />
|
||||
<h4>{{ pkgInfo.title }}</h4>
|
||||
</div>
|
||||
@if (res$ | async; as res) {
|
||||
@if (error) {
|
||||
<tui-notification appearance="negative">
|
||||
<div [innerHTML]="error"></div>
|
||||
</tui-notification>
|
||||
}
|
||||
|
||||
@if (warning) {
|
||||
<tui-notification appearance="warning">
|
||||
<div [innerHTML]="warning"></div>
|
||||
</tui-notification>
|
||||
}
|
||||
|
||||
@if (requestInfo) {
|
||||
<action-request-info
|
||||
[originalValue]="res.originalValue || {}"
|
||||
[operations]="res.operations || []"
|
||||
/>
|
||||
}
|
||||
|
||||
<app-form
|
||||
[spec]="res.spec"
|
||||
[value]="res.originalValue || {}"
|
||||
[buttons]="buttons"
|
||||
[operations]="res.operations || []"
|
||||
>
|
||||
<button
|
||||
tuiButton
|
||||
appearance="flat-grayscale"
|
||||
type="reset"
|
||||
[style.margin-right]="'auto'"
|
||||
>
|
||||
Reset Defaults
|
||||
</button>
|
||||
</app-form>
|
||||
} @else {
|
||||
<tui-loader size="l" textContent="loading" />
|
||||
}
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
tui-notification {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
.service-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1.4rem;
|
||||
img {
|
||||
height: 20px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
`,
|
||||
],
|
||||
standalone: true,
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
TuiNotification,
|
||||
TuiLoader,
|
||||
TuiButton,
|
||||
ActionRequestInfoComponent,
|
||||
FormComponent,
|
||||
],
|
||||
providers: [InvalidService],
|
||||
})
|
||||
export class ActionInputModal {
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
private readonly actionService = inject(ActionService)
|
||||
private readonly context =
|
||||
injectContext<TuiDialogContext<void, PackageActionData>>()
|
||||
|
||||
readonly actionId = this.context.data.actionInfo.id
|
||||
readonly warning = this.context.data.actionInfo.metadata.warning
|
||||
readonly pkgInfo = this.context.data.pkgInfo
|
||||
readonly requestInfo = this.context.data.requestInfo
|
||||
|
||||
buttons: ActionButton<any>[] = [
|
||||
{
|
||||
text: 'Submit',
|
||||
handler: value => this.execute(value),
|
||||
},
|
||||
]
|
||||
|
||||
error = ''
|
||||
|
||||
res$ = defer(() =>
|
||||
this.api.getActionInput({
|
||||
packageId: this.pkgInfo.id,
|
||||
actionId: this.actionId,
|
||||
}),
|
||||
).pipe(
|
||||
map(res => {
|
||||
const originalValue = res.value || {}
|
||||
|
||||
return {
|
||||
spec: res.spec,
|
||||
originalValue,
|
||||
operations: this.requestInfo?.request.input
|
||||
? compare(
|
||||
JSON.parse(JSON.stringify(originalValue)),
|
||||
utils.deepMerge(
|
||||
JSON.parse(JSON.stringify(originalValue)),
|
||||
this.requestInfo.request.input.value,
|
||||
) as object,
|
||||
)
|
||||
: null,
|
||||
}
|
||||
}),
|
||||
catchError(e => {
|
||||
this.error = String(getErrorMessage(e))
|
||||
return EMPTY
|
||||
}),
|
||||
)
|
||||
|
||||
async execute(input: object) {
|
||||
if (await this.checkConflicts(input)) {
|
||||
return this.actionService.execute(this.pkgInfo.id, this.actionId, input)
|
||||
}
|
||||
}
|
||||
|
||||
private async checkConflicts(input: object): Promise<boolean> {
|
||||
const packages = await getAllPackages(this.patch)
|
||||
|
||||
const breakages = Object.keys(packages)
|
||||
.filter(
|
||||
id =>
|
||||
id !== this.pkgInfo.id &&
|
||||
Object.values(packages[id].requestedActions).some(
|
||||
({ request, active }) =>
|
||||
!active &&
|
||||
request.severity === 'critical' &&
|
||||
request.packageId === this.pkgInfo.id &&
|
||||
request.actionId === this.actionId &&
|
||||
request.when?.condition === 'input-not-matches' &&
|
||||
request.input &&
|
||||
json
|
||||
.compare(input, request.input)
|
||||
.some(op => op.op === 'add' || op.op === 'replace'),
|
||||
),
|
||||
)
|
||||
.map(id => id)
|
||||
|
||||
if (!breakages.length) return true
|
||||
|
||||
const message =
|
||||
'As a result of this change, the following services will no longer work properly and may crash:<ul>'
|
||||
const content = `${message}${breakages.map(
|
||||
id => `<li><b>${getManifest(packages[id]).title}</b></li>`,
|
||||
)}</ul>`
|
||||
const data: TuiConfirmData = { content, yes: 'Continue', no: 'Cancel' }
|
||||
|
||||
return firstValueFrom(
|
||||
this.dialogs.open<boolean>(TUI_CONFIRM, { data }).pipe(endWith(false)),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { TuiDialogOptions } from '@taiga-ui/core'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { injectContext } from '@taiga-ui/polymorpheus'
|
||||
import { ToAdditionalPipe } from 'src/app/routes/portal/routes/service/pipes/to-additional.pipe'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { ServiceAdditionalItemComponent } from './additional-item.component'
|
||||
|
||||
@Component({
|
||||
@@ -31,6 +26,5 @@ import { ServiceAdditionalItemComponent } from './additional-item.component'
|
||||
imports: [ToAdditionalPipe, ServiceAdditionalItemComponent],
|
||||
})
|
||||
export class ServiceAdditionalModal {
|
||||
readonly pkg =
|
||||
inject<TuiDialogOptions<PackageDataEntry>>(POLYMORPHEUS_CONTEXT).data
|
||||
readonly pkg = injectContext<TuiDialogOptions<PackageDataEntry>>().data
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { TuiLoader, TuiButton } from '@taiga-ui/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ErrorService } from '@start9labs/shared'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ServicePropertyComponent } from '../components/property.component'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@if (loading$ | async) {
|
||||
<tui-loader />
|
||||
} @else {
|
||||
@for (prop of properties | keyvalue: asIsOrder; track prop) {
|
||||
<service-property [label]="prop.key" [value]="prop.value" />
|
||||
} @empty {
|
||||
No properties
|
||||
}
|
||||
}
|
||||
<button tuiButton iconStart="@tui.refresh-cw" (click)="refresh()">
|
||||
Refresh
|
||||
</button>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
button {
|
||||
float: right;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [CommonModule, TuiButton, ServicePropertyComponent, TuiLoader],
|
||||
})
|
||||
export class ServicePropertiesModal {
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
|
||||
readonly id = inject<{ data: string }>(POLYMORPHEUS_CONTEXT).data
|
||||
readonly loading$ = new BehaviorSubject(true)
|
||||
|
||||
properties: Record<string, string> = {}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.getProperties()
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
await this.getProperties()
|
||||
}
|
||||
|
||||
private async getProperties(): Promise<void> {
|
||||
this.loading$.next(true)
|
||||
|
||||
try {
|
||||
// @TODO Matt this needs complete rework, right?
|
||||
// this.properties = await this.api.getPackageProperties({ id: this.id })
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
this.loading$.next(false)
|
||||
}
|
||||
}
|
||||
|
||||
asIsOrder(a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { WithId } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Pipe({
|
||||
name: 'groupActions',
|
||||
standalone: true,
|
||||
})
|
||||
export class GroupActionsPipe implements PipeTransform {
|
||||
transform(
|
||||
actions: PackageDataEntry['actions'],
|
||||
): Array<Array<WithId<T.ActionMetadata>>> | null {
|
||||
if (!actions) return null
|
||||
|
||||
const noGroup = 'noGroup'
|
||||
const grouped = Object.entries(actions).reduce<
|
||||
Record<string, WithId<T.ActionMetadata>[]>
|
||||
>((groups, [id, action]) => {
|
||||
const actionWithId = { id, ...action }
|
||||
const groupKey = action.group || noGroup
|
||||
|
||||
if (!groups[groupKey]) {
|
||||
groups[groupKey] = [actionWithId]
|
||||
} else {
|
||||
groups[groupKey].push(actionWithId)
|
||||
}
|
||||
|
||||
return groups
|
||||
}, {})
|
||||
|
||||
return Object.values(grouped).map(group =>
|
||||
group.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { getDepDetails } from 'src/app/utils/dep-info'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { ActionRequest } from '../components/action-request.component'
|
||||
|
||||
@Pipe({
|
||||
standalone: true,
|
||||
name: 'toActionRequests',
|
||||
})
|
||||
export class ToActionRequestsPipe implements PipeTransform {
|
||||
transform(pkg: PackageDataEntry, packages: Record<string, PackageDataEntry>) {
|
||||
const { id } = getManifest(pkg)
|
||||
const critical: ActionRequest[] = []
|
||||
const important: ActionRequest[] = []
|
||||
|
||||
Object.values(pkg.requestedActions)
|
||||
.filter(r => r.active)
|
||||
.forEach(r => {
|
||||
const self = r.request.packageId === id
|
||||
const toReturn = {
|
||||
...r.request,
|
||||
actionName: self
|
||||
? pkg.actions[r.request.actionId].name
|
||||
: packages[r.request.packageId]?.actions[r.request.actionId].name ||
|
||||
'Unknown Action',
|
||||
dependency: self
|
||||
? null
|
||||
: getDepDetails(pkg, packages, r.request.packageId),
|
||||
}
|
||||
|
||||
if (r.request.severity === 'critical') {
|
||||
critical.push(toReturn)
|
||||
} else {
|
||||
important.push(toReturn)
|
||||
}
|
||||
})
|
||||
|
||||
return { critical, important }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { inject, Pipe, PipeTransform } from '@angular/core'
|
||||
import { CopyService, MarkdownComponent } from '@start9labs/shared'
|
||||
import { CopyService, MARKDOWN } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { from } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
@@ -70,7 +69,7 @@ export class ToAdditionalPipe implements PipeTransform {
|
||||
|
||||
private showLicense({ id, version }: T.Manifest) {
|
||||
this.dialogs
|
||||
.open(new PolymorpheusComponent(MarkdownComponent), {
|
||||
.open(MARKDOWN, {
|
||||
label: 'License',
|
||||
size: 'l',
|
||||
data: {
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
import { inject, Pipe, PipeTransform } from '@angular/core'
|
||||
import { Params } from '@angular/router'
|
||||
import { MarkdownComponent } from '@start9labs/shared'
|
||||
import { MARKDOWN } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { from } from 'rxjs'
|
||||
// @TODO Alex implement config
|
||||
// import {
|
||||
// ConfigModal,
|
||||
// PackageConfigData,
|
||||
// } from 'src/app/routes/portal/modals/config.component'
|
||||
import { ServiceAdditionalModal } from 'src/app/routes/portal/routes/service/modals/additional.component'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { ProxyService } from 'src/app/services/proxy.service'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { ServicePropertiesModal } from 'src/app/routes/portal/routes/service/modals/properties.component'
|
||||
|
||||
export interface ServiceMenu {
|
||||
icon: string
|
||||
@@ -47,24 +41,6 @@ export class ToMenuPipe implements PipeTransform {
|
||||
description: `Understand how to use ${manifest.title}`,
|
||||
action: () => this.showInstructions(manifest),
|
||||
},
|
||||
{
|
||||
icon: '@tui.sliders-vertical',
|
||||
name: 'Config',
|
||||
description: `Customize ${manifest.title}`,
|
||||
action: () => this.openConfig(manifest),
|
||||
},
|
||||
{
|
||||
icon: '@tui.key',
|
||||
name: 'Properties',
|
||||
description: `Runtime information, credentials, and other values of interest`,
|
||||
action: () =>
|
||||
this.dialogs
|
||||
.open(new PolymorpheusComponent(ServicePropertiesModal), {
|
||||
label: `${manifest.title} credentials`,
|
||||
data: manifest.id,
|
||||
})
|
||||
.subscribe(),
|
||||
},
|
||||
{
|
||||
icon: '@tui.zap',
|
||||
name: 'Actions',
|
||||
@@ -121,7 +97,7 @@ export class ToMenuPipe implements PipeTransform {
|
||||
.catch(e => console.error('Failed to mark instructions as seen', e))
|
||||
|
||||
this.dialogs
|
||||
.open(new PolymorpheusComponent(MarkdownComponent), {
|
||||
.open(MARKDOWN, {
|
||||
label: `${title} instructions`,
|
||||
size: 'l',
|
||||
data: {
|
||||
@@ -130,11 +106,4 @@ export class ToMenuPipe implements PipeTransform {
|
||||
})
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
private openConfig({ title, id }: T.Manifest) {
|
||||
// this.formDialog.open<PackageConfigData>(ConfigModal, {
|
||||
// label: `${title} configuration`,
|
||||
// data: { pkgId: id },
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,215 +1,99 @@
|
||||
import { TUI_CONFIRM } from '@taiga-ui/kit'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import {
|
||||
isEmptyObject,
|
||||
WithId,
|
||||
ErrorService,
|
||||
LoadingService,
|
||||
getPkgId,
|
||||
} from '@start9labs/shared'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { filter, switchMap, timer } from 'rxjs'
|
||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import {
|
||||
DataModel,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { hasCurrentDeps } from 'src/app/utils/has-deps'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import { ServiceActionComponent } from '../components/action.component'
|
||||
import { ActionSuccessPage } from '../modals/action-success/action-success.page'
|
||||
import { GroupActionsPipe } from '../pipes/group-actions.pipe'
|
||||
import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
|
||||
import { toSignal } from '@angular/core/rxjs-interop'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { getAllPackages, getManifest } from 'src/app/utils/get-package-data'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { filter, map } from 'rxjs'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { StandardActionsService } from 'src/app/services/standard-actions.service'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { ActionService } from 'src/app/services/action.service'
|
||||
import { ServiceActionComponent } from '../components/action.component'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@if (pkg$ | async; as pkg) {
|
||||
@if (package(); as pkg) {
|
||||
<section>
|
||||
<h3 class="g-title">Standard Actions</h3>
|
||||
<button
|
||||
class="g-action"
|
||||
[action]="action"
|
||||
(click)="tryUninstall(pkg)"
|
||||
[action]="rebuild"
|
||||
(click)="service.rebuild(pkg.manifest.id)"
|
||||
></button>
|
||||
<button
|
||||
class="g-action"
|
||||
[action]="uninstall"
|
||||
(click)="service.uninstall(pkg.manifest)"
|
||||
></button>
|
||||
</section>
|
||||
<ng-container *ngIf="pkg.actions | groupActions as actionGroups">
|
||||
<h3 *ngIf="actionGroups.length" class="g-title">
|
||||
Actions for {{ (pkg | toManifest).title }}
|
||||
</h3>
|
||||
<div *ngFor="let group of actionGroups">
|
||||
@if (pkg.actions.length) {
|
||||
<h3 class="g-title">Actions for {{ pkg.manifest.title }}</h3>
|
||||
}
|
||||
@for (action of pkg.actions; track $index) {
|
||||
@if (action.visibility !== 'hidden') {
|
||||
<button
|
||||
*ngFor="let action of group"
|
||||
class="g-action"
|
||||
[action]="{
|
||||
name: action.name,
|
||||
description: action.description,
|
||||
icon: '@tui.circle-play',
|
||||
}"
|
||||
(click)="handleAction(action)"
|
||||
[action]="action"
|
||||
(click)="
|
||||
handleAction(pkg.mainStatus, pkg.icon, pkg.manifest, action)
|
||||
"
|
||||
></button>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
ServiceActionComponent,
|
||||
GroupActionsPipe,
|
||||
ToManifestPipe,
|
||||
],
|
||||
imports: [ServiceActionComponent],
|
||||
})
|
||||
export class ServiceActionsRoute {
|
||||
private readonly id = getPkgId(inject(ActivatedRoute))
|
||||
private readonly actions = inject(ActionService)
|
||||
|
||||
readonly pkg$ = this.patch
|
||||
.watch$('packageData', this.id)
|
||||
.pipe(filter(pkg => pkg.stateInfo.state === 'installed'))
|
||||
readonly service = inject(StandardActionsService)
|
||||
readonly package = toSignal(
|
||||
inject<PatchDB<DataModel>>(PatchDB)
|
||||
.watch$('packageData', getPkgId())
|
||||
.pipe(
|
||||
filter(pkg => pkg.stateInfo.state === 'installed'),
|
||||
map(pkg => ({
|
||||
mainStatus: pkg.status.main,
|
||||
icon: pkg.icon,
|
||||
manifest: getManifest(pkg),
|
||||
actions: Object.keys(pkg.actions).map(id => ({
|
||||
id,
|
||||
...pkg.actions[id],
|
||||
})),
|
||||
})),
|
||||
),
|
||||
)
|
||||
|
||||
readonly action = {
|
||||
icon: '@tui.trash-2',
|
||||
name: 'Uninstall',
|
||||
description:
|
||||
'This will uninstall the service from StartOS and delete all data permanently.',
|
||||
}
|
||||
readonly rebuild = REBUILD
|
||||
readonly uninstall = UNINSTALL
|
||||
|
||||
constructor(
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly dialogs: TuiDialogService,
|
||||
private readonly errorService: ErrorService,
|
||||
private readonly loader: LoadingService,
|
||||
private readonly router: Router,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly formDialog: FormDialogService,
|
||||
) {}
|
||||
|
||||
async handleAction(action: WithId<T.ActionMetadata>) {
|
||||
// @TODO Matt this needs complete rework, right?
|
||||
// if (action.disabled) {
|
||||
// this.dialogs
|
||||
// .open(action.disabled, {
|
||||
// label: 'Forbidden',
|
||||
// size: 's',
|
||||
// })
|
||||
// .subscribe()
|
||||
// } else {
|
||||
// if (action.input && !isEmptyObject(action.input)) {
|
||||
// this.formDialog.open(FormComponent, {
|
||||
// label: action.name,
|
||||
// data: {
|
||||
// spec: action.input,
|
||||
// buttons: [
|
||||
// {
|
||||
// text: 'Execute',
|
||||
// handler: async (value: any) =>
|
||||
// this.executeAction(action.id, value),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// })
|
||||
// } else {
|
||||
// this.dialogs
|
||||
// .open(TUI_CONFIRM, {
|
||||
// label: 'Confirm',
|
||||
// size: 's',
|
||||
// data: {
|
||||
// content: `Are you sure you want to execute action "${
|
||||
// action.name
|
||||
// }"? ${action.warning || ''}`,
|
||||
// yes: 'Execute',
|
||||
// no: 'Cancel',
|
||||
// },
|
||||
// })
|
||||
// .pipe(filter(Boolean))
|
||||
// .subscribe(() => this.executeAction(action.id))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
async tryUninstall(pkg: PackageDataEntry): Promise<void> {
|
||||
const { title, alerts, id } = getManifest(pkg)
|
||||
|
||||
let content =
|
||||
alerts.uninstall ||
|
||||
`Uninstalling ${title} will permanently delete its data`
|
||||
|
||||
if (hasCurrentDeps(id, await getAllPackages(this.patch))) {
|
||||
content = `${content}. Services that depend on ${title} will no longer work properly and may crash`
|
||||
}
|
||||
|
||||
this.dialogs
|
||||
.open(TUI_CONFIRM, {
|
||||
label: 'Warning',
|
||||
size: 's',
|
||||
data: {
|
||||
content,
|
||||
yes: 'Uninstall',
|
||||
no: 'Cancel',
|
||||
},
|
||||
})
|
||||
.pipe(filter(Boolean))
|
||||
.subscribe(() => this.uninstall())
|
||||
}
|
||||
|
||||
private async uninstall() {
|
||||
const loader = this.loader.open(`Beginning uninstall...`).subscribe()
|
||||
|
||||
try {
|
||||
await this.embassyApi.uninstallPackage({ id: this.id })
|
||||
this.embassyApi
|
||||
.setDbValue<boolean>(['ack-instructions', this.id], false)
|
||||
.catch(e => console.error('Failed to mark instructions as unseen', e))
|
||||
this.router.navigate(['./portal/dashboard'])
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
loader.unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
private async executeAction(
|
||||
actionId: string,
|
||||
input?: object,
|
||||
): Promise<boolean> {
|
||||
const loader = this.loader.open('Executing action...').subscribe()
|
||||
|
||||
try {
|
||||
// @TODO Matt this needs complete rework, right?
|
||||
// const data = await this.embassyApi.executePackageAction({
|
||||
// id: this.id,
|
||||
// actionId,
|
||||
// input,
|
||||
// })
|
||||
|
||||
timer(500)
|
||||
.pipe(
|
||||
switchMap(() =>
|
||||
this.dialogs.open(new PolymorpheusComponent(ActionSuccessPage), {
|
||||
label: 'Execution Complete',
|
||||
// data,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
return true
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
return false
|
||||
} finally {
|
||||
loader.unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
asIsOrder() {
|
||||
return 0
|
||||
handleAction(
|
||||
mainStatus: T.MainStatus['main'],
|
||||
icon: string,
|
||||
manifest: T.Manifest,
|
||||
action: T.ActionMetadata & { id: string },
|
||||
) {
|
||||
this.actions.present({
|
||||
pkgInfo: { id: manifest.id, title: manifest.title, icon, mainStatus },
|
||||
actionInfo: { id: action.id, metadata: action },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const REBUILD = {
|
||||
icon: '@tui.wrench',
|
||||
name: 'Rebuild Service',
|
||||
description:
|
||||
'Rebuilds the service container. It is harmless and only takes a few seconds to complete, but it should only be necessary if a StartOS bug is preventing dependencies, interfaces, or actions from synchronizing.',
|
||||
}
|
||||
|
||||
const UNINSTALL = {
|
||||
icon: '@tui.trash-2',
|
||||
name: 'Uninstall',
|
||||
description:
|
||||
'Uninstalls this service from StartOS and delete all data permanently.',
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ import { getMultihostAddresses } from '../../../components/interfaces/interface.
|
||||
imports: [CommonModule, InterfaceComponent],
|
||||
})
|
||||
export class ServiceInterfaceRoute {
|
||||
private readonly route = inject(ActivatedRoute)
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
|
||||
readonly context = {
|
||||
packageId: getPkgId(this.route),
|
||||
interfaceId: this.route.snapshot.paramMap.get('interfaceId') || '',
|
||||
packageId: getPkgId(),
|
||||
interfaceId:
|
||||
inject(ActivatedRoute).snapshot.paramMap.get('interfaceId') || '',
|
||||
}
|
||||
|
||||
readonly interfacesWithAddresses$ = combineLatest([
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { RR } from 'src/app/services/api/api.types'
|
||||
import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component'
|
||||
import { RR } from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
|
||||
@Component({
|
||||
template: '<logs [fetchLogs]="fetch" [followLogs]="follow" [context]="id" />',
|
||||
@@ -15,7 +14,7 @@ import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.compon
|
||||
export class ServiceLogsRoute {
|
||||
private readonly api = inject(ApiService)
|
||||
|
||||
readonly id = getPkgId(inject(ActivatedRoute))
|
||||
readonly id = getPkgId()
|
||||
|
||||
readonly follow = async (params: RR.FollowServerLogsReq) =>
|
||||
this.api.followPackageLogs({ id: this.id, ...params })
|
||||
|
||||
@@ -5,11 +5,6 @@ import { isEmptyObject } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { combineLatest, map, switchMap } from 'rxjs'
|
||||
// @TODO Alex implement config
|
||||
// import {
|
||||
// ConfigModal,
|
||||
// PackageConfigData,
|
||||
// } from 'src/app/routes/portal/modals/config.component'
|
||||
import { ServiceBackupsComponent } from 'src/app/routes/portal/routes/service/components/backups.component'
|
||||
import { InstallingProgressPipe } from 'src/app/routes/portal/routes/service/pipes/install-progress.pipe'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
@@ -30,13 +25,16 @@ import {
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
import { DependentInfo } from 'src/app/types/dependent-info'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { ServiceActionRequestComponent } from '../components/action-request.component'
|
||||
import { ServiceActionsComponent } from '../components/actions.component'
|
||||
import { ServiceDependenciesComponent } from '../components/dependencies.component'
|
||||
import { ServiceErrorComponent } from '../components/error.component'
|
||||
import { ServiceHealthChecksComponent } from '../components/health-checks.component'
|
||||
import { ServiceInterfaceListComponent } from '../components/interface-list.component'
|
||||
import { ServiceMenuComponent } from '../components/menu.component'
|
||||
import { ServiceProgressComponent } from '../components/progress.component'
|
||||
import { ServiceStatusComponent } from '../components/status.component'
|
||||
import { ToActionRequestsPipe } from '../pipes/to-action-requests.pipe'
|
||||
import { DependencyInfo } from '../types/dependency-info'
|
||||
|
||||
@Component({
|
||||
@@ -61,10 +59,17 @@ import { DependencyInfo } from '../types/dependency-info'
|
||||
<service-backups [pkg]="service.pkg" />
|
||||
</section>
|
||||
|
||||
<section [style.grid-column]="'span 6'">
|
||||
<h3>Metrics</h3>
|
||||
TODO
|
||||
</section>
|
||||
@if (service.pkg.status.main === 'error') {
|
||||
<section class="error">
|
||||
<h3>Error</h3>
|
||||
<service-error [pkg]="service.pkg" />
|
||||
</section>
|
||||
} @else {
|
||||
<section [style.grid-column]="'span 6'">
|
||||
<h3>Metrics</h3>
|
||||
TODO
|
||||
</section>
|
||||
}
|
||||
|
||||
<section [style.grid-column]="'span 4'" [style.align-self]="'start'">
|
||||
<h3>Menu</h3>
|
||||
@@ -72,6 +77,34 @@ import { DependencyInfo } from '../types/dependency-info'
|
||||
</section>
|
||||
|
||||
<div>
|
||||
@if (service.pkg | toActionRequests: service.allPkgs; as requests) {
|
||||
@if (requests.critical.length) {
|
||||
<section>
|
||||
<h3>Required Actions</h3>
|
||||
@for (request of requests.critical; track $index) {
|
||||
<button
|
||||
[actionRequest]="request"
|
||||
[pkg]="service.pkg"
|
||||
[allPkgs]="service.allPkgs"
|
||||
></button>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
@if (requests.important.length) {
|
||||
<section>
|
||||
<h3>Requested Actions</h3>
|
||||
@for (request of requests.important; track $index) {
|
||||
<button
|
||||
[actionRequest]="request"
|
||||
[pkg]="service.pkg"
|
||||
[allPkgs]="service.allPkgs"
|
||||
></button>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
}
|
||||
|
||||
<section>
|
||||
<h3>Health Checks</h3>
|
||||
<service-health-checks [checks]="(health$ | async) || []" />
|
||||
@@ -124,6 +157,24 @@ import { DependencyInfo } from '../types/dependency-info'
|
||||
background: var(--tui-background-neutral-1);
|
||||
box-shadow: inset 0 7rem 0 -4rem var(--tui-background-neutral-1);
|
||||
clip-path: polygon(0 1.5rem, 1.5rem 0, 100% 0, 100% 100%, 0 100%);
|
||||
|
||||
&.error {
|
||||
box-shadow: inset 0 7rem 0 -4rem var(--tui-status-negative-pale);
|
||||
grid-column: span 6;
|
||||
|
||||
h3 {
|
||||
color: var(--tui-status-negative);
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep [tuiCell] {
|
||||
width: stretch;
|
||||
margin: 0 -1rem;
|
||||
|
||||
&:not(:last-child) {
|
||||
box-shadow: 0 0.51rem 0 -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
@@ -153,6 +204,9 @@ import { DependencyInfo } from '../types/dependency-info'
|
||||
ServiceDependenciesComponent,
|
||||
ServiceMenuComponent,
|
||||
ServiceBackupsComponent,
|
||||
ServiceActionRequestComponent,
|
||||
ServiceErrorComponent,
|
||||
ToActionRequestsPipe,
|
||||
InstallingProgressPipe,
|
||||
],
|
||||
})
|
||||
@@ -169,15 +223,20 @@ export class ServiceRoute {
|
||||
readonly service$ = this.pkgId$.pipe(
|
||||
switchMap(pkgId =>
|
||||
combineLatest([
|
||||
this.patch.watch$('packageData', pkgId),
|
||||
this.patch.watch$('packageData'),
|
||||
this.depErrorService.getPkgDepErrors$(pkgId),
|
||||
]),
|
||||
]).pipe(
|
||||
map(([allPkgs, depErrors]) => {
|
||||
const pkg = allPkgs[pkgId]
|
||||
return {
|
||||
allPkgs,
|
||||
pkg,
|
||||
dependencies: this.getDepInfo(pkg, depErrors),
|
||||
status: renderPkgStatus(pkg, depErrors),
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
map(([pkg, depErrors]) => ({
|
||||
pkg,
|
||||
dependencies: this.getDepInfo(pkg, depErrors),
|
||||
status: renderPkgStatus(pkg, depErrors),
|
||||
})),
|
||||
)
|
||||
|
||||
readonly health$ = this.pkgId$.pipe(
|
||||
@@ -263,11 +322,8 @@ export class ServiceRoute {
|
||||
errorText = 'Incorrect version'
|
||||
fixText = 'Update'
|
||||
fixAction = () => this.fixDep(pkg, pkgManifest, 'update', depId)
|
||||
// @TODO Matt do we just remove this case?
|
||||
// } else if (depError.type === 'configUnsatisfied') {
|
||||
// errorText = 'Config not satisfied'
|
||||
// fixText = 'Auto config'
|
||||
// fixAction = () => this.fixDep(pkg, pkgManifest, 'configure', depId)
|
||||
} else if (depError.type === 'actionRequired') {
|
||||
errorText = 'Action Required (see above)'
|
||||
} else if (depError.type === 'notRunning') {
|
||||
errorText = 'Not running'
|
||||
fixText = 'Start'
|
||||
|
||||
@@ -51,7 +51,7 @@ interface Package {
|
||||
}
|
||||
</div>
|
||||
<footer class="g-buttons">
|
||||
<button tuiButton appearance="flat" (click)="toggleSelectAll()">
|
||||
<button tuiButton appearance="flat-grayscale" (click)="toggleSelectAll()">
|
||||
Toggle all
|
||||
</button>
|
||||
<button tuiButton [disabled]="!hasSelection" (click)="done()">
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import { toSignal } from '@angular/core/rxjs-interop'
|
||||
import {
|
||||
TuiWrapperModule,
|
||||
TuiInputModule,
|
||||
TuiInputNumberModule,
|
||||
} from '@taiga-ui/legacy'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Component, inject } from '@angular/core'
|
||||
import { toSignal } from '@angular/core/rxjs-interop'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { TuiDialogContext, TuiDialogService, TuiButton } from '@taiga-ui/core'
|
||||
import { TuiButton, TuiDialogContext, TuiDialogService } from '@taiga-ui/core'
|
||||
import { TuiBadge, TuiSwitch } from '@taiga-ui/kit'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
TuiInputModule,
|
||||
TuiInputNumberModule,
|
||||
TuiWrapperModule,
|
||||
} from '@taiga-ui/legacy'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { from, map } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { BackupJob, BackupTarget } from 'src/app/services/api/api.types'
|
||||
import { TARGET, TARGET_CREATE } from './target.component'
|
||||
import { BACKUP, BACKUP_OPTIONS } from './backup.component'
|
||||
import { BackupJobBuilder } from '../utils/job-builder'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe'
|
||||
import { BackupJobBuilder } from '../utils/job-builder'
|
||||
import { BACKUP, BACKUP_OPTIONS } from './backup.component'
|
||||
import { TARGET, TARGET_CREATE } from './target.component'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -113,7 +110,7 @@ export class BackupsEditModal {
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly context =
|
||||
inject<TuiDialogContext<BackupJob, BackupJobBuilder>>(POLYMORPHEUS_CONTEXT)
|
||||
injectContext<TuiDialogContext<BackupJob, BackupJobBuilder>>()
|
||||
|
||||
readonly target = toSignal(
|
||||
from(this.api.getBackupTargets({})).pipe(map(({ saved }) => saved)),
|
||||
|
||||
@@ -223,13 +223,13 @@ export class BackupsHistoryModal {
|
||||
}
|
||||
}
|
||||
|
||||
showReport(run: BackupRun) {
|
||||
showReport({ report, completedAt }: BackupRun) {
|
||||
this.dialogs
|
||||
.open(REPORT, {
|
||||
label: 'Backup Report',
|
||||
data: {
|
||||
report: run.report,
|
||||
timestamp: run.completedAt,
|
||||
content: report,
|
||||
timestamp: completedAt,
|
||||
},
|
||||
})
|
||||
.subscribe()
|
||||
|
||||
@@ -5,10 +5,7 @@ import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { TuiMapperPipe } from '@taiga-ui/cdk'
|
||||
import { TuiButton, TuiDialogContext, TuiGroup } from '@taiga-ui/core'
|
||||
import { TuiBlock, TuiCheckbox } from '@taiga-ui/kit'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { take } from 'rxjs'
|
||||
import { PackageBackupInfo } from 'src/app/services/api/api.types'
|
||||
@@ -79,7 +76,7 @@ export class BackupsRecoverModal {
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly context =
|
||||
inject<TuiDialogContext<void, RecoverData>>(POLYMORPHEUS_CONTEXT)
|
||||
injectContext<TuiDialogContext<void, RecoverData>>()
|
||||
|
||||
readonly packageData$ = inject<PatchDB<DataModel>>(PatchDB)
|
||||
.watch$('packageData')
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Component, inject } from '@angular/core'
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerComponent, StartOSDiskInfo } from '@start9labs/shared'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
|
||||
interface Data {
|
||||
servers: StartOSDiskInfo[]
|
||||
@@ -23,8 +20,7 @@ interface Data {
|
||||
imports: [ServerComponent],
|
||||
})
|
||||
export class ServersComponent {
|
||||
readonly context =
|
||||
inject<TuiDialogContext<StartOSDiskInfo, Data>>(POLYMORPHEUS_CONTEXT)
|
||||
readonly context = injectContext<TuiDialogContext<StartOSDiskInfo, Data>>()
|
||||
}
|
||||
|
||||
export const SERVERS = new PolymorpheusComponent(ServersComponent)
|
||||
|
||||
@@ -15,10 +15,7 @@ import {
|
||||
TuiIcon,
|
||||
TuiLoader,
|
||||
} from '@taiga-ui/core'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { BackupTarget } from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
@@ -83,9 +80,9 @@ export class BackupsTargetModal {
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
|
||||
readonly context =
|
||||
inject<
|
||||
injectContext<
|
||||
TuiDialogContext<BackupTarget & { id: string }, { type: BackupType }>
|
||||
>(POLYMORPHEUS_CONTEXT)
|
||||
>()
|
||||
|
||||
readonly loading = signal(true)
|
||||
readonly text =
|
||||
|
||||
@@ -76,7 +76,7 @@ import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
|
||||
<button
|
||||
tuiButton
|
||||
type="button"
|
||||
appearance="outline"
|
||||
appearance="outline-grayscale"
|
||||
(click)="showService()"
|
||||
>
|
||||
View Installed
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { TUI_CONFIRM } from '@taiga-ui/kit'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import {
|
||||
MarketplaceRegistryComponent,
|
||||
StoreIconComponentModule,
|
||||
} from '@start9labs/marketplace'
|
||||
import {
|
||||
ErrorService,
|
||||
LoadingService,
|
||||
@@ -9,30 +12,24 @@ import {
|
||||
toUrl,
|
||||
} from '@start9labs/shared'
|
||||
import {
|
||||
StoreIconComponentModule,
|
||||
MarketplaceRegistryComponent,
|
||||
} from '@start9labs/marketplace'
|
||||
import {
|
||||
TuiButton,
|
||||
TuiDialogContext,
|
||||
TuiDialogService,
|
||||
TuiIcon,
|
||||
TuiTitle,
|
||||
TuiButton,
|
||||
TuiDialogContext,
|
||||
} from '@taiga-ui/core'
|
||||
import {
|
||||
PolymorpheusComponent,
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { TUI_CONFIRM } from '@taiga-ui/kit'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { combineLatest, filter, firstValueFrom, map, Subscription } from 'rxjs'
|
||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { DataModel, UIStore } from 'src/app/services/patch-db/data-model'
|
||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import { getMarketplaceValueSpec, getPromptOptions } from '../utils/registry'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import { DataModel, UIStore } from 'src/app/services/patch-db/data-model'
|
||||
import { getMarketplaceValueSpec, getPromptOptions } from '../utils/registry'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@@ -100,7 +97,7 @@ export class MarketplaceRegistryModal {
|
||||
private readonly formDialog = inject(FormDialogService)
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly marketplaceService = inject(MarketplaceService)
|
||||
private readonly context = inject<TuiDialogContext>(POLYMORPHEUS_CONTEXT)
|
||||
private readonly context = injectContext<TuiDialogContext>()
|
||||
private readonly route = inject(ActivatedRoute)
|
||||
private readonly router = inject(Router)
|
||||
private readonly hosts$ = inject<PatchDB<DataModel>>(PatchDB).watch$(
|
||||
|
||||
@@ -46,12 +46,12 @@ import { toRouterLink } from 'src/app/utils/to-router-link'
|
||||
(overflownChange)="overflow = $event"
|
||||
/>
|
||||
@if (overflow) {
|
||||
<button tuiLink (click)="service.viewFull(notificationItem)">
|
||||
<button tuiLink (click)="service.viewModal(notificationItem, true)">
|
||||
View Full
|
||||
</button>
|
||||
}
|
||||
@if (notificationItem.code === 1) {
|
||||
<button tuiLink (click)="service.viewReport(notificationItem)">
|
||||
@if (notificationItem.code === 1 || notificationItem.code === 2) {
|
||||
<button tuiLink (click)="service.viewModal(notificationItem)">
|
||||
View Report
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -15,44 +15,68 @@ import {
|
||||
} from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiLet } from '@taiga-ui/cdk'
|
||||
import { TuiAlertService, TuiButton } from '@taiga-ui/core'
|
||||
import { TuiButton } from '@taiga-ui/core'
|
||||
import { TuiProgressBar } from '@taiga-ui/kit'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { combineLatest, map } from 'rxjs'
|
||||
import { combineLatest, filter, firstValueFrom, map } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ClientStorageService } from 'src/app/services/client-storage.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { InstallingProgressPipe } from 'src/app/routes/portal/routes/service/pipes/install-progress.pipe'
|
||||
import { SideloadService } from './sideload.service'
|
||||
|
||||
@Component({
|
||||
selector: 'sideload-package',
|
||||
template: `
|
||||
<div class="outer-container">
|
||||
<ng-content />
|
||||
<marketplace-package-hero
|
||||
*tuiLet="button$ | async as button"
|
||||
[pkg]="package"
|
||||
>
|
||||
<div class="inner-container">
|
||||
@if (button !== null && button !== 'Install') {
|
||||
<a
|
||||
tuiButton
|
||||
appearance="tertiary-solid"
|
||||
[routerLink]="'/portal/service/' + package.id"
|
||||
>
|
||||
View installed
|
||||
</a>
|
||||
}
|
||||
@if (button) {
|
||||
<button tuiButton (click)="upload()">{{ button }}</button>
|
||||
}
|
||||
</div>
|
||||
</marketplace-package-hero>
|
||||
<!-- @TODO Matt do we want this here? How do we turn s9pk into MarketplacePkg? -->
|
||||
<!-- <marketplace-about [pkg]="package" />-->
|
||||
<!-- @if (!(package.dependencyMetadata | empty)) {-->
|
||||
<!-- <marketplace-dependencies [pkg]="package" (open)="open($event)" />-->
|
||||
<!-- }-->
|
||||
<!-- <marketplace-additional [pkg]="package" />-->
|
||||
@if (progress$ | async; as progress) {
|
||||
@for (phase of progress.phases; track $index) {
|
||||
<p>
|
||||
{{ phase.name }}
|
||||
@if (phase.progress | installingProgress; as progress) {
|
||||
: {{ progress }}%
|
||||
}
|
||||
</p>
|
||||
<progress
|
||||
tuiProgressBar
|
||||
size="xs"
|
||||
[style.color]="
|
||||
phase.progress === true
|
||||
? 'var(--tui-text-positive)'
|
||||
: 'var(--tui-text-action)'
|
||||
"
|
||||
[attr.value]="(phase.progress | installingProgress) / 100 || null"
|
||||
></progress>
|
||||
}
|
||||
} @else {
|
||||
<marketplace-package-hero
|
||||
*tuiLet="button$ | async as button"
|
||||
[pkg]="package"
|
||||
>
|
||||
<div class="inner-container">
|
||||
@if (button !== null && button !== 'Install') {
|
||||
<a
|
||||
tuiButton
|
||||
appearance="tertiary-solid"
|
||||
[routerLink]="'/portal/service/' + package.id"
|
||||
>
|
||||
View installed
|
||||
</a>
|
||||
}
|
||||
@if (button) {
|
||||
<button tuiButton (click)="upload()">{{ button }}</button>
|
||||
}
|
||||
</div>
|
||||
</marketplace-package-hero>
|
||||
<!-- @TODO Matt do we want this here? How do we turn s9pk into MarketplacePkg? -->
|
||||
<!-- <marketplace-about [pkg]="package" />-->
|
||||
<!-- @if (!(package.dependencyMetadata | empty)) {-->
|
||||
<!-- <marketplace-dependencies [pkg]="package" (open)="open($event)" />-->
|
||||
<!-- }-->
|
||||
<!-- <marketplace-additional [pkg]="package" />-->
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
styles: [
|
||||
@@ -87,6 +111,8 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
||||
TuiLet,
|
||||
MarketplacePackageHeroComponent,
|
||||
MarketplaceDependenciesComponent,
|
||||
InstallingProgressPipe,
|
||||
TuiProgressBar,
|
||||
],
|
||||
})
|
||||
export class SideloadPackageComponent {
|
||||
@@ -94,9 +120,10 @@ export class SideloadPackageComponent {
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly router = inject(Router)
|
||||
private readonly alerts = inject(TuiAlertService)
|
||||
private readonly exver = inject(Exver)
|
||||
private readonly sideloadService = inject(SideloadService)
|
||||
|
||||
readonly progress$ = this.sideloadService.progress$
|
||||
readonly button$ = combineLatest([
|
||||
inject(ClientStorageService).showDevTools$,
|
||||
inject<PatchDB<DataModel>>(PatchDB)
|
||||
@@ -133,17 +160,14 @@ export class SideloadPackageComponent {
|
||||
file!: File
|
||||
|
||||
async upload() {
|
||||
const loader = this.loader.open('Uploading package').subscribe()
|
||||
const loader = this.loader.open('Starting upload').subscribe()
|
||||
|
||||
try {
|
||||
const { upload } = await this.api.sideloadPackage()
|
||||
const { upload, progress } = await this.api.sideloadPackage()
|
||||
|
||||
await this.api.uploadPackage(upload, this.file).catch(console.error)
|
||||
await this.router.navigate(['/portal/service', this.package.id])
|
||||
|
||||
this.alerts
|
||||
.open('Package uploaded successfully', { appearance: 'positive' })
|
||||
.subscribe()
|
||||
this.sideloadService.followProgress(progress)
|
||||
this.api.uploadPackage(upload, this.file).catch(console.error)
|
||||
await firstValueFrom(this.progress$.pipe(filter(Boolean)))
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { inject, Injectable } from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { endWith, ReplaySubject, shareReplay, Subject, switchMap } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
@@ -7,25 +7,16 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SideloadService {
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly guid$ = new Subject<string>()
|
||||
|
||||
readonly websocketConnected$ = new ReplaySubject()
|
||||
|
||||
readonly progress$ = this.guid$.pipe(
|
||||
switchMap(guid =>
|
||||
this.api
|
||||
.openWebsocket$<T.FullProgress>(guid, {
|
||||
openObserver: {
|
||||
next: () => this.websocketConnected$.next(''),
|
||||
},
|
||||
})
|
||||
.pipe(endWith(null)),
|
||||
this.api.openWebsocket$<T.FullProgress>(guid).pipe(endWith(null)),
|
||||
),
|
||||
shareReplay(1),
|
||||
)
|
||||
|
||||
constructor(private readonly api: ApiService) {}
|
||||
|
||||
followProgress(guid: string) {
|
||||
this.guid$.next(guid)
|
||||
}
|
||||
@@ -4,13 +4,13 @@ import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { TUI_CONFIRM } from '@taiga-ui/kit'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { filter } from 'rxjs'
|
||||
import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import {
|
||||
ActionInputModal,
|
||||
PackageActionData,
|
||||
} from '../modals/action-input.component'
|
||||
} from 'src/app/routes/portal/routes/service/modals/action-input.component'
|
||||
import { ActionSuccessPage } from 'src/app/routes/portal/routes/service/modals/action-success/action-success.page'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
|
||||
const allowedStatuses = {
|
||||
'only-running': new Set(['running']),
|
||||
@@ -98,11 +98,7 @@ export class ActionService {
|
||||
}
|
||||
}
|
||||
|
||||
async execute(
|
||||
packageId: string,
|
||||
actionId: string,
|
||||
input?: object,
|
||||
): Promise<boolean> {
|
||||
async execute(packageId: string, actionId: string, input?: object) {
|
||||
const loader = this.loader.open('Loading...').subscribe()
|
||||
|
||||
try {
|
||||
@@ -112,7 +108,7 @@ export class ActionService {
|
||||
input: input || null,
|
||||
})
|
||||
|
||||
if (!res) return true
|
||||
if (!res) return
|
||||
|
||||
if (res.result) {
|
||||
this.dialogs
|
||||
@@ -124,10 +120,8 @@ export class ActionService {
|
||||
} else if (res.message) {
|
||||
this.dialogs.open(res.message, { label: res.title }).subscribe()
|
||||
}
|
||||
return true // needed to dismiss original modal/alert
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
return false // don't dismiss original modal/alert
|
||||
} finally {
|
||||
loader.unsubscribe()
|
||||
}
|
||||
|
||||
@@ -5,13 +5,7 @@ import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core'
|
||||
import { TuiConfirmData, TUI_CONFIRM } from '@taiga-ui/kit'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { defaultIfEmpty, filter, firstValueFrom } from 'rxjs'
|
||||
// @TODO Alex implement config
|
||||
// import {
|
||||
// ConfigModal,
|
||||
// PackageConfigData,
|
||||
// } from 'src/app/routes/portal/modals/config.component'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { getAllPackages } from 'src/app/utils/get-package-data'
|
||||
import { hasCurrentDeps } from 'src/app/utils/has-deps'
|
||||
@@ -19,21 +13,13 @@ import { hasCurrentDeps } from 'src/app/utils/has-deps'
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ActionsService {
|
||||
export class ControlsService {
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly formDialog = inject(FormDialogService)
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
|
||||
configure(manifest: T.Manifest): void {
|
||||
// this.formDialog.open<PackageConfigData>(ConfigModal, {
|
||||
// label: `${manifest.title} configuration`,
|
||||
// data: { pkgId: manifest.id },
|
||||
// })
|
||||
}
|
||||
|
||||
async start(manifest: T.Manifest, unmet: boolean): Promise<void> {
|
||||
const deps = `${manifest.title} has unmet dependencies. It will not work as expected.`
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { inject, Injectable } from '@angular/core'
|
||||
import { ErrorService } from '@start9labs/shared'
|
||||
import { ErrorService, MARKDOWN } from '@start9labs/shared'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { firstValueFrom, merge, shareReplay, Subject } from 'rxjs'
|
||||
import { REPORT } from 'src/app/components/report.component'
|
||||
import {
|
||||
ServerNotification,
|
||||
ServerNotifications,
|
||||
} from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { REPORT } from 'src/app/components/report.component'
|
||||
import { firstValueFrom, merge, shareReplay, Subject } from 'rxjs'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -89,19 +89,19 @@ export class NotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
viewFull(notification: ServerNotification<number>) {
|
||||
this.dialogs
|
||||
.open(notification.message, { label: notification.title })
|
||||
.subscribe()
|
||||
}
|
||||
viewModal(
|
||||
{ data, createdAt, code, title, message }: ServerNotification<number>,
|
||||
full = false,
|
||||
) {
|
||||
const label = full || code === 2 ? title : 'Backup Report'
|
||||
const content = code === 1 ? REPORT : MARKDOWN
|
||||
|
||||
viewReport(notification: ServerNotification<number>) {
|
||||
this.dialogs
|
||||
.open(REPORT, {
|
||||
label: 'Backup Report',
|
||||
.open(full ? message : content, {
|
||||
label,
|
||||
data: {
|
||||
report: notification.data,
|
||||
timestamp: notification.createdAt,
|
||||
content: data,
|
||||
timestamp: createdAt,
|
||||
},
|
||||
})
|
||||
.subscribe()
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { hasCurrentDeps } from '../util/has-deps'
|
||||
import { getAllPackages } from '../util/get-package-data'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { DataModel } from './patch-db/data-model'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from './api/embassy-api.service'
|
||||
import { inject, Injectable } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { TUI_CONFIRM } from '@taiga-ui/kit'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { filter } from 'rxjs'
|
||||
import { getAllPackages } from '../utils/get-package-data'
|
||||
import { hasCurrentDeps } from '../utils/has-deps'
|
||||
import { ApiService } from './api/embassy-api.service'
|
||||
import { DataModel } from './patch-db/data-model'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class StandardActionsService {
|
||||
constructor(
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly api: ApiService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly errorService: ErrorService,
|
||||
private readonly loader: LoadingService,
|
||||
private readonly navCtrl: NavController,
|
||||
) {}
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly router = inject(Router)
|
||||
|
||||
async rebuild(id: string) {
|
||||
const loader = this.loader.open(`Rebuilding Container...`).subscribe()
|
||||
|
||||
try {
|
||||
await this.api.rebuildPackage({ id })
|
||||
this.navCtrl.navigateBack('/services/' + id)
|
||||
await this.router.navigate(['portal', 'services', id])
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
@@ -34,48 +35,38 @@ export class StandardActionsService {
|
||||
}
|
||||
}
|
||||
|
||||
async tryUninstall(manifest: T.Manifest): Promise<void> {
|
||||
const { id, title, alerts } = manifest
|
||||
|
||||
let message =
|
||||
async uninstall({ id, title, alerts }: T.Manifest): Promise<void> {
|
||||
let content =
|
||||
alerts.uninstall ||
|
||||
`Uninstalling ${title} will permanently delete its data`
|
||||
|
||||
if (hasCurrentDeps(id, await getAllPackages(this.patch))) {
|
||||
message = `${message}. Services that depend on ${title} will no longer work properly and may crash`
|
||||
content = `${content}. Services that depend on ${title} will no longer work properly and may crash`
|
||||
}
|
||||
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
message,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
this.dialogs
|
||||
.open(TUI_CONFIRM, {
|
||||
label: 'Warning',
|
||||
size: 's',
|
||||
data: {
|
||||
content,
|
||||
yes: 'Uninstall',
|
||||
no: 'Cancel',
|
||||
},
|
||||
{
|
||||
text: 'Uninstall',
|
||||
handler: () => {
|
||||
this.uninstall(id)
|
||||
},
|
||||
cssClass: 'enter-click',
|
||||
},
|
||||
],
|
||||
cssClass: 'alert-warning-message',
|
||||
})
|
||||
|
||||
await alert.present()
|
||||
})
|
||||
.pipe(filter(Boolean))
|
||||
.subscribe(() => this.doUninstall(id))
|
||||
}
|
||||
|
||||
private async uninstall(id: string) {
|
||||
private async doUninstall(id: string) {
|
||||
const loader = this.loader.open(`Beginning uninstall...`).subscribe()
|
||||
|
||||
try {
|
||||
await this.api.uninstallPackage({ id })
|
||||
this.api
|
||||
await this.api
|
||||
.setDbValue<boolean>(['ackInstructions', id], false)
|
||||
.catch(e => console.error('Failed to mark instructions as unseen', e))
|
||||
this.navCtrl.navigateRoot('/services')
|
||||
await this.router.navigate(['portal'])
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
|
||||
@@ -41,7 +41,7 @@ export function isRemoving(
|
||||
}
|
||||
|
||||
export function isInstalling(
|
||||
pkg: PackageDataEntry,
|
||||
pkg: T.PackageDataEntry,
|
||||
): pkg is PackageDataEntry<InstallingState> {
|
||||
return pkg.stateInfo.state === 'installing'
|
||||
}
|
||||
|
||||
@@ -205,20 +205,20 @@ button.g-action {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.g-success.g-success {
|
||||
color: var(--tui-status-positive);
|
||||
.g-success {
|
||||
color: var(--tui-status-positive) !important;
|
||||
}
|
||||
|
||||
.g-warning.g-warning {
|
||||
color: var(--tui-status-warning);
|
||||
.g-warning {
|
||||
color: var(--tui-status-warning) !important;
|
||||
}
|
||||
|
||||
.g-error.g-error {
|
||||
color: var(--tui-status-negative);
|
||||
color: var(--tui-status-negative) !important;
|
||||
}
|
||||
|
||||
.g-info.g-info {
|
||||
color: var(--tui-status-info);
|
||||
.g-info {
|
||||
color: var(--tui-status-info) !important;
|
||||
}
|
||||
|
||||
ng-component {
|
||||
|
||||
Reference in New Issue
Block a user