From f846436711763d3f2fa604f749d6ff83b3878604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=8E=E5=A4=A9=E4=BD=A0=E5=A5=BD?= Date: Mon, 9 Feb 2026 23:13:18 +0800 Subject: [PATCH 1/8] feat(i18n): Add internationalization support - Integrated svelte-i18n for localization. - Extracted hardcoded strings to locale files. - Added English (en) and Chinese Simplified (zh) localizations. --- package-lock.json | 705 +++++++++++++++++- package.json | 1 + src/lib/Navigation.svelte | 19 +- src/lib/States.svelte.ts | 4 + src/lib/cards/acl/GroupListCard.svelte | 9 +- src/lib/cards/acl/HostListCard.svelte | 5 +- src/lib/cards/acl/PolicyListCard.svelte | 51 +- src/lib/cards/acl/SshRuleListCard.svelte | 45 +- src/lib/cards/acl/TagOwnerListCard.svelte | 9 +- src/lib/cards/common/ItemCreatedAt.svelte | 3 +- src/lib/cards/common/ItemDelete.svelte | 13 +- src/lib/cards/common/ItemListName.svelte | 7 +- src/lib/cards/node/NodeCreate.svelte | 11 +- src/lib/cards/node/NodeExpiresAt.svelte | 3 +- src/lib/cards/node/NodeHostname.svelte | 3 +- src/lib/cards/node/NodeLastSeen.svelte | 5 +- src/lib/cards/node/NodeListCard.svelte | 3 +- src/lib/cards/node/NodeOwner.svelte | 5 +- .../cards/node/NodeRegistrationMethod.svelte | 11 +- src/lib/cards/node/NodeRoutes.svelte | 3 +- src/lib/cards/node/NodeTags.svelte | 9 +- src/lib/cards/node/NodeTileCard.svelte | 15 +- src/lib/cards/route/RouteListCard.svelte | 3 +- src/lib/cards/route/RouteTileCard.svelte | 3 +- src/lib/cards/user/UserCreate.svelte | 7 +- src/lib/cards/user/UserDisplayName.svelte | 3 +- src/lib/cards/user/UserEmail.svelte | 3 +- src/lib/cards/user/UserListCard.svelte | 3 +- src/lib/cards/user/UserListNodes.svelte | 3 +- src/lib/cards/user/UserListPreAuthKey.svelte | 9 +- src/lib/cards/user/UserListPreAuthKeys.svelte | 11 +- src/lib/cards/user/UserProvider.svelte | 5 +- src/lib/cards/user/UserTileCard.svelte | 7 +- src/lib/common/funcs.ts | 71 +- src/lib/i18n.ts | 25 + src/lib/locales/en.json | 164 ++++ src/lib/locales/zh.json | 237 ++++++ src/lib/page/PageHeader.svelte | 5 +- src/lib/parts/Tabbed.svelte | 5 +- src/routes/+layout.svelte | 15 +- src/routes/+page.svelte | 19 +- src/routes/acls/+page.svelte | 15 +- src/routes/deploy/+page.svelte | 81 +- src/routes/nodes/+page.svelte | 15 +- src/routes/routes/+page.svelte | 13 +- src/routes/settings/+page.svelte | 66 +- src/routes/users/+page.svelte | 13 +- 47 files changed, 1491 insertions(+), 249 deletions(-) create mode 100644 src/lib/i18n.ts create mode 100644 src/lib/locales/en.json create mode 100644 src/lib/locales/zh.json diff --git a/package-lock.json b/package-lock.json index 9d7e9e4..5462a54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "ipaddr.js": "^2.2.0", "js-xxhash": "^4.0.0", "json5": "^2.2.3", + "svelte-i18n": "^4.0.1", "svelte-jsoneditor": "^2.4.0", "unplugin-icons": "^22.1.0" }, @@ -799,6 +800,57 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.7.2", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", @@ -2601,6 +2653,22 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/cli-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", + "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.64", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2722,6 +2790,19 @@ "node": ">=4" } }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -2739,6 +2820,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -2760,7 +2847,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2888,6 +2974,58 @@ "dev": true, "license": "MIT" }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/esbuild": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", @@ -3149,6 +3287,21 @@ "dev": true, "license": "MIT" }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -3245,6 +3398,16 @@ "node": ">=0.10.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/expect-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", @@ -3255,6 +3418,15 @@ "node": ">=12.0.0" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3498,6 +3670,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "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==", + "license": "MIT" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3597,6 +3781,18 @@ "node": ">=0.8.19" } }, + "node_modules/intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -3678,6 +3874,12 @@ "node": ">=0.12.0" } }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, "node_modules/is-reference": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", @@ -3969,6 +4171,15 @@ "dev": true, "license": "ISC" }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -3991,6 +4202,25 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, + "node_modules/memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4073,7 +4303,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -4139,6 +4368,12 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "license": "MIT" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -4882,7 +5117,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, "license": "MIT", "dependencies": { "mri": "^1.1.0" @@ -5338,6 +5572,442 @@ "@floating-ui/dom": "^1.5.3" } }, + "node_modules/svelte-i18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-4.0.1.tgz", + "integrity": "sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==", + "license": "MIT", + "dependencies": { + "cli-color": "^2.0.3", + "deepmerge": "^4.2.2", + "esbuild": "^0.19.2", + "estree-walker": "^2", + "intl-messageformat": "^10.5.3", + "sade": "^1.8.1", + "tiny-glob": "^0.2.9" + }, + "bin": { + "svelte-i18n": "dist/cli.js" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/svelte-i18n/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, "node_modules/svelte-jsoneditor": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/svelte-jsoneditor/-/svelte-jsoneditor-2.4.0.tgz", @@ -5589,6 +6259,29 @@ "node": ">=0.8" } }, + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -5681,6 +6374,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 6540639..dba9ae4 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "ipaddr.js": "^2.2.0", "js-xxhash": "^4.0.0", "json5": "^2.2.3", + "svelte-i18n": "^4.0.1", "svelte-jsoneditor": "^2.4.0", "unplugin-icons": "^22.1.0" } diff --git a/src/lib/Navigation.svelte b/src/lib/Navigation.svelte index 772d1de..a1aa23a 100644 --- a/src/lib/Navigation.svelte +++ b/src/lib/Navigation.svelte @@ -14,6 +14,7 @@ import { onMount, type Component } from 'svelte'; import { page } from '$app/state'; import { App } from '$lib/States.svelte'; + import { _ } from 'svelte-i18n'; type NavigationProps = { labels?: boolean @@ -37,18 +38,18 @@ type Page = { path: string; - name: string; + nameKey: string; logo: Component; }; const allPages: Page[] = [ - { path: '/', name: 'Home', logo: RawMdiHome }, - { path: '/users', name: 'Users', logo: RawMdiPerson }, - { path: '/nodes', name: 'Nodes', logo: RawMdiDevices }, - { path: '/deploy', name: 'Deploy', logo: RawMdiHomeGroupPlus }, - { path: '/routes', name: 'Routes', logo: RawMdiRouter }, - { path: '/acls', name: 'ACLs', logo: RawMdiSecurity }, - { path: '/settings', name: 'Settings', logo: RawMdiSettings }, + { path: '/', nameKey: 'navigation.home', logo: RawMdiHome }, + { path: '/users', nameKey: 'navigation.users', logo: RawMdiPerson }, + { path: '/nodes', nameKey: 'navigation.nodes', logo: RawMdiDevices }, + { path: '/deploy', nameKey: 'navigation.deploy', logo: RawMdiHomeGroupPlus }, + { path: '/routes', nameKey: 'navigation.routes', logo: RawMdiRouter }, + { path: '/acls', nameKey: 'navigation.acls', logo: RawMdiSecurity }, + { path: '/settings', nameKey: 'navigation.settings', logo: RawMdiSettings }, ].filter((p) => p != undefined); const pages = $derived.by(() => App.hasValidApi ? allPages : allPages.slice(-1)); @@ -70,7 +71,7 @@ {#if labels} - {p.name} + {$_(p.nameKey)} {/if} diff --git a/src/lib/States.svelte.ts b/src/lib/States.svelte.ts index 022847e..8d411c1 100644 --- a/src/lib/States.svelte.ts +++ b/src/lib/States.svelte.ts @@ -7,6 +7,7 @@ import type { ToastStore } from '@skeletonlabs/skeleton'; import { apiGet } from './common/api'; import { arraysEqual, clone, toastError, toastWarning } from './common/funcs'; import { debug } from './common/debug'; +import { locale } from 'svelte-i18n'; export type LayoutStyle = 'tile' | 'list'; @@ -116,6 +117,9 @@ export class HeadscaleAdmin { } }) + // language settings + language = new StateLocal('locale', 'en') + // api info apiValid = $state(false); apiUrl = new StateLocal('apiUrl', ''); diff --git a/src/lib/cards/acl/GroupListCard.svelte b/src/lib/cards/acl/GroupListCard.svelte index c449b04..4d9ae49 100644 --- a/src/lib/cards/acl/GroupListCard.svelte +++ b/src/lib/cards/acl/GroupListCard.svelte @@ -6,6 +6,7 @@ import Delete from '$lib/parts/Delete.svelte'; import MultiSelect from '$lib/parts/MultiSelect.svelte'; import Text from '$lib/parts/Text.svelte'; + import { _ } from 'svelte-i18n'; import { App } from '$lib/States.svelte'; import { debug } from '$lib/common/debug'; @@ -47,7 +48,7 @@ try { if (groupName !== groupNameNew) { acl.renameGroup(groupName, groupNameNew); - toastSuccess(`Group renamed from '${groupName}' to '${groupNameNew}'`, ToastStore); + toastSuccess($_('cards.groupRenamedSuccess', { values: { oldName: groupName, newName: groupNameNew } }), ToastStore); groupName = groupNameNew; } return true; @@ -63,7 +64,7 @@ deleting = true; try { acl.deleteGroup(groupName); - toastSuccess(`Group '${groupName}' deleted`, ToastStore); + toastSuccess($_('cards.groupDeleted', { values: { name: groupName } }), ToastStore); } catch (e) { if (e instanceof Error) { toastError('', ToastStore, e); @@ -97,7 +98,7 @@ {#snippet children()}

- Members of + {$_('cards.membersOf')}
diff --git a/src/lib/cards/acl/HostListCard.svelte b/src/lib/cards/acl/HostListCard.svelte index 71b1e5c..11feadf 100644 --- a/src/lib/cards/acl/HostListCard.svelte +++ b/src/lib/cards/acl/HostListCard.svelte @@ -6,6 +6,7 @@ import Delete from '$lib/parts/Delete.svelte'; import { debug } from '$lib/common/debug'; import Text from '$lib/parts/Text.svelte'; + import { _ } from 'svelte-i18n'; const ToastStore = getToastStore(); @@ -74,7 +75,7 @@ function deleteHost() { try { acl.deleteHost(host.name); - toastSuccess(`Host '${host.name}' deleted`, ToastStore); + toastSuccess($_('cards.hostDeleted', { values: { name: host.name } }), ToastStore); } catch (e) { debug(e); if (e instanceof Error) { @@ -108,7 +109,7 @@ class="card p-4 variant-filled-warning text-center {popupShow ? '' : 'invisible'}" data-popup="popupHover-host-{hostName}" > -

Host '{hostName}' has the same name as a user.
Please rename the host.

+

{$_('cards.hostRenameWarning', { values: { hostName } })}

diff --git a/src/lib/cards/acl/PolicyListCard.svelte b/src/lib/cards/acl/PolicyListCard.svelte index 82d313b..ad9df85 100644 --- a/src/lib/cards/acl/PolicyListCard.svelte +++ b/src/lib/cards/acl/PolicyListCard.svelte @@ -5,6 +5,7 @@ import Delete from '$lib/parts/Delete.svelte'; import CardListContainer from '$lib/cards/CardListContainer.svelte'; import { debug } from '$lib/common/debug'; + import { _ } from 'svelte-i18n'; import RawMdiArrowUp from '~icons/mdi/chevron-up'; import RawMdiArrowDown from '~icons/mdi/chevron-down'; @@ -86,13 +87,13 @@ let tabSetSrc = $state(0) let tabSetDst = $state(0) - const tabs = [ - { name: "custom", title: "Custom", logo: RawMdiPencil }, - { name: "user", title: "User", logo: RawMdiTag }, - { name: "host", title: "Host", logo: RawMdiDevices }, - { name: "group", title: "Group", logo: RawMdiGroups }, - { name: "tag", title: "Tag", logo: RawMdiTag }, - ] + const tabs = $derived([ + { name: "custom", title: $_('cards.custom'), logo: RawMdiPencil }, + { name: "user", title: $_('cards.user'), logo: RawMdiTag }, + { name: "host", title: $_('cards.host'), logo: RawMdiDevices }, + { name: "group", title: $_('cards.group'), logo: RawMdiGroups }, + { name: "tag", title: $_('cards.tag'), logo: RawMdiTag }, + ]) const srcNewType = $derived(tabs[tabSetSrc].name) let srcNewHost = $state('') @@ -123,7 +124,7 @@ loading = true; try { acl.delPolicy(idx) - toastSuccess(`Policy #'${idx+1}' has been deleted`, ToastStore); + toastSuccess($_('cards.policyDeleted', { values: { number: idx+1 } }), ToastStore); } catch (e) { if (e instanceof Error) { toastError('', ToastStore, e); @@ -144,7 +145,7 @@ function addSrc(host: string) { if (host.length === 0) { - throw new Error("Invailid Host Provided") + throw new Error($_('cards.invalidHost')) } policy.src.push(host) @@ -152,7 +153,7 @@ function addDst(host: string, ports: string) { if (host.length === 0) { - throw new Error("Invailid Host Provided") + throw new Error($_('cards.invalidHost')) } if (policy.proto === "icmp") { @@ -168,11 +169,11 @@ const n = parseInt(p, 10) if (isNaN(n)) { - throw new Error("Invalid Port Number Provided") + throw new Error($_('cards.invalidPort')) } if (n < 1 || n > 65535) { - throw new Error("Invalid Port Number Provided") + throw new Error($_('cards.invalidPort')) } } @@ -222,7 +223,7 @@

- +

- Protocol: + {$_('cards.protocol')}

+ onclick={()=>{ policy.proto = undefined }}>{$_('cards.any')} + onclick={()=>{ policy.proto = "tcp" }}>{$_('cards.tcp')} + onclick={()=>{ policy.proto = "udp" }}>{$_('cards.udp')} + onclick={()=>{ policy.proto = "icmp" }}>{$_('cards.icmp')}

- Sources: + {$_('cards.sources')}

@@ -319,7 +320,7 @@ {/each}

- Destinations: + {$_('cards.destinations')}

diff --git a/src/lib/cards/acl/SshRuleListCard.svelte b/src/lib/cards/acl/SshRuleListCard.svelte index cd911b2..4bde4fa 100644 --- a/src/lib/cards/acl/SshRuleListCard.svelte +++ b/src/lib/cards/acl/SshRuleListCard.svelte @@ -7,6 +7,7 @@ import CardListContainer from '$lib/cards/CardListContainer.svelte'; import { debug } from '$lib/common/debug'; import { get } from 'svelte/store'; + import { _ } from 'svelte-i18n'; import RawMdiGroups from '~icons/mdi/account-group'; import RawMdiPencil from '~icons/mdi/pencil'; @@ -55,19 +56,19 @@ let deleting = $state(false); let tabSetSrc = $state(0) - const tabsSrc = [ - { name: "custom", title: "Custom", logo: RawMdiPencil }, - { name: "user", title: "User", logo: RawMdiTag }, - { name: "group", title: "Group", logo: RawMdiGroups }, - { name: "tag", title: "Tag", logo: RawMdiTag }, - ] + const tabsSrc = $derived([ + { name: "custom", title: $_('cards.custom'), logo: RawMdiPencil }, + { name: "user", title: $_('cards.user'), logo: RawMdiTag }, + { name: "group", title: $_('cards.group'), logo: RawMdiGroups }, + { name: "tag", title: $_('cards.tag'), logo: RawMdiTag }, + ]) let tabSetDst = $state(0) - const tabsDst = [ - { name: "custom", title: "Custom", logo: RawMdiPencil }, - { name: "user", title: "User", logo: RawMdiTag }, - { name: "tag", title: "Tag", logo: RawMdiTag }, - ] + const tabsDst = $derived([ + { name: "custom", title: $_('cards.custom'), logo: RawMdiPencil }, + { name: "user", title: $_('cards.user'), logo: RawMdiTag }, + { name: "tag", title: $_('cards.tag'), logo: RawMdiTag }, + ]) const srcNewType = $derived(tabsSrc[tabSetSrc].name) let srcNewHost = $state('') @@ -94,7 +95,7 @@ deleting = true; try { acl.delSshRule(idx) - toastSuccess(`SSH Rule #'${idx+1}' has been deleted`, ToastStore); + toastSuccess($_('cards.sshRuleDeleted', { values: { number: idx+1 } }), ToastStore); } catch (e) { if (e instanceof Error) { toastError('', ToastStore, e); @@ -115,7 +116,7 @@ function addSrc(host: string) { if (host.length === 0) { - throw new Error("Invailid Host Provided") + throw new Error($_('cards.invalidHost')) } rule.src.push(host) @@ -123,7 +124,7 @@ function addDst(host: string) { if (host.length === 0) { - throw new Error("Invailid Host Provided") + throw new Error($_('cards.invalidHost')) } rule.dst.push(host) @@ -134,11 +135,11 @@ } - + {#snippet children()}

- Sources: + {$_('cards.sources')}

@@ -204,7 +205,7 @@ {/each}

- Destinations: + {$_('cards.destinations')}

@@ -269,7 +270,7 @@ {/each}

- Usernames: + {$_('cards.usernames')}

- Owners of + {$_('cards.ownersOf')}
diff --git a/src/lib/cards/common/ItemCreatedAt.svelte b/src/lib/cards/common/ItemCreatedAt.svelte index 3a0b36a..a37caa2 100644 --- a/src/lib/cards/common/ItemCreatedAt.svelte +++ b/src/lib/cards/common/ItemCreatedAt.svelte @@ -1,6 +1,7 @@ - + {new Date(item.createdAt).toLocaleString('en-Gb', { minute: '2-digit', year: 'numeric', diff --git a/src/lib/cards/common/ItemDelete.svelte b/src/lib/cards/common/ItemDelete.svelte index d04f1f0..9ddb826 100644 --- a/src/lib/cards/common/ItemDelete.svelte +++ b/src/lib/cards/common/ItemDelete.svelte @@ -9,6 +9,7 @@ import { toastError, toastSuccess } from '$lib/common/funcs'; import Delete from '$lib/parts/Delete.svelte'; import { App } from '$lib/States.svelte'; + import { _ } from 'svelte-i18n'; type ItemDeleteProps = { item: Named, @@ -33,27 +34,27 @@ if (isUser(item)) { if (await deleteUser(item)) { - toastSuccess(`Deleted User "${name}" (ID: ${id})`, ToastStore); + toastSuccess($_('cards.deletedUser', { values: { name, id } }), ToastStore); DrawerStore.close() } else { - let msg = `Failed to Delete User "${name}" (${id}).`; + let msg = $_('cards.failedDeleteUser', { values: { name, id } }); if(App.nodes.value.some((node) => node.user.id === item.id)){ - msg += " Still has nodes." + msg += $_('cards.stillHasNodes'); } toastError(msg, ToastStore); } } if (isNode(item)) { if (await deleteNode(item)) { - toastSuccess(`Deleted machine "${name}" (${id})`, ToastStore); + toastSuccess($_('cards.deletedMachine', { values: { name, id } }), ToastStore); DrawerStore.close() } else { - toastError(`Failed to Delete Nachine "${name}" (${id})`, ToastStore); + toastError($_('cards.failedDeleteMachine', { values: { name, id } }), ToastStore); } } } - + diff --git a/src/lib/cards/common/ItemListName.svelte b/src/lib/cards/common/ItemListName.svelte index cd4b51e..b9909e9 100644 --- a/src/lib/cards/common/ItemListName.svelte +++ b/src/lib/cards/common/ItemListName.svelte @@ -6,6 +6,7 @@ import RawMdiRename from '~icons/mdi/rename'; import RawMdiCheckCircleOutline from '~icons/mdi/check-circle-outline'; import RawMdiCloseCircleOutline from '~icons/mdi/close-circle-outline'; + import { _ } from 'svelte-i18n'; import { renameNode, renameUser } from '$lib/common/api'; import { toastError, focus } from '$lib/common/funcs'; import { getToastStore } from '@skeletonlabs/skeleton'; @@ -32,7 +33,7 @@ const ToastStore = getToastStore(); - +
{#if showRename}
{#if showCreate} @@ -131,7 +132,7 @@ type="checkbox" bind:checked={checked.ephemeral} /> -

Ephemeral

+

{$_('cards.ephemeral')}

diff --git a/src/lib/cards/user/UserProvider.svelte b/src/lib/cards/user/UserProvider.svelte index e18e23b..2059e45 100644 --- a/src/lib/cards/user/UserProvider.svelte +++ b/src/lib/cards/user/UserProvider.svelte @@ -1,6 +1,7 @@ - - {user.provider || 'local'} + + {user.provider || $_('cards.local')} \ No newline at end of file diff --git a/src/lib/cards/user/UserTileCard.svelte b/src/lib/cards/user/UserTileCard.svelte index 3f4b2c9..72db0c0 100644 --- a/src/lib/cards/user/UserTileCard.svelte +++ b/src/lib/cards/user/UserTileCard.svelte @@ -6,6 +6,7 @@ import CardTileContainer from '../CardTileContainer.svelte'; import OnlineUserIndicator from '$lib/parts/OnlineUserIndicator.svelte'; import { App } from '$lib/States.svelte'; + import { _ } from 'svelte-i18n'; type UserTileCardProps = { user: User, @@ -25,20 +26,20 @@
- ID: {user.id} + {$_('common.id')}: {user.id}
{getUserDisplay(user)}
-
Created:
+
{$_('cards.created')}
{dateToStr(new Date(user.createdAt))}
-
Nodes:
+
{$_('cards.nodes')}
{nodeCount}
diff --git a/src/lib/common/funcs.ts b/src/lib/common/funcs.ts index 0ca9c2f..bf56545 100644 --- a/src/lib/common/funcs.ts +++ b/src/lib/common/funcs.ts @@ -4,6 +4,8 @@ import IPAddr from 'ipaddr.js'; import { debug } from './debug'; import DOMPurify from 'dompurify'; import type { Direction, Node, OnlineStatus, User } from './types'; +import { get } from 'svelte/store'; +import { locale as i18nLocale } from 'svelte-i18n'; import { App } from '$lib/States.svelte'; export function clone(item: T): T { @@ -98,7 +100,7 @@ export function getTime( export function getTimeDifferenceMessage(time1: number): string { const difference = getTimeDifference(time1, new Date().getTime()); - return difference.finite ? difference.message : 'Does Not Expire'; + return difference.finite ? difference.message : (safeIsZh() ? '不过期' : 'Does Not Expire'); } export function getTimeDifferenceColor(td: TimeDifference): string { @@ -114,7 +116,7 @@ export function getTimeDifference(time1: number, time2?: number): TimeDifference return { future: true, finite: false, - message: 'Does Not Expire', + message: safeIsZh() ? '不过期' : 'Does Not Expire', }; } @@ -131,24 +133,40 @@ export function getTimeDifference(time1: number, time2?: number): TimeDifference const weeks = Math.floor(days / 7); const months = Math.floor(weeks / 4); - if (months > 0) { - message = `${months} month${months == 1 ? '' : 's'}`; - } else if (weeks > 0) { - message = `${weeks} week${weeks == 1 ? '' : 's'}`; - } else if (days > 0) { - message = `${days} day${days == 1 ? '' : 's'}`; - } else if (hours > 0) { - message = `${hours} hour${hours == 1 ? '' : 's'}`; - } else if (minutes > 0) { - message = `${minutes} minute${minutes == 1 ? '' : 's'}`; + if (safeIsZh()) { + if (months > 0) { + message = `${months}个月`; + } else if (weeks > 0) { + message = `${weeks}周`; + } else if (days > 0) { + message = `${days}天`; + } else if (hours > 0) { + message = `${hours}小时`; + } else if (minutes > 0) { + message = `${minutes}分钟`; + } else { + message = `${seconds}秒`; + } } else { - message = `${seconds} second${seconds == 1 ? '' : 's'}`; + if (months > 0) { + message = `${months} month${months == 1 ? '' : 's'}`; + } else if (weeks > 0) { + message = `${weeks} week${weeks == 1 ? '' : 's'}`; + } else if (days > 0) { + message = `${days} day${days == 1 ? '' : 's'}`; + } else if (hours > 0) { + message = `${hours} hour${hours == 1 ? '' : 's'}`; + } else if (minutes > 0) { + message = `${minutes} minute${minutes == 1 ? '' : 's'}`; + } else { + message = `${seconds} second${seconds == 1 ? '' : 's'}`; + } } return { future: isFuture, finite: true, - message: message + ` ${isFuture ? 'from now' : 'ago'}`, + message: safeIsZh() ? `${message}${isFuture ? '后' : '前'}` : message + ` ${isFuture ? 'from now' : 'ago'}`, }; } @@ -157,16 +175,33 @@ export function dateToStr(d: Date | string) { d = new Date(d); } - return d.toLocaleString('en-US', { + if (safeIsZh()) { + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + const hour = String(d.getHours()).padStart(2, '0'); + const minute = String(d.getMinutes()).padStart(2, '0'); + return `${year}-${month}-${day} ${hour}:${minute}`; + } + + return d.toLocaleString('en-GB', { day: '2-digit', - month: '2-digit', + month: 'short', year: 'numeric', - hour: 'numeric', + hour: '2-digit', minute: '2-digit', - //hour12: false + hour12: false }); } +function safeIsZh(): boolean { + try { + return get(i18nLocale) === 'zh'; + } catch { + return false; + } +} + export function toastSuccess(message: string, toastStore: ToastStore) { message = DOMPurify.sanitize(message) toastStore.trigger({ diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts new file mode 100644 index 0000000..ef6565d --- /dev/null +++ b/src/lib/i18n.ts @@ -0,0 +1,25 @@ +import { browser } from '$app/environment'; +import { init, register, locale } from 'svelte-i18n'; + +const defaultLocale = 'en'; + +register('en', () => import('./locales/en.json')); +register('zh', () => import('./locales/zh.json')); + +const getInitialLocale = () => { + if (browser) { + const stored = window.localStorage.getItem('locale'); + if (stored && (stored === 'en' || stored === 'zh')) { + return stored; + } + } + return defaultLocale; +}; + +init({ + fallbackLocale: defaultLocale, + initialLocale: getInitialLocale() +}); + +// Set the locale immediately for SSR compatibility +locale.set(getInitialLocale()); diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json new file mode 100644 index 0000000..429fbf9 --- /dev/null +++ b/src/lib/locales/en.json @@ -0,0 +1,164 @@ +{ + "app": { + "title": "Headscale-Admin", + "github": "GitHub" + }, + "navigation": { + "home": "Home", + "users": "Users", + "nodes": "Nodes", + "routes": "Routes", + "acls": "ACLs", + "settings": "Settings", + "deploy": "Deploy" + }, + "settings": { + "title": "Settings", + "apiUrl": "API URL", + "apiKey": "API Key", + "apiUrlPlaceholder": "Enter API URL", + "apiKeyPlaceholder": "Enter your API Key", + "apiTtl": "API Refresh Interval (seconds)", + "theme": "Theme", + "language": "Language", + "debugging": "Console Debugging", + "save": "Save Settings", + "authorized": "Authorized", + "notAuthorized": "Not Authorized", + "expiresIn": "Expires in", + "checkingAuth": "Checking authorization...", + "savedSettings": "Saved Settings", + "logUsers": "Log Users", + "logNodes": "Log Nodes", + "logPreAuthKeys": "Log PreAuthKeys", + "logApiKeyInfo": "Log ApiKey Info", + "showApiKey": "Show API Key", + "hideApiKey": "Hide API Key", + "refreshApiKey": "Refresh API Key" + }, + "common": { + "close": "Close", + "cancel": "Cancel", + "confirm": "Confirm", + "delete": "Delete", + "edit": "Edit", + "save": "Save", + "create": "Create", + "loading": "Loading...", + "name": "Name", + "email": "Email", + "user": "User", + "node": "Node", + "route": "Route", + "status": "Status", + "actions": "Actions", + "online": "Online", + "offline": "Offline", + "expired": "Expired", + "active": "Active", + "createdAt": "Created At", + "lastSeen": "Last Seen", + "hostname": "Hostname", + "addresses": "Addresses", + "tags": "Tags", + "owner": "Owner", + "routes": "Routes", + "all": "All", + "id": "ID", + "sort": "Sort", + "filter": "Filter", + "name": "Name", + "search": "Search...", + "createUser": "Create User", + "createNode": "Create Node", + "newUsername": "New Username...", + "deviceKey": "Device Key...", + "usernameAndKeyRequired": "Username and Device Key are Required", + "createdUser": "Created user", + "createdNode": "Created node", + "failedCreateUser": "Failed to create user", + "failedCreateNode": "Failed to create node" + }, + "users": { + "title": "Users", + "createUser": "Create User", + "displayName": "Display Name", + "provider": "Provider", + "email": "Email", + "nodes": "Nodes", + "preAuthKeys": "Pre-Auth Keys" + }, + "nodes": { + "title": "Nodes", + "createNode": "Create Node", + "registrationMethod": "Registration Method", + "expiresAt": "Expires At", + "info": "Info" + }, + "routes": { + "title": "Routes", + "enabled": "Enabled", + "advertised": "Advertised", + "destination": "Destination" + }, + "acls": { + "title": "ACLs", + "config": "Config", + "groups": "Groups", + "hosts": "Hosts", + "policies": "Policies", + "sshRules": "SSH Rules", + "tagOwners": "Tag Owners" + }, + "deploy": { + "title": "Deploy", + "copyCommand": "Copied Command to Clipboard!", + "general": "General:", + "advertise": "Advertise:", + "accept": "Accept:", + "saveDefaults": "Save Defaults", + "savedDefaults": "Saved Deployment Defaults", + "shieldsUp": "Shields Up", + "shieldsUpHelp": "Block incoming connections", + "generateQR": "Generate QR Code", + "generateQRHelp": "Create a scannable QR code to import into TailScale client", + "reset": "Reset", + "resetHelp": "Reset unspecified settings to default values", + "operator": "Operator", + "operatorHelp": "(Unix Only) Run as a different user", + "forceReauth": "Force Reauthentication", + "forceReauthHelp": "Force user to re-authenticate to Headscale server", + "sshServer": "SSH Server", + "sshServerHelp": "Run a local SSH server accessible by administrators", + "preAuthKey": "PreAuth Key", + "preAuthKeyHelp": "A generated key to automatically authenticate the node for a given user", + "unattended": "Unattended", + "unattendedHelp": "Run the tailscale client in unattended mode (on startup)", + "allowLANAccess": "Allow LAN Access", + "allowLANAccessHelp": "Allow local network access while connected to the TailNet and using an exit node", + "advertiseExitNode": "Advertise Exit Node", + "advertiseExitNodeHelp": "Allow other nodes on the TailNet to use this node as a gateway", + "advertiseTags": "Advertise Tags", + "advertiseTagsHelp": "List of advertised tags to apply to a machine on provisioning", + "advertiseRoutes": "Advertise Routes", + "advertiseRoutesHelp": "List of subnets which are reachable via this node", + "acceptDNS": "Accept DNS", + "acceptDNSHelp": "Accept the HeadScale-provided DNS settings", + "acceptRoutes": "Accept Routes", + "acceptRoutesHelp": "Accept other nodes' advertised subnets", + "exitNode": "Exit Node", + "exitNodeHelp": "Use this node as a gateway (target node must advertise exit node)", + "validKeys": "Valid Key(s)", + "tagError": "Tag should be a lowercase alphanumeric word", + "cidrError": "Invalid CIDR Format" + }, + "home": { + "title": "Home", + "totalUsers": "Total Users", + "onlineUsers": "Online Users", + "validPreAuthKeys": "Valid PreAuth Keys", + "totalNodes": "Total Nodes", + "onlineNodes": "Online Nodes", + "totalRoutes": "Total Routes" + } +} diff --git a/src/lib/locales/zh.json b/src/lib/locales/zh.json new file mode 100644 index 0000000..88c2027 --- /dev/null +++ b/src/lib/locales/zh.json @@ -0,0 +1,237 @@ +{ + "app": { + "title": "Headscale 管理器", + "github": "GitHub" + }, + "navigation": { + "home": "首页", + "users": "用户", + "nodes": "节点", + "routes": "路由", + "acls": "访问控制", + "settings": "设置", + "deploy": "部署" + }, + "settings": { + "title": "设置", + "apiUrl": "API 地址", + "apiKey": "API 密钥", + "apiUrlPlaceholder": "请输入 API 地址", + "apiKeyPlaceholder": "请输入您的 API 密钥", + "apiTtl": "API 刷新间隔(秒)", + "theme": "主题", + "language": "语言", + "debugging": "控制台调试", + "save": "保存设置", + "authorized": "已授权", + "notAuthorized": "未授权", + "expiresIn": "过期时间", + "checkingAuth": "正在检查授权...", + "savedSettings": "设置已保存", + "logUsers": "记录用户", + "logNodes": "记录节点", + "logPreAuthKeys": "记录预授权密钥", + "logApiKeyInfo": "记录 API 密钥信息", + "showApiKey": "显示 API 密钥", + "hideApiKey": "隐藏 API 密钥", + "refreshApiKey": "刷新 API 密钥" + }, + "common": { + "close": "关闭", + "cancel": "取消", + "confirm": "确认", + "delete": "删除", + "edit": "编辑", + "save": "保存", + "create": "创建", + "loading": "加载中...", + "name": "名称", + "email": "邮箱", + "user": "用户", + "node": "节点", + "route": "路由", + "status": "状态", + "actions": "操作", + "online": "在线", + "offline": "离线", + "expired": "已过期", + "active": "激活", + "createdAt": "创建时间", + "lastSeen": "最后活跃", + "hostname": "主机名", + "addresses": "地址", + "tags": "标签", + "owner": "所有者", + "routes": "路由", + "all": "全部", + "id": "ID", + "sort": "排序", + "filter": "筛选", + "name": "名称", + "search": "搜索...", + "createUser": "创建用户", + "createNode": "创建节点", + "newUsername": "新用户名...", + "deviceKey": "设备密钥...", + "usernameAndKeyRequired": "用户名和设备密钥是必需的", + "createdUser": "已创建用户", + "createdNode": "已创建节点", + "failedCreateUser": "创建用户失败", + "failedCreateNode": "创建节点失败" + }, + "users": { + "title": "用户", + "createUser": "创建用户", + "displayName": "显示名称", + "provider": "提供商", + "email": "邮箱", + "nodes": "节点", + "preAuthKeys": "预授权密钥" + }, + "nodes": { + "title": "节点", + "createNode": "创建节点", + "registrationMethod": "注册方式", + "expiresAt": "过期时间", + "info": "信息" + }, + "routes": { + "title": "路由", + "enabled": "已启用", + "advertised": "已通告", + "destination": "目标" + }, + "acls": { + "title": "访问控制列表", + "config": "配置", + "groups": "组", + "hosts": "主机", + "policies": "策略", + "sshRules": "SSH 规则", + "tagOwners": "标签所有者" + }, + "deploy": { + "title": "部署", + "copyCommand": "命令已复制到剪贴板!", + "general": "常规设置:", + "advertise": "通告设置:", + "accept": "接受设置:", + "saveDefaults": "保存默认设置", + "savedDefaults": "部署默认设置已保存", + "shieldsUp": "防护模式", + "shieldsUpHelp": "阻止传入连接", + "generateQR": "生成二维码", + "generateQRHelp": "创建可扫描的二维码以导入到 TailScale 客户端", + "reset": "重置", + "resetHelp": "将未指定的设置重置为默认值", + "operator": "操作用户", + "operatorHelp": "(仅限 Unix)以不同用户身份运行", + "forceReauth": "强制重新认证", + "forceReauthHelp": "强制用户重新向 Headscale 服务器认证", + "sshServer": "SSH 服务器", + "sshServerHelp": "运行管理员可访问的本地 SSH 服务器", + "preAuthKey": "预授权密钥", + "preAuthKeyHelp": "自动为指定用户认证节点的生成密钥", + "unattended": "无人值守", + "unattendedHelp": "以无人值守模式运行 tailscale 客户端(开机启动)", + "allowLANAccess": "允许局域网访问", + "allowLANAccessHelp": "连接到 TailNet 并使用出口节点时允许本地网络访问", + "advertiseExitNode": "通告出口节点", + "advertiseExitNodeHelp": "允许 TailNet 上的其他节点使用此节点作为网关", + "advertiseTags": "通告标签", + "advertiseTagsHelp": "在配置时应用到设备的通告标签列表", + "advertiseRoutes": "通告路由", + "advertiseRoutesHelp": "可通过此节点访问的子网列表", + "acceptDNS": "接受 DNS", + "acceptDNSHelp": "接受 HeadScale 提供的 DNS 设置", + "acceptRoutes": "接受路由", + "acceptRoutesHelp": "接受其他节点通告的子网", + "exitNode": "出口节点", + "exitNodeHelp": "使用此节点作为网关(目标节点必须通告出口节点)", + "validKeys": "个有效密钥", + "tagError": "标签应为小写字母数字单词", + "cidrError": "无效的 CIDR 格式" + }, + "home": { + "title": "首页", + "totalUsers": "总用户数", + "onlineUsers": "在线用户", + "validPreAuthKeys": "有效预授权密钥", + "totalNodes": "总节点数", + "onlineNodes": "在线节点", + "totalRoutes": "总路由数" + }, + "cards": { + "provider": "提供商:", + "email": "邮箱:", + "displayName": "显示名称:", + "hostname": "主机名:", + "created": "创建时间:", + "lastSeen": "最后活跃:", + "user": "用户:", + "routes": "路由:", + "owner": "所有者:", + "tags": "标签:", + "registerMethod": "注册方式:", + "expires": "过期时间:", + "name": "名称:", + "info": "信息:", + "ipv4Address": "IPv4 地址:", + "onlineNow": "在线", + "unspecified": "未指定", + "local": "本地", + "preAuthKey": "预授权密钥", + "cli": "命令行", + "oidc": "OIDC", + "custom": "自定义", + "group": "组", + "host": "主机", + "tag": "标签", + "srcObject": "源对象...", + "dstObject": "目标对象...", + "dstPorts": "目标端口...", + "selectOwners": "选择 {tagName} 的所有者...", + "selectMembers": "选择 {groupName} 的成员...", + "invalidHost": "提供的主机无效", + "invalidPort": "提供的端口号无效", + "protocol": "协议:", + "any": "任意", + "tcp": "TCP", + "udp": "UDP", + "icmp": "ICMP", + "sources": "源:", + "destinations": "目标:", + "add": "添加", + "newOwner": "新所有者:", + "userNameEmpty": "用户名不能为空", + "nodeNameEmpty": "节点名不能为空", + "sshRule": "SSH 规则", + "usernames": "用户名:", + "advertisedTags": "通告标签:", + "tagsPreventedByACL": "以下标签已被当前 ACL 阻止:", + "hideInvalid": "隐藏无效", + "create": "创建", + "ephemeral": "临时", + "reusable": "可重用", + "delete": "删除", + "hostRenameWarning": "主机 '{hostName}' 与用户同名。
请重命名该主机。", + "deletedUser": "已删除用户 \"{name}\" (ID: {id})", + "deletedMachine": "已删除机器 \"{name}\" ({id})", + "failedDeleteUser": "删除用户 \"{name}\" ({id}) 失败", + "failedDeleteMachine": "删除机器 \"{name}\" ({id}) 失败", + "stillHasNodes": " 仍有节点。", + "invalidTags": "无效标签:", + "policyDeleted": "策略 #{number} 已删除", + "sshRuleDeleted": "SSH 规则 #{number} 已删除", + "groupDeleted": "组 '{name}' 已删除", + "tagDeleted": "标签 '{name}' 已删除", + "groupRenamedSuccess": "组已从 '{oldName}' 重命名为 '{newName}'", + "tagRenamedSuccess": "标签已从 '{oldName}' 重命名为 '{newName}'", + "membersOf": "成员", + "ownersOf": "所有者", + "hostDeleted": "主机 '{name}' 已删除", + "nodes": "节点:", + "used": "已使用", + "expired": "已过期" + } +} diff --git a/src/lib/page/PageHeader.svelte b/src/lib/page/PageHeader.svelte index 6f98cdd..48a6bdf 100644 --- a/src/lib/page/PageHeader.svelte +++ b/src/lib/page/PageHeader.svelte @@ -7,6 +7,7 @@ import type { LayoutStyle, Valued } from '$lib/States.svelte'; import { App } from '$lib/States.svelte'; import type { Snippet } from 'svelte'; + import { _ } from 'svelte-i18n'; type PageHeaderProps = { filterString?: string, @@ -22,7 +23,7 @@ title, show = $bindable(false), layout = $bindable(undefined), - buttonText = 'Create', + buttonText = $_('common.create'), button, }: PageHeaderProps = $props() @@ -77,7 +78,7 @@ class="input rounded-md text-sm w-64 md:w-96 {regexIsValid ? '' : 'input-error'}" bind:value={filterString} use:focus - placeholder="Search..." + placeholder={$_('common.search')} /> {/if}
diff --git a/src/lib/parts/Tabbed.svelte b/src/lib/parts/Tabbed.svelte index 7f40308..2da61ec 100644 --- a/src/lib/parts/Tabbed.svelte +++ b/src/lib/parts/Tabbed.svelte @@ -1,12 +1,13 @@ @@ -23,7 +24,7 @@ - {tab.title} + {tab.titleKey ? $_(tab.titleKey) : tab.title} {/each} \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 7970906..08a661c 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -46,12 +46,21 @@ import { version } from '$lib/common/debug'; import { App } from '$lib/States.svelte'; import { setTheme } from '$lib/common/themes'; + import '$lib/i18n'; + import { locale, waitLocale, _, isLoading } from 'svelte-i18n'; let { children } = $props() let ToastStore = $state(getToastStore()); - onMount(() => { + onMount(async () => { + // Wait for i18n to be ready first + await waitLocale(); + + // Initialize language + locale.set(App.language.value); + await waitLocale(); + setTheme(App.theme.value || 'skeleton') App.populateAll(createPopulateErrorHandler(ToastStore), true) @@ -85,7 +94,7 @@ - Headscale-Admin + {$isLoading ? 'Headscale-Admin' : $_('app.title')} {version} @@ -99,7 +108,7 @@ rel="noreferrer" > - GitHub + {$isLoading ? 'GitHub' : $_('app.github')}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 0a00c4c..50fa8cb 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -12,9 +12,10 @@ import RawMdiUser from '~icons/mdi/user'; import { App } from '$lib/States.svelte'; + import { _ } from 'svelte-i18n'; type Summary = { - title: string; + titleKey: string; border: string; value: any; icon: any; @@ -24,14 +25,14 @@ const summaries = $derived( [ { - title: 'Total Users', + titleKey: 'home.totalUsers', border: 'border-primary-700 dark:border-primary-600', icon: RawMdiUser, value: App.users.value.length, path: '/users', }, { - title: 'Online Users', + titleKey: 'home.onlineUsers', border: 'border-primary-500 dark:border-primary-400', icon: RawMdiUser, value: App.users.value.filter((user) => @@ -42,7 +43,7 @@ path: '/users', }, { - title: 'Valid PreAuth Keys', + titleKey: 'home.validPreAuthKeys', border: 'border-slate-700 dark:border-slate-500', icon: RawMdiDevices, value: App.preAuthKeys.value.filter( @@ -51,21 +52,21 @@ path: '/users', }, { - title: 'Total Nodes', + titleKey: 'home.totalNodes', border: 'border-secondary-700 dark:border-secondary-600', icon: RawMdiDevices, value: App.nodes.value.length, path: '/nodes', }, { - title: 'Online Nodes', + titleKey: 'home.onlineNodes', border: 'border-secondary-400 dark:border-secondary-400', icon: RawMdiDevices, value: App.nodes.value.filter((n) => n.online).length, path: '/nodes', }, { - title: 'Total Routes', + titleKey: 'home.totalRoutes', border: 'border-warning-600 dark:border-warning-600', icon: RawMdiRouter, value: App.nodes.value.reduce( @@ -79,7 +80,7 @@ - + {#each summaries as summary} @@ -101,7 +102,7 @@
- {summary.title} + {$_(summary.titleKey)}
diff --git a/src/routes/acls/+page.svelte b/src/routes/acls/+page.svelte index 48cf187..5ad4003 100644 --- a/src/routes/acls/+page.svelte +++ b/src/routes/acls/+page.svelte @@ -23,6 +23,7 @@ import Policies from './Policies.svelte'; import TagOwners from './TagOwners.svelte' import SshRules from './SshRules.svelte'; + import { _ } from 'svelte-i18n'; const ToastStore = getToastStore() @@ -32,12 +33,12 @@ // Navigation tabs let tabSet: number = $state(0); const tabs = [ - { name: 'groups', title: 'Groups', logo: RawMdiGroups }, - { name: 'tag-owners', title: 'Tag Owners', logo: RawMdiTag }, - { name: 'hosts', title: 'Hosts', logo: RawMdiDevices }, - { name: 'policies', title: 'Policies', logo: RawMdiSecurity }, - { name: 'ssh', title: 'SSH', logo: RawMdiConsole }, - { name: 'config', title: 'Config', logo: RawMdiCodeJSON }, + { name: 'groups', titleKey: 'acls.groups', logo: RawMdiGroups }, + { name: 'tag-owners', titleKey: 'acls.tagOwners', logo: RawMdiTag }, + { name: 'hosts', titleKey: 'acls.hosts', logo: RawMdiDevices }, + { name: 'policies', titleKey: 'acls.policies', logo: RawMdiSecurity }, + { name: 'ssh', titleKey: 'acls.sshRules', logo: RawMdiConsole }, + { name: 'config', titleKey: 'acls.config', logo: RawMdiCodeJSON }, ]; onMount(() => { @@ -51,7 +52,7 @@ - + - + {#snippet button()} @@ -152,21 +162,21 @@ {#if apiKeyInfo.authorized !== null}
- {apiKeyInfo.authorized ? "Authorized" : "Not Authorized"} + {apiKeyInfo.authorized ? $_('settings.authorized') : $_('settings.notAuthorized')} {#if apiKeyInfo.authorized && apiKeyExpirationMessage} - Expires in: {apiKeyExpirationMessage.message} + {$_('settings.expiresIn')}: {apiKeyExpirationMessage.message} {/if}
{:else if loading} -
Checking authorization...
+
{$_('settings.checkingAuth')}
{/if}
- +
- + { + App.language.value = settings.language; + locale.set(settings.language); + if (browser) { + localStorage.setItem('locale', settings.language); + } + }} + > + {#each LANGUAGES as language} + + {/each} + +
+
diff --git a/src/routes/users/+page.svelte b/src/routes/users/+page.svelte index adcb610..ba77cc8 100644 --- a/src/routes/users/+page.svelte +++ b/src/routes/users/+page.svelte @@ -12,6 +12,7 @@ import { App } from '$lib/States.svelte'; import { getSortedFilteredUsers } from '$lib/common/funcs'; import FilterOnlineBtn from '$lib/parts/FilterOnlineBtn.svelte'; + import { _ } from 'svelte-i18n'; let showCreate = $state(false); const layout = $derived(App.layoutUser.value) @@ -40,7 +41,7 @@ - + {#snippet button()} {/snippet} @@ -49,15 +50,15 @@
- - + +
- - - + + +
From ac08af54b1ca78727bd45e57f9a7049dabf58c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=8E=E5=A4=A9=E4=BD=A0=E5=A5=BD?= Date: Mon, 9 Feb 2026 23:13:18 +0800 Subject: [PATCH 2/8] feat: Adapt for Headscale v0.28.0 and add ACL management - Updated API integration for Headscale v0.27/v0.28 changes. - Added ACL management UI (Policies, SSH Rules, Groups, Tag Owners). - Refactored state management using States.svelte.ts. --- .npmrc | 1 - package-lock.json | 531 +++++++++++------- src/app.html | 35 +- src/app.postcss | 2 +- src/lib/Navigation.svelte | 2 +- src/lib/States.svelte.ts | 11 +- src/lib/cards/CardListContainer.svelte | 2 +- src/lib/cards/CardListItem.svelte | 2 +- src/lib/cards/acl/GroupListCard.svelte | 6 +- src/lib/cards/acl/HostListCard.svelte | 2 +- src/lib/cards/acl/ListEntry.svelte | 2 +- src/lib/cards/acl/PolicyListCard.svelte | 22 +- src/lib/cards/acl/SshRuleListCard.svelte | 21 +- src/lib/cards/acl/TagOwnerListCard.svelte | 4 +- src/lib/cards/common/ItemListName.svelte | 1 + src/lib/cards/node/NodeCreate.svelte | 4 +- src/lib/cards/node/NodeExpiresAt.svelte | 87 ++- src/lib/cards/node/NodeListCard.svelte | 2 +- src/lib/cards/node/NodeOwner.svelte | 78 +-- src/lib/cards/node/NodeTags.svelte | 113 ++-- src/lib/cards/node/NodeTileCard.svelte | 11 + src/lib/cards/route/RouteListCard.svelte | 2 +- src/lib/cards/user/UserCreate.svelte | 2 + src/lib/cards/user/UserListCard.svelte | 2 +- src/lib/cards/user/UserListNodes.svelte | 7 +- src/lib/cards/user/UserListPreAuthKey.svelte | 24 +- src/lib/cards/user/UserListPreAuthKeys.svelte | 14 +- src/lib/common/acl.svelte.ts | 151 +++-- src/lib/common/api/base.ts | 12 +- src/lib/common/api/get.ts | 19 +- src/lib/common/api/modify.ts | 13 +- src/lib/common/debug.ts | 2 +- src/lib/common/errors.ts | 17 + src/lib/common/funcs.ts | 43 +- src/lib/common/themes.ts | 1 + src/lib/common/types.ts | 4 +- src/lib/locales/en.json | 32 +- src/lib/locales/zh.json | 9 +- src/lib/page/PageDrawer.svelte | 5 +- src/lib/page/PageHeader.svelte | 5 + src/lib/parts/LoaderModal.svelte | 2 + src/lib/parts/MultiSelect.svelte | 1 + src/lib/parts/NewItem.svelte | 5 + src/lib/parts/OnlineNodeIndicator.svelte | 4 +- src/lib/parts/OnlineUserIndicator.svelte | 4 +- src/lib/parts/Text.svelte | 3 + src/routes/+layout.svelte | 13 +- src/routes/acls/Hosts.svelte | 4 +- src/routes/deploy/+page.svelte | 8 +- src/routes/deploy/DeployCheck.svelte | 5 +- src/routes/nodes/+page.svelte | 47 +- src/routes/settings/+page.svelte | 135 +++-- svelte.config.js | 3 - tailwind.config.ts | 100 ++++ 54 files changed, 1067 insertions(+), 570 deletions(-) diff --git a/.npmrc b/.npmrc index 0c05da4..b6f27f1 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1 @@ engine-strict=true -resolution-mode=highest diff --git a/package-lock.json b/package-lock.json index 5462a54..e8cd104 100644 --- a/package-lock.json +++ b/package-lock.json @@ -613,9 +613,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -642,13 +642,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -657,9 +657,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -680,10 +680,23 @@ "node": "*" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -694,9 +707,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", - "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -706,7 +719,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -718,9 +731,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -742,19 +755,22 @@ } }, "node_modules/@eslint/js": { - "version": "9.21.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", - "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -762,13 +778,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.12.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1499,9 +1515,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.5.tgz", + "integrity": "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==", "cpu": [ "arm" ], @@ -1513,9 +1529,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.5.tgz", + "integrity": "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==", "cpu": [ "arm64" ], @@ -1527,9 +1543,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.5.tgz", + "integrity": "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==", "cpu": [ "arm64" ], @@ -1541,9 +1557,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.5.tgz", + "integrity": "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==", "cpu": [ "x64" ], @@ -1555,9 +1571,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", - "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.5.tgz", + "integrity": "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==", "cpu": [ "arm64" ], @@ -1569,9 +1585,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", - "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.5.tgz", + "integrity": "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==", "cpu": [ "x64" ], @@ -1583,9 +1599,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.5.tgz", + "integrity": "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==", "cpu": [ "arm" ], @@ -1597,9 +1613,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.5.tgz", + "integrity": "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==", "cpu": [ "arm" ], @@ -1611,9 +1627,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.5.tgz", + "integrity": "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==", "cpu": [ "arm64" ], @@ -1625,9 +1641,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.5.tgz", + "integrity": "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==", "cpu": [ "arm64" ], @@ -1638,10 +1654,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "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==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.5.tgz", + "integrity": "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==", "cpu": [ "loong64" ], @@ -1652,10 +1668,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "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==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.5.tgz", + "integrity": "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==", "cpu": [ "ppc64" ], @@ -1667,9 +1683,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.5.tgz", + "integrity": "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.5.tgz", + "integrity": "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==", "cpu": [ "riscv64" ], @@ -1681,9 +1711,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.5.tgz", + "integrity": "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==", "cpu": [ "s390x" ], @@ -1695,9 +1725,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.5.tgz", + "integrity": "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==", "cpu": [ "x64" ], @@ -1709,9 +1739,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.5.tgz", + "integrity": "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==", "cpu": [ "x64" ], @@ -1722,10 +1752,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.5.tgz", + "integrity": "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.5.tgz", + "integrity": "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==", "cpu": [ "arm64" ], @@ -1737,9 +1781,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.5.tgz", + "integrity": "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==", "cpu": [ "ia32" ], @@ -1750,10 +1794,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.5.tgz", + "integrity": "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "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==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.5.tgz", + "integrity": "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==", "cpu": [ "x64" ], @@ -1793,6 +1851,23 @@ "integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==", "license": "ISC" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.8.tgz", + "integrity": "sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, "node_modules/@sveltejs/adapter-auto": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-4.0.0.tgz", @@ -1817,17 +1892,19 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.17.3.tgz", - "integrity": "sha512-GcNaPDr0ti4O/TonPewkML2DG7UVXkSxPN3nPMlpmx0Rs4b2kVP4gymz98WEHlfzPXdd4uOOT1Js26DtieTNBQ==", + "version": "2.49.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.49.2.tgz", + "integrity": "sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==", "dev": true, "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", "cookie": "^0.6.0", - "devalue": "^5.1.0", + "devalue": "^5.3.2", "esm-env": "^1.2.2", - "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", @@ -1842,9 +1919,15 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0" + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } } }, "node_modules/@sveltejs/kit/node_modules/esm-env": { @@ -1931,9 +2014,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/json-schema": { @@ -2281,9 +2364,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2574,9 +2657,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "dev": true, "funding": [ { @@ -2866,9 +2949,9 @@ } }, "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.1.tgz", + "integrity": "sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A==", "dev": true, "license": "MIT" }, @@ -3091,32 +3174,32 @@ } }, "node_modules/eslint": { - "version": "9.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", - "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "9.21.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3214,9 +3297,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3244,9 +3327,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3255,9 +3338,9 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3303,15 +3386,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3321,9 +3404,9 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3624,9 +3707,9 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -3941,9 +4024,9 @@ } }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4002,9 +4085,9 @@ } }, "node_modules/jsonpath-plus": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.2.0.tgz", - "integrity": "sha512-T9V+8iNYKFL2n2rF+w02LBOT2JjDnTjioaNFrxRy0Bv1y/hNsqR/EBK7Ojy2ythRHwmz2cRIls+9JitQGZC/sw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", "license": "MIT", "dependencies": { "@jsep-plugin/assignment": "^1.3.0", @@ -5051,13 +5134,13 @@ } }, "node_modules/rollup": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", - "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.5.tgz", + "integrity": "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -5067,25 +5150,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@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", + "@rollup/rollup-android-arm-eabi": "4.53.5", + "@rollup/rollup-android-arm64": "4.53.5", + "@rollup/rollup-darwin-arm64": "4.53.5", + "@rollup/rollup-darwin-x64": "4.53.5", + "@rollup/rollup-freebsd-arm64": "4.53.5", + "@rollup/rollup-freebsd-x64": "4.53.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.5", + "@rollup/rollup-linux-arm-musleabihf": "4.53.5", + "@rollup/rollup-linux-arm64-gnu": "4.53.5", + "@rollup/rollup-linux-arm64-musl": "4.53.5", + "@rollup/rollup-linux-loong64-gnu": "4.53.5", + "@rollup/rollup-linux-ppc64-gnu": "4.53.5", + "@rollup/rollup-linux-riscv64-gnu": "4.53.5", + "@rollup/rollup-linux-riscv64-musl": "4.53.5", + "@rollup/rollup-linux-s390x-gnu": "4.53.5", + "@rollup/rollup-linux-x64-gnu": "4.53.5", + "@rollup/rollup-linux-x64-musl": "4.53.5", + "@rollup/rollup-openharmony-arm64": "4.53.5", + "@rollup/rollup-win32-arm64-msvc": "4.53.5", + "@rollup/rollup-win32-ia32-msvc": "4.53.5", + "@rollup/rollup-win32-x64-gnu": "4.53.5", + "@rollup/rollup-win32-x64-msvc": "4.53.5", "fsevents": "~2.3.2" } }, @@ -5491,21 +5577,6 @@ } } }, - "node_modules/svelte-check/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/svelte-eslint-parser": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.0.0.tgz", @@ -6295,6 +6366,54 @@ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -6535,15 +6654,18 @@ } }, "node_modules/vite": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", - "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -6667,6 +6789,37 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitefu": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.5.tgz", diff --git a/src/app.html b/src/app.html index 7226faa..c8d21cf 100644 --- a/src/app.html +++ b/src/app.html @@ -1,5 +1,5 @@ - + @@ -8,7 +8,38 @@
%sveltekit.body% diff --git a/src/app.postcss b/src/app.postcss index d6bb6e4..d905e72 100644 --- a/src/app.postcss +++ b/src/app.postcss @@ -11,7 +11,7 @@ body { */ html, body { - @apply overflow-y-scroll; + @apply h-full overflow-hidden; } /* modern theme */ diff --git a/src/lib/Navigation.svelte b/src/lib/Navigation.svelte index a1aa23a..b3604fb 100644 --- a/src/lib/Navigation.svelte +++ b/src/lib/Navigation.svelte @@ -27,7 +27,7 @@ const DrawerStore = getDrawerStore(); function classesActive(href: string): string { - return href === page.route.id ? 'bg-primary-300 dark:bg-primary-700' : ''; + return href === page.route.id ? 'variant-filled-primary' : ''; } let newPath = $state(''); diff --git a/src/lib/States.svelte.ts b/src/lib/States.svelte.ts index 8d411c1..b5a9033 100644 --- a/src/lib/States.svelte.ts +++ b/src/lib/States.svelte.ts @@ -225,10 +225,17 @@ export class HeadscaleAdmin { async populateApiKeyInfo(): Promise { const { apiKeys } = await apiGet(`/api/v1/apikey`); - const myKey = apiKeys.filter((key) => this.apiKey.value.startsWith(key.prefix))[0]; + const myKey = apiKeys.find((key) => { + const cleanPrefix = key.prefix.replace(/\*+$/, ''); + return this.apiKey.value.startsWith(cleanPrefix); + }); + const apiKeyInfo = this.apiKeyInfo.value - apiKeyInfo.expires = myKey.expiration; apiKeyInfo.authorized = true; + if (myKey) { + apiKeyInfo.expires = myKey.expiration; + } + this.apiKeyInfo.value = {...apiKeyInfo}; return true; } diff --git a/src/lib/cards/CardListContainer.svelte b/src/lib/cards/CardListContainer.svelte index 2d87118..a3d8c84 100644 --- a/src/lib/cards/CardListContainer.svelte +++ b/src/lib/cards/CardListContainer.svelte @@ -2,6 +2,6 @@ let { children } = $props() -
+
{@render children()}
diff --git a/src/lib/cards/CardListItem.svelte b/src/lib/cards/CardListItem.svelte index 042104e..6924cae 100644 --- a/src/lib/cards/CardListItem.svelte +++ b/src/lib/cards/CardListItem.svelte @@ -13,7 +13,7 @@ diff --git a/src/lib/cards/acl/GroupListCard.svelte b/src/lib/cards/acl/GroupListCard.svelte index 4d9ae49..496ce70 100644 --- a/src/lib/cards/acl/GroupListCard.svelte +++ b/src/lib/cards/acl/GroupListCard.svelte @@ -11,7 +11,7 @@ import { App } from '$lib/States.svelte'; import { debug } from '$lib/common/debug'; import { getToastStore } from '@skeletonlabs/skeleton'; - import { toastSuccess, toastError } from '$lib/common/funcs'; + import { toastSuccess, toastError, getUserAclName } from '$lib/common/funcs'; import RawMdiGroup from '~icons/mdi/account-group-outline'; @@ -26,9 +26,7 @@ let {acl = $bindable(), groupName, open = $bindable()}: GroupListCardProps = $props() const groupMembers = $derived(acl.getGroupMembers(groupName)); - const userNames = $derived(App.users.value.map((u) => { - return u.email ? u.email : u.name; - }).toSorted()) + const userNames = $derived(App.users.value.map(getUserAclName).toSorted()) let group = $state(makeGroup()); let groupNameNew = $state(''); diff --git a/src/lib/cards/acl/HostListCard.svelte b/src/lib/cards/acl/HostListCard.svelte index 11feadf..17875e8 100644 --- a/src/lib/cards/acl/HostListCard.svelte +++ b/src/lib/cards/acl/HostListCard.svelte @@ -114,7 +114,7 @@
{#if userNames.includes(hostName)} diff --git a/src/lib/cards/acl/ListEntry.svelte b/src/lib/cards/acl/ListEntry.svelte index 45762ef..a5f9813 100644 --- a/src/lib/cards/acl/ListEntry.svelte +++ b/src/lib/cards/acl/ListEntry.svelte @@ -27,7 +27,7 @@ diff --git a/src/lib/cards/acl/PolicyListCard.svelte b/src/lib/cards/acl/PolicyListCard.svelte index ad9df85..b59526a 100644 --- a/src/lib/cards/acl/PolicyListCard.svelte +++ b/src/lib/cards/acl/PolicyListCard.svelte @@ -1,7 +1,7 @@ -
- - {diff.message} - - - { - loading = true - try{ - App.updateValue(App.nodes, await expireNode(node)) +
+ {#if editMode} + + + + {:else} + + {diff.message} + + + + { + loading = true + try{ + App.updateValue(App.nodes, await expireNode(node)) + } finally { + loading = false + } + }} + /> + + {/if}
- + \ No newline at end of file diff --git a/src/lib/cards/node/NodeListCard.svelte b/src/lib/cards/node/NodeListCard.svelte index 8719346..33346d2 100644 --- a/src/lib/cards/node/NodeListCard.svelte +++ b/src/lib/cards/node/NodeListCard.svelte @@ -20,7 +20,7 @@ diff --git a/src/lib/cards/node/NodeOwner.svelte b/src/lib/cards/node/NodeOwner.svelte index 230bb19..90e66fb 100644 --- a/src/lib/cards/node/NodeOwner.svelte +++ b/src/lib/cards/node/NodeOwner.svelte @@ -2,36 +2,21 @@ import CardListEntry from '../CardListEntry.svelte'; import type { Node } from '$lib/common/types'; import OnlineUserIndicator from '$lib/parts/OnlineUserIndicator.svelte'; - import { changeNodeOwner } from '$lib/common/api'; - import { openDrawer, toastError, toastSuccess } from '$lib/common/funcs'; - import { debug } from '$lib/common/debug'; - import { getDrawerStore, getToastStore } from '@skeletonlabs/skeleton'; - import { slide } from 'svelte/transition'; + import { openDrawer } from '$lib/common/funcs'; + import { getDrawerStore } from '@skeletonlabs/skeleton'; import { _ } from 'svelte-i18n'; - import RawMdiSwapHorizontal from '~icons/mdi/swap-horizontal'; - import RawMdiCheckCircleOutline from '~icons/mdi/check-circle-outline'; - import RawMdiCloseCircleOutline from '~icons/mdi/close-circle-outline'; - - import { App } from '$lib/States.svelte'; - type NodeOwnerProps = { node: Node, } let { node }: NodeOwnerProps = $props() const drawerStore = getDrawerStore(); - let transferUser = $state(''); - let showTransfer = $state(false); - let transferring = $state(false); - - const ToastStore = getToastStore();
- { @@ -40,65 +25,6 @@ > {node.user.name} - -
- {#if showTransfer} -
- {$_('cards.newOwner')} - - - -
- {/if}
diff --git a/src/lib/cards/node/NodeTags.svelte b/src/lib/cards/node/NodeTags.svelte index 8d6d348..76aa172 100644 --- a/src/lib/cards/node/NodeTags.svelte +++ b/src/lib/cards/node/NodeTags.svelte @@ -1,14 +1,13 @@ -
-

{$_('cards.tagsPreventedByACL')}

-

- {#if popupInvalidTagsShow == true} - {#each tagsInvalid as tag} - - {/each} - {/if} -

-
-
-
- - {#snippet childTitle()} - -{$_('cards.advertisedTags')} - {#if tagsInvalid.length > 0} - - {/if} - - {/snippet} -
- {#each tagsValid as tag} - - {/each} -
-
diff --git a/src/lib/cards/node/NodeTileCard.svelte b/src/lib/cards/node/NodeTileCard.svelte index e070314..c3e2ddc 100644 --- a/src/lib/cards/node/NodeTileCard.svelte +++ b/src/lib/cards/node/NodeTileCard.svelte @@ -77,5 +77,16 @@ {routeCount} + +
+ {#if node.tags.length > 0} + {#each node.tags as tag} + {tag.replace('tag:', '')} + {/each} + {:else} + - + {/if} +
+

diff --git a/src/lib/cards/route/RouteListCard.svelte b/src/lib/cards/route/RouteListCard.svelte index c30b066..309ada7 100644 --- a/src/lib/cards/route/RouteListCard.svelte +++ b/src/lib/cards/route/RouteListCard.svelte @@ -19,7 +19,7 @@ diff --git a/src/lib/cards/user/UserCreate.svelte b/src/lib/cards/user/UserCreate.svelte index c7d62ac..17553ea 100644 --- a/src/lib/cards/user/UserCreate.svelte +++ b/src/lib/cards/user/UserCreate.svelte @@ -39,6 +39,8 @@
diff --git a/src/lib/cards/user/UserListNodes.svelte b/src/lib/cards/user/UserListNodes.svelte index 7a57511..4665a8d 100644 --- a/src/lib/cards/user/UserListNodes.svelte +++ b/src/lib/cards/user/UserListNodes.svelte @@ -18,12 +18,7 @@ const drawerStore = getDrawerStore(); - const filteredNodes = $derived.by(() => { - if (App.users.value.filter((u) => u.id == user.id).length == 1) { - return App.nodes.value.filter((n) => n.user.id == user.id); - } - return []; - }); + const filteredNodes = $derived(App.nodes.value.filter((n) => n.user.id == user.id)); diff --git a/src/lib/cards/user/UserListPreAuthKey.svelte b/src/lib/cards/user/UserListPreAuthKey.svelte index 4124ee4..73c7a92 100644 --- a/src/lib/cards/user/UserListPreAuthKey.svelte +++ b/src/lib/cards/user/UserListPreAuthKey.svelte @@ -43,17 +43,6 @@ {preAuthKey.key.substring(0, 8)} - - { - await expirePreAuthKey(preAuthKey); - const keys = await getPreAuthKeys([preAuthKey.user.id]); - keys.forEach((pak) => { - App.updateValue(App.preAuthKeys, pak) - }); - }} - /> -
@@ -89,4 +78,17 @@
+
+ + { + await expirePreAuthKey(preAuthKey); + const keys = await getPreAuthKeys([preAuthKey.user.id]); + keys.forEach((pak) => { + App.updateValue(App.preAuthKeys, pak) + }); + }} + /> + +
diff --git a/src/lib/cards/user/UserListPreAuthKeys.svelte b/src/lib/cards/user/UserListPreAuthKeys.svelte index b3e47e7..f0f918f 100644 --- a/src/lib/cards/user/UserListPreAuthKeys.svelte +++ b/src/lib/cards/user/UserListPreAuthKeys.svelte @@ -96,7 +96,13 @@ checked.reusable, expires, ); - App.preAuthKeys.value.push(preAuthKey) + // Avoid duplicates if race condition with auto-refresh + const existingIdx = App.preAuthKeys.value.findIndex(k => k.id === preAuthKey.id); + if (existingIdx >= 0) { + App.preAuthKeys.value[existingIdx] = preAuthKey; + } else { + App.preAuthKeys.value.push(preAuthKey); + } } catch (e) { debug(e); } finally { @@ -116,6 +122,8 @@ class="flex flex-row flex-wrap col-span-12 pt-2 space-x-3 justify-end items-center text-sm" >
diff --git a/src/lib/parts/LoaderModal.svelte b/src/lib/parts/LoaderModal.svelte index 6958dc3..c649a9f 100644 --- a/src/lib/parts/LoaderModal.svelte +++ b/src/lib/parts/LoaderModal.svelte @@ -33,6 +33,8 @@
{title}
{body}