mirror of
https://github.com/gurucomputing/headscale-ui.git
synced 2026-01-23 02:34:43 +00:00
Merge pull request #199 from gurucomputing:save-server-settings
Save-server-settings
This commit is contained in:
commit
efe2aee09f
15 changed files with 474 additions and 244 deletions
257
package-lock.json
generated
257
package-lock.json
generated
|
|
@ -607,9 +607,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
|
||||
"integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz",
|
||||
"integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -621,9 +621,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz",
|
||||
"integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz",
|
||||
"integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -635,9 +635,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz",
|
||||
"integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz",
|
||||
"integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -649,9 +649,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz",
|
||||
"integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz",
|
||||
"integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -663,9 +663,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz",
|
||||
"integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz",
|
||||
"integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -677,9 +677,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz",
|
||||
"integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz",
|
||||
"integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -691,9 +691,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz",
|
||||
"integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz",
|
||||
"integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -705,9 +705,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz",
|
||||
"integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz",
|
||||
"integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -719,9 +719,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz",
|
||||
"integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz",
|
||||
"integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -733,9 +733,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz",
|
||||
"integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz",
|
||||
"integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -747,9 +747,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz",
|
||||
"integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz",
|
||||
"integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
|
|
@ -761,9 +761,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz",
|
||||
"integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz",
|
||||
"integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
|
@ -775,9 +775,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz",
|
||||
"integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz",
|
||||
"integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
|
|
@ -789,9 +789,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz",
|
||||
"integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz",
|
||||
"integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
|
|
@ -803,9 +803,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz",
|
||||
"integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz",
|
||||
"integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -817,9 +817,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz",
|
||||
"integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz",
|
||||
"integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -831,9 +831,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz",
|
||||
"integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz",
|
||||
"integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -845,9 +845,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz",
|
||||
"integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz",
|
||||
"integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -859,9 +859,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz",
|
||||
"integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz",
|
||||
"integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -896,25 +896,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.15.2.tgz",
|
||||
"integrity": "sha512-p208T1kdM6zd8k4YXIUM60pLWQ8dZqehXSiqn4NulXHyHibX53uIAL2xtNL8GjxX2IVPqPRT978MwVYhCKExdQ==",
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.17.1.tgz",
|
||||
"integrity": "sha512-CpoGSLqE2MCmcQwA2CWJvOsZ9vW+p/1H3itrFykdgajUNAEyQPbsaSn7fZb6PLHQwe+07njxje9ss0fjZoCAyw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.6.0",
|
||||
"cookie": "^0.6.0",
|
||||
"devalue": "^5.1.0",
|
||||
"esm-env": "^1.2.1",
|
||||
"esm-env": "^1.2.2",
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"kleur": "^4.1.5",
|
||||
"magic-string": "^0.30.5",
|
||||
"mrmime": "^2.0.0",
|
||||
"sade": "^1.8.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"sirv": "^3.0.0",
|
||||
"tiny-glob": "^0.2.9"
|
||||
"sirv": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"svelte-kit": "svelte-kit.js"
|
||||
|
|
@ -1243,9 +1241,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001692",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
|
||||
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
|
||||
"version": "1.0.30001699",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz",
|
||||
"integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1455,9 +1453,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.82",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.82.tgz",
|
||||
"integrity": "sha512-Zq16uk1hfQhyGx5GpwPAYDwddJuSGhtRhgOA2mCxANYaDT79nAeGnaXogMGng4KqLaJUVnOnuL0+TDop9nLOiA==",
|
||||
"version": "1.5.97",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.97.tgz",
|
||||
"integrity": "sha512-HKLtaH02augM7ZOdYRuO19rWDeY+QSJ1VxnXFa/XDFLf07HvM90pALIJFgrO+UVaajI3+aJMMpojoUTLZyQ7JQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
|
@ -1574,9 +1572,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
||||
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
|
||||
"integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
|
@ -1584,9 +1582,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz",
|
||||
"integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==",
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
|
|
@ -1701,20 +1699,6 @@
|
|||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/globalyzer": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
|
||||
"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/globrex": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
|
||||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
|
|
@ -2181,9 +2165,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
|
||||
"integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
|
||||
"version": "8.5.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
|
||||
"integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2345,9 +2329,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.0.tgz",
|
||||
"integrity": "sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
|
@ -2372,9 +2356,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-tailwindcss": {
|
||||
"version": "0.6.9",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.9.tgz",
|
||||
"integrity": "sha512-r0i3uhaZAXYP0At5xGfJH876W3HHGHDp+LCRUJrs57PBeQ6mYHMwr25KH8NPX44F2yGTvdnH7OqCshlQx183Eg==",
|
||||
"version": "0.6.11",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.11.tgz",
|
||||
"integrity": "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -2385,7 +2369,7 @@
|
|||
"@prettier/plugin-pug": "*",
|
||||
"@shopify/prettier-plugin-liquid": "*",
|
||||
"@trivago/prettier-plugin-sort-imports": "*",
|
||||
"@zackad/prettier-plugin-twig-melody": "*",
|
||||
"@zackad/prettier-plugin-twig": "*",
|
||||
"prettier": "^3.0",
|
||||
"prettier-plugin-astro": "*",
|
||||
"prettier-plugin-css-order": "*",
|
||||
|
|
@ -2412,7 +2396,7 @@
|
|||
"@trivago/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@zackad/prettier-plugin-twig-melody": {
|
||||
"@zackad/prettier-plugin-twig": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-astro": {
|
||||
|
|
@ -2528,9 +2512,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.30.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
|
||||
"integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==",
|
||||
"version": "4.34.6",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz",
|
||||
"integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -2544,25 +2528,25 @@
|
|||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.30.1",
|
||||
"@rollup/rollup-android-arm64": "4.30.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.30.1",
|
||||
"@rollup/rollup-darwin-x64": "4.30.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.30.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.30.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.30.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.30.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.30.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.30.1",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.30.1",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.30.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.30.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.30.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.30.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.30.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.30.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.30.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.30.1",
|
||||
"@rollup/rollup-android-arm-eabi": "4.34.6",
|
||||
"@rollup/rollup-android-arm64": "4.34.6",
|
||||
"@rollup/rollup-darwin-arm64": "4.34.6",
|
||||
"@rollup/rollup-darwin-x64": "4.34.6",
|
||||
"@rollup/rollup-freebsd-arm64": "4.34.6",
|
||||
"@rollup/rollup-freebsd-x64": "4.34.6",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.34.6",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.34.6",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.34.6",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.34.6",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.34.6",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.34.6",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.34.6",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.34.6",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.34.6",
|
||||
"@rollup/rollup-linux-x64-musl": "4.34.6",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.34.6",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.34.6",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.34.6",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
|
|
@ -2822,9 +2806,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.18.0.tgz",
|
||||
"integrity": "sha512-/Eb81lB8bVUxQPmkPVNBYrU9cZ544+9hE91ZUUXTMf7eWcGW84N1hS3gvv/XsUNOWLLg3IicXP2qa8W3KpTUHA==",
|
||||
"version": "5.19.9",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.19.9.tgz",
|
||||
"integrity": "sha512-860s752/ZZxHIsii31ELkdKBOCeAuDsfb/AGUXJyQyzUVLRSt4oqEw/BV5+2+mNg8mbqmD3OK+vMvwWMPM6f8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -3010,17 +2994,6 @@
|
|||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-glob": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
|
||||
"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"globalyzer": "0.1.0",
|
||||
"globrex": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
|
@ -3104,15 +3077,15 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz",
|
||||
"integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz",
|
||||
"integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.24.2",
|
||||
"postcss": "^8.4.49",
|
||||
"rollup": "^4.23.0"
|
||||
"postcss": "^8.5.1",
|
||||
"rollup": "^4.30.1"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
|
|
|
|||
52
src/lib/components/common/classes.svelte
Normal file
52
src/lib/components/common/classes.svelte
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<script module lang="ts">
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
export class PersistentAppSettingsObject {
|
||||
daisyUITheme = ''; // for setting the UI theme. See https://daisyui.com/docs/themes/
|
||||
headscaleAPIKey = ''; // sensitive, allows for administrative access to headscale
|
||||
headscaleURL = ''; // url for headscale to use
|
||||
debugLogging = false; // to turn on additional messages
|
||||
|
||||
public constructor(init?: Partial<PersistentAppSettingsObject>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppSettingsObject {
|
||||
navbarTitle = ''; // for setting the title of the page
|
||||
appLoaded = false; // for hiding the screen until hydration has completed
|
||||
sidebarDrawerOpen = false; // for determining if the sidebar is open when on a small screen
|
||||
toastAlerts = new SvelteMap<string, toastAlert>(); // for adding or removing alerts
|
||||
apiTested = true; // used to hide the app if the api tests are failing
|
||||
apiKeyList: APIKey[] = []; //list of apikeys retrieved from headscale API
|
||||
apiKeyExpiration?: number = undefined; // number of days left until the key in use expires
|
||||
|
||||
public constructor(init?: Partial<AppSettingsObject>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
// alert used for populating toasts in the layout
|
||||
export class toastAlert {
|
||||
message = ''; //message to display
|
||||
notificationType = 'alert'; //to style the toast
|
||||
id = ''; //UUID generated to reference the toast
|
||||
|
||||
public constructor(init?: Partial<toastAlert>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
// retrieved as an array from headscale
|
||||
export class APIKey {
|
||||
id = ''; // unique identifier for headscale
|
||||
prefix = ''; // beginning of key to match full string
|
||||
expiration = ''; // when key expires, formatting as datetime
|
||||
createdAt = ''; // date of creation
|
||||
lastSeen = ''; // date last seen, seems to be always null?
|
||||
|
||||
public constructor(init?: Partial<APIKey>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
export class PersistentAppSettingsObject {
|
||||
daisyUITheme = "" // for setting the UI theme. See https://daisyui.com/docs/themes/
|
||||
headscaleAPIKey = "" // sensitive, allows for administrative access to headscale
|
||||
headscaleURL = "" //url for headscale to use
|
||||
|
||||
public constructor(init?: Partial<PersistentAppSettingsObject>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppSettingsObject {
|
||||
navbarTitle = "" // for setting the title of the page
|
||||
appLoaded = false // for hiding the screen until hydration has completed
|
||||
sidebarDrawerOpen = false // for determining if the sidebar is open when on a small screen
|
||||
toastAlerts: toastAlert[] = [] // for adding or removing alerts
|
||||
|
||||
public constructor(init?: Partial<AppSettingsObject>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
// alert used for populating toasts in the layout
|
||||
export class toastAlert {
|
||||
message = "" //message to display
|
||||
notificationType = "alert" //to style the toast
|
||||
id = "" //UUID generated to reference the toast
|
||||
|
||||
public constructor(init?: Partial<toastAlert>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
// defines global state objects for the application
|
||||
var appSettingsInitializer = new AppSettingsObject();
|
||||
export let appSettings = $state(Object.assign({}, appSettingsInitializer));
|
||||
|
||||
|
||||
// defines global state objects that get written to localStorage for persistence
|
||||
var persistentAppSettingsInitializer = new PersistentAppSettingsObject();
|
||||
export let persistentAppSettings = $state(Object.assign({}, persistentAppSettingsInitializer));
|
||||
|
|
|
|||
15
src/lib/components/layout/toast-functions.svelte
Normal file
15
src/lib/components/layout/toast-functions.svelte
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script module lang="ts">
|
||||
import { appSettings } from '../common/state.svelte';
|
||||
import { toastAlert } from '../common/classes.svelte';
|
||||
|
||||
export function newToastAlert(message: string) {
|
||||
let uuid = crypto.randomUUID();
|
||||
appSettings.toastAlerts.set(
|
||||
uuid,
|
||||
new toastAlert({
|
||||
id: uuid,
|
||||
message: message
|
||||
})
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,40 +1,56 @@
|
|||
<!-- used to generate alerts or messages -->
|
||||
<script lang="ts">
|
||||
import { appSettings } from '$lib/components/common/state.svelte';
|
||||
let { toastAlert } = $props();
|
||||
import { toastAlert } from '../common/classes.svelte';
|
||||
interface Props {
|
||||
toast: toastAlert;
|
||||
}
|
||||
|
||||
let progress = $state(100);
|
||||
let { toast }: Props = $props();
|
||||
|
||||
// sets a timer and removes the alert after totalTime miliseconds
|
||||
const intervalTime = 20; // 20ms
|
||||
const totalTime = 4000; // 4 seconds
|
||||
const decrement = 100 / (totalTime / intervalTime);
|
||||
async function delayedClose(id: string) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 4000));
|
||||
removeAlert(id);
|
||||
}
|
||||
delayedClose(toast.id);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
progress -= decrement;
|
||||
if (progress <= 0) {
|
||||
progress = 0;
|
||||
removeAlert();
|
||||
}
|
||||
}, intervalTime);
|
||||
|
||||
function removeAlert() {
|
||||
clearInterval(interval); // Stop the interval at 0
|
||||
appSettings.toastAlerts = appSettings.toastAlerts.filter(function (returnObj) {
|
||||
return returnObj.id !== toastAlert.id;
|
||||
});
|
||||
function removeAlert(id: string) {
|
||||
appSettings.toastAlerts.delete(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="alert flex justify-between min-w-60 text-wrap">
|
||||
<div>{toastAlert.message}</div>
|
||||
<div>
|
||||
<button aria-label="close notification" class="mask mask-circle hover:bg-base-300" onclick={() => removeAlert()}>
|
||||
<div class="radial-progress" style="--value:{progress}; --size:2rem; --thickness: 2px;" role="progressbar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="alert flex min-w-60 justify-between text-wrap">
|
||||
<div>{toast.message}</div>
|
||||
<div>
|
||||
<button aria-label="close notification" class="mask mask-circle hover:bg-base-300" onclick={() => removeAlert(toast.id)}>
|
||||
<div class="animate-value radial-progress" style="--value:var(--_value); --size:2rem; --thickness: 2px;" role="progressbar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* used to animate the timeout */
|
||||
@property --_value {
|
||||
syntax: '<number>';
|
||||
inherits: true;
|
||||
initial-value: 0;
|
||||
}
|
||||
.animate-value {
|
||||
animation-name: grow;
|
||||
animation-duration: 4s;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
@keyframes grow {
|
||||
from {
|
||||
--_value: 0;
|
||||
}
|
||||
to {
|
||||
--_value: 100;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
141
src/lib/components/settings/server-settings-functions.svelte
Normal file
141
src/lib/components/settings/server-settings-functions.svelte
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<script module lang="ts">
|
||||
import { appSettings, persistentAppSettings } from '../common/state.svelte';
|
||||
import { newToastAlert } from '../layout/toast-functions.svelte';
|
||||
|
||||
export async function getAPIKeys() {
|
||||
try {
|
||||
const response = await fetch(`${persistentAppSettings.headscaleURL}/api/v1/apikey`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${persistentAppSettings.headscaleAPIKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
newToastAlert(`API test failed (check your server settings): ${response.status}`);
|
||||
appSettings.apiTested = false;
|
||||
} else {
|
||||
appSettings.apiKeyList = (await response.json()).apiKeys;
|
||||
appSettings.apiTested = true;
|
||||
|
||||
// determine the remaining time for the key we are currently using.
|
||||
for (const key of appSettings.apiKeyList) {
|
||||
if (persistentAppSettings.headscaleAPIKey.startsWith(key.prefix)) {
|
||||
getKeyRemainingTime(new Date(key.expiration));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
let message: string;
|
||||
if (error instanceof Error) {
|
||||
message = error.message;
|
||||
} else {
|
||||
message = String(error);
|
||||
}
|
||||
newToastAlert(`API test failed (check your server settings): ${message}`);
|
||||
appSettings.apiTested = false;
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyRemainingTime(expiration: Date) {
|
||||
let currentTime = new Date();
|
||||
// gets time difference in seconds
|
||||
appSettings.apiKeyExpiration = Math.round((expiration.getTime() - currentTime.getTime()) / 1000 / 60 / 60 / 24);
|
||||
if (appSettings.apiKeyExpiration < 30) {
|
||||
newToastAlert(`${appSettings.apiKeyExpiration} days left before API Key expiry, consider rolling your key`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function rotateAPIKey() {
|
||||
for (const key of appSettings.apiKeyList) {
|
||||
// select the current key being used in the app settings
|
||||
if (persistentAppSettings.headscaleAPIKey.startsWith(key.prefix)) {
|
||||
let currentKey = key;
|
||||
|
||||
// generate a new expiration time 90 days in the future
|
||||
let newExpiration = new Date();
|
||||
newExpiration.setDate(newExpiration.getDate() + 90);
|
||||
|
||||
// create a new API key with the new expiration
|
||||
let apiKey = await createNewAPIKey(newExpiration);
|
||||
|
||||
// The above should always return a value, let's check that
|
||||
if (apiKey == undefined) {
|
||||
throw new Error('expecting API key string, string was undefined');
|
||||
}
|
||||
|
||||
// Set the new key as the current key in the persistent settings
|
||||
persistentAppSettings.headscaleAPIKey = apiKey;
|
||||
|
||||
// Expire the previously current key
|
||||
await expireAPIKey(currentKey.prefix);
|
||||
|
||||
// Get keys again to make sure it all worked
|
||||
await getAPIKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function createNewAPIKey(expireDate: Date) {
|
||||
try {
|
||||
const response = await fetch(`${persistentAppSettings.headscaleURL}/api/v1/apikey`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${persistentAppSettings.headscaleAPIKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
expiration: expireDate.toISOString()
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
newToastAlert(`Creating new API Key Failed (check your server settings): ${response.status}`);
|
||||
appSettings.apiTested = false;
|
||||
} else {
|
||||
let apiKey = '';
|
||||
apiKey = (await response.json()).apiKey;
|
||||
return apiKey;
|
||||
}
|
||||
} catch (error) {
|
||||
let message: string;
|
||||
if (error instanceof Error) {
|
||||
message = error.message;
|
||||
} else {
|
||||
message = String(error);
|
||||
}
|
||||
newToastAlert(`API Call Failed (check your server settings): ${message}`);
|
||||
appSettings.apiTested = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function expireAPIKey(apiPrefix: string) {
|
||||
try {
|
||||
const response = await fetch(`${persistentAppSettings.headscaleURL}/api/v1/apikey/expire`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${persistentAppSettings.headscaleAPIKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prefix: apiPrefix
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
newToastAlert(`API test failed (check your server settings): ${response.status}`);
|
||||
appSettings.apiTested = false;
|
||||
}
|
||||
} catch (error) {
|
||||
let message: string;
|
||||
if (error instanceof Error) {
|
||||
message = error.message;
|
||||
} else {
|
||||
message = String(error);
|
||||
}
|
||||
newToastAlert(`API test failed (check your server settings): ${message}`);
|
||||
appSettings.apiTested = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,15 +1,73 @@
|
|||
<script lang="ts">
|
||||
import { appSettings, persistentAppSettings } from '$lib/components/common/state.svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { getAPIKeys, rotateAPIKey } from './server-settings-functions.svelte';
|
||||
|
||||
let apiSecretHidden = $state(true); // for hiding or showing the API key
|
||||
let rotateButtonDisabled = $state(false);
|
||||
|
||||
function rotateAPIKeyClick() {
|
||||
rotateButtonDisabled = true;
|
||||
rotateAPIKey().then(() => {
|
||||
rotateButtonDisabled = false;
|
||||
// console.log(appSettings.apiKeyList);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-control">
|
||||
<h1 class="bold mb-4 text-xl text-primary">Server Settings</h1>
|
||||
|
||||
<label class="mb-2 block font-bold text-secondary" for="url"> Headscale URL </label>
|
||||
<input class="input input-sm input-bordered w-full" type="url" placeholder="https://hs.yourdomain.com.au" />
|
||||
<label for="url" class="label">
|
||||
<span class="label-text-alt">URL for your headscale server instance (does not need populating if it's on the same subdomain)</span>
|
||||
</label>
|
||||
<label class="mb-2 block font-bold text-secondary" for="password"> Headscale API Key </label>
|
||||
<input class="input input-sm input-bordered w-full" minlength="54" maxlength="54" type="password" placeholder="******************" />
|
||||
<label for="url" class="label">
|
||||
<span class="label-text-alt">Generate an API key for your headscale instance and place it here.</span>
|
||||
</label>
|
||||
<button class="btn btn-sm btn-secondary w-16">save</button>
|
||||
<form id="server-settings" onsubmit={getAPIKeys}>
|
||||
<label class="mb-2 block font-bold text-secondary" for="headscaleURL"> Headscale URL </label>
|
||||
<input id="headscaleURL" bind:value={persistentAppSettings.headscaleURL} class="input input-sm input-bordered w-full" type="url" placeholder="https://hs.yourdomain.com.au" />
|
||||
<label for="headscaleURL" class="label">
|
||||
<span class="label-text-alt">URL for your headscale server instance (does not need populating if it's on the same subdomain)</span>
|
||||
</label>
|
||||
<div class="relative flex">
|
||||
<label class="mb-2 block font-bold text-secondary" for="headscaleKey"> Headscale API Key </label>
|
||||
{#if appSettings.apiKeyExpiration != undefined}
|
||||
<button type="button" disabled={rotateButtonDisabled} onclick={rotateAPIKeyClick} class="tooltip" data-tip="{appSettings.apiKeyExpiration} days left. Click to rotate key" aria-label="check time left"
|
||||
><svg data-slot="icon" fill="none" class="-my-3 ml-2 h-5 w-5 {appSettings.apiKeyExpiration >= 30 ? "stroke-success" : "stroke-error"}" stroke-width="2.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"></path>
|
||||
</svg></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="relative flex">
|
||||
<input id="headscaleKey" bind:value={persistentAppSettings.headscaleAPIKey} class="input input-sm input-bordered w-full" minlength="40" maxlength="40" type={apiSecretHidden ? 'password' : 'text'} required placeholder="******************" />
|
||||
<button
|
||||
type="button"
|
||||
class="ml-2"
|
||||
onclick={() => {
|
||||
apiSecretHidden = !apiSecretHidden;
|
||||
}}
|
||||
>{#if apiSecretHidden}
|
||||
<!-- eye off -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="my-1.5 h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" /></svg>
|
||||
{:else}
|
||||
<!-- eye on -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="my-1.5 h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<label for="headscaleKey" class="label">
|
||||
<span class="label-text-alt">Generate an API key for your headscale instance and place it here.</span>
|
||||
</label>
|
||||
</form>
|
||||
<span
|
||||
><button form="server-settings" class="btn btn-secondary btn-sm w-24"> Test API</button>
|
||||
{#if appSettings.apiTested}
|
||||
<svg in:fly|global={{ x: 10, duration: 600 }} xmlns="http://www.w3.org/2000/svg" class="inline h-6 w-6 fill-none stroke-success" viewBox="0 0 24 24" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
{:else}
|
||||
<svg in:fly|global={{ x: 10, duration: 600 }} data-slot="icon" fill="none" stroke-width="1.5" class="inline h-6 w-6 fill-none stroke-error" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></path>
|
||||
</svg>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
export function testAPIConnectivity() {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { persistentAppSettings } from '$lib/components/common/state.svelte';
|
||||
import { persistentAppSettings } from '$lib/components/common/state.svelte';
|
||||
import Svelecte from 'svelecte';
|
||||
let themeOptions = ['hsui', 'light', 'dark', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'synthwave', 'retro', 'cyberpunk', 'valentine', 'halloween', 'garden', 'forest', 'aqua', 'lofi', 'pastel', 'fantasy', 'wireframe', 'black', 'luxury', 'dracula', 'cmyk', 'autumn', 'business', 'acid', 'lemonade', 'night', 'coffee', 'winter', 'dim', 'nord', 'sunset'];
|
||||
</script>
|
||||
|
||||
<h1 class="bold mb-4 mt-4 text-xl text-primary">Application Theme</h1>
|
||||
<Svelecte name="daisyUITheme" options={['hsui', 'light', 'dark', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'synthwave', 'retro', 'cyberpunk', 'valentine', 'halloween', 'garden', 'forest', 'aqua', 'lofi', 'pastel', 'fantasy', 'wireframe', 'black', 'luxury', 'dracula', 'cmyk', 'autumn', 'business', 'acid', 'lemonade', 'night', 'coffee', 'winter', 'dim', 'nord', 'sunset']} bind:value={persistentAppSettings.daisyUITheme} />
|
||||
<Svelecte name="daisyUITheme" options={themeOptions} bind:value={persistentAppSettings.daisyUITheme} />
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { toastAlert, type PersistentAppSettingsObject } from '$lib/components/common/classes.svelte';
|
||||
import Toast from '$lib/components/layout/toast.svelte';
|
||||
import { type PersistentAppSettingsObject } from '$lib/components/common/classes.svelte';
|
||||
import { appSettings, persistentAppSettings } from '$lib/components/common/state.svelte';
|
||||
import Navbar from '$lib/components/layout/navbar.svelte';
|
||||
import Sidebar from '$lib/components/layout/sidebar.svelte';
|
||||
import { appSettings, persistentAppSettings } from '$lib/components/common/state.svelte';
|
||||
import Toast from '$lib/components/layout/toast.svelte';
|
||||
import { getAPIKeys } from '$lib/components/settings/server-settings-functions.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import '../app.css';
|
||||
|
|
@ -20,24 +21,24 @@
|
|||
localStorage.setItem('persistentAppSettings', JSON.stringify(persistentAppSettings));
|
||||
});
|
||||
|
||||
// populate any settings being passed through by url params
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let headscaleApiKeyParam = urlParams.get('apikey');
|
||||
let headscaleUrlParam = urlParams.get('url');
|
||||
|
||||
if (headscaleApiKeyParam) {
|
||||
persistentAppSettings.headscaleAPIKey = headscaleApiKeyParam;
|
||||
}
|
||||
if (headscaleUrlParam) {
|
||||
persistentAppSettings.headscaleURL = headscaleUrlParam;
|
||||
}
|
||||
|
||||
// perform an initial API test
|
||||
getAPIKeys();
|
||||
|
||||
// delay load until page is hydrated
|
||||
appSettings.appLoaded = true;
|
||||
|
||||
// alert test
|
||||
// appSettings.toastAlerts.push(
|
||||
// new toastAlert({
|
||||
// message: 'this is a test message',
|
||||
// id: crypto.randomUUID()
|
||||
// })
|
||||
// );
|
||||
|
||||
// appSettings.toastAlerts.push(
|
||||
// new toastAlert({
|
||||
// message: 'this is a test message too and super long and long and long',
|
||||
// id: crypto.randomUUID()
|
||||
// })
|
||||
// );
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if appSettings.appLoaded}
|
||||
|
|
@ -47,9 +48,9 @@
|
|||
<div class="drawer-content flex flex-col">
|
||||
<!-- toast content -->
|
||||
<div class="toast toast-center toast-top z-40">
|
||||
{#each appSettings.toastAlerts as toast}
|
||||
{#each appSettings.toastAlerts.entries() as [toastID, toastObject]}
|
||||
<div transition:fade={{ duration: 200 }}>
|
||||
<Toast toastAlert={toast}></Toast>
|
||||
<Toast toast={toastObject}></Toast>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { appSettings } from '$lib/components/common/state.svelte';
|
||||
import ServerSettings from '$lib/components/settings/server-settings.svelte';
|
||||
import ThemeSettings from '$lib/components/settings/theme-settings.svelte';
|
||||
import { appSettings } from '$lib/components/common/state.svelte';
|
||||
|
||||
appSettings.navbarTitle = 'Settings';
|
||||
appSettings.sidebarDrawerOpen = false;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ const config = {
|
|||
paths: {
|
||||
base: '/web'
|
||||
}
|
||||
},
|
||||
compilerOptions: {
|
||||
runes: true
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
export default config;
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
|
|
|
|||
|
|
@ -2,5 +2,8 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
|||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
plugins: [sveltekit()],
|
||||
server: {
|
||||
allowedHosts: true
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue