mirror of
https://github.com/qi4L/JYso.git
synced 2026-09-21 22:40:43 +08:00
perf:gui相关文件
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "jyso-web",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.28.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"vite": "^5.4.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@babel/code-frame": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.0.tgz", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
|
||||
|
||||
"@babel/core": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
|
||||
|
||||
"@babel/generator": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
||||
|
||||
"@babel/helper-globals": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/[email protected]", "http://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/[email protected]", "http://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/helpers": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.2.tgz", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
|
||||
|
||||
"@babel/parser": ["@babel/[email protected]", "http://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-self": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-source": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
||||
|
||||
"@babel/template": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
||||
|
||||
"@babel/traverse": ["@babel/[email protected]", "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
||||
|
||||
"@babel/types": ["@babel/[email protected]", "http://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/[email protected]", "http://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/[email protected]", "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/[email protected]", "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/[email protected]", "http://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@remix-run/router": ["@remix-run/[email protected]", "https://registry.npmmirror.com/@remix-run/router/-/router-1.23.2.tgz", {}, "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/[email protected]", "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", { "os": "android", "cpu": "arm" }, "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", { "os": "linux", "cpu": "arm" }, "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", { "os": "linux", "cpu": "arm" }, "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw=="],
|
||||
|
||||
"@rollup/rollup-openbsd-x64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", { "os": "none", "cpu": "arm64" }, "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/[email protected]", "http://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA=="],
|
||||
|
||||
"@types/babel__core": ["@types/[email protected]", "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||
|
||||
"@types/babel__generator": ["@types/[email protected]", "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
||||
|
||||
"@types/babel__template": ["@types/[email protected]", "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
||||
|
||||
"@types/babel__traverse": ["@types/[email protected]", "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
||||
|
||||
"@types/estree": ["@types/[email protected]", "http://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/prop-types": ["@types/[email protected]", "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
|
||||
|
||||
"@types/react": ["@types/[email protected]", "https://registry.npmmirror.com/@types/react/-/react-18.3.28.tgz", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw=="],
|
||||
|
||||
"@types/react-dom": ["@types/[email protected]", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
|
||||
|
||||
"@vitejs/plugin-react": ["@vitejs/[email protected]", "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
||||
|
||||
"asynckit": ["[email protected]", "http://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"axios": ["[email protected]", "http://registry.npmmirror.com/axios/-/axios-1.15.2.tgz", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A=="],
|
||||
|
||||
"baseline-browser-mapping": ["[email protected]", "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA=="],
|
||||
|
||||
"browserslist": ["[email protected]", "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["[email protected]", "http://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"caniuse-lite": ["[email protected]", "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", {}, "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ=="],
|
||||
|
||||
"combined-stream": ["[email protected]", "http://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"convert-source-map": ["[email protected]", "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"csstype": ["[email protected]", "http://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"debug": ["[email protected]", "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"delayed-stream": ["[email protected]", "http://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"dunder-proto": ["[email protected]", "http://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"electron-to-chromium": ["[email protected]", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.345.tgz", {}, "sha512-F9JXQGiMrz6yVNPI2qOVPvB9HzjH5cGzhs8oJ6A28V5L/YnzN/0KsuiibqF+F1Fd9qxFzD1BUnYSd8JfULxTwg=="],
|
||||
|
||||
"es-define-property": ["[email protected]", "http://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["[email protected]", "http://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["[email protected]", "http://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"es-set-tostringtag": ["[email protected]", "http://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||
|
||||
"esbuild": ["[email protected]", "http://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
||||
|
||||
"escalade": ["[email protected]", "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"follow-redirects": ["[email protected]", "http://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
|
||||
|
||||
"form-data": ["[email protected]", "http://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
|
||||
"fsevents": ["[email protected]", "http://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["[email protected]", "http://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"gensync": ["[email protected]", "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-intrinsic": ["[email protected]", "http://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["[email protected]", "http://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"gopd": ["[email protected]", "http://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"has-symbols": ["[email protected]", "http://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["[email protected]", "http://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["[email protected]", "http://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="],
|
||||
|
||||
"js-tokens": ["[email protected]", "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"jsesc": ["[email protected]", "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json5": ["[email protected]", "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"loose-envify": ["[email protected]", "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"math-intrinsics": ["[email protected]", "http://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"mime-db": ["[email protected]", "http://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["[email protected]", "http://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"ms": ["[email protected]", "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["[email protected]", "http://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"node-releases": ["[email protected]", "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.38.tgz", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="],
|
||||
|
||||
"picocolors": ["[email protected]", "http://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"postcss": ["[email protected]", "http://registry.npmmirror.com/postcss/-/postcss-8.5.12.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA=="],
|
||||
|
||||
"proxy-from-env": ["[email protected]", "http://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
||||
|
||||
"react": ["[email protected]", "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||
|
||||
"react-dom": ["[email protected]", "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],
|
||||
|
||||
"react-refresh": ["[email protected]", "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||
|
||||
"react-router": ["[email protected]", "https://registry.npmmirror.com/react-router/-/react-router-6.30.3.tgz", { "dependencies": { "@remix-run/router": "1.23.2" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw=="],
|
||||
|
||||
"react-router-dom": ["[email protected]", "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.30.3.tgz", { "dependencies": { "@remix-run/router": "1.23.2", "react-router": "6.30.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag=="],
|
||||
|
||||
"rollup": ["[email protected]", "http://registry.npmmirror.com/rollup/-/rollup-4.60.2.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.2", "@rollup/rollup-android-arm64": "4.60.2", "@rollup/rollup-darwin-arm64": "4.60.2", "@rollup/rollup-darwin-x64": "4.60.2", "@rollup/rollup-freebsd-arm64": "4.60.2", "@rollup/rollup-freebsd-x64": "4.60.2", "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", "@rollup/rollup-linux-arm-musleabihf": "4.60.2", "@rollup/rollup-linux-arm64-gnu": "4.60.2", "@rollup/rollup-linux-arm64-musl": "4.60.2", "@rollup/rollup-linux-loong64-gnu": "4.60.2", "@rollup/rollup-linux-loong64-musl": "4.60.2", "@rollup/rollup-linux-ppc64-gnu": "4.60.2", "@rollup/rollup-linux-ppc64-musl": "4.60.2", "@rollup/rollup-linux-riscv64-gnu": "4.60.2", "@rollup/rollup-linux-riscv64-musl": "4.60.2", "@rollup/rollup-linux-s390x-gnu": "4.60.2", "@rollup/rollup-linux-x64-gnu": "4.60.2", "@rollup/rollup-linux-x64-musl": "4.60.2", "@rollup/rollup-openbsd-x64": "4.60.2", "@rollup/rollup-openharmony-arm64": "4.60.2", "@rollup/rollup-win32-arm64-msvc": "4.60.2", "@rollup/rollup-win32-ia32-msvc": "4.60.2", "@rollup/rollup-win32-x64-gnu": "4.60.2", "@rollup/rollup-win32-x64-msvc": "4.60.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ=="],
|
||||
|
||||
"scheduler": ["[email protected]", "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||
|
||||
"semver": ["[email protected]", "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"source-map-js": ["[email protected]", "http://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"update-browserslist-db": ["[email protected]", "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||
|
||||
"vite": ["[email protected]", "http://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
|
||||
|
||||
"yallist": ["[email protected]", "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>JYso</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "jyso-web",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"liquid-glass-react": "^1.1.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.28.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"vite": "^5.4.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||
import Login from './pages/Login'
|
||||
import Dashboard from './pages/Dashboard'
|
||||
import ProtectedRoute from './components/ProtectedRoute'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/dashboard" element={
|
||||
<ProtectedRoute>
|
||||
<Dashboard />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import axios from 'axios'
|
||||
|
||||
let authToken = null
|
||||
|
||||
export function setAuthToken(token) {
|
||||
authToken = token
|
||||
}
|
||||
|
||||
export function getAuthToken() {
|
||||
return authToken
|
||||
}
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: '/api',
|
||||
timeout: 10000,
|
||||
})
|
||||
|
||||
api.interceptors.request.use(config => {
|
||||
if (authToken) {
|
||||
config.headers.Authorization = 'Bearer ' + authToken
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
api.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
authToken = null
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export function login(username, password) {
|
||||
return axios.post('/api/auth/login', { username, password })
|
||||
}
|
||||
|
||||
export function getStatus() {
|
||||
return api.get('/status')
|
||||
}
|
||||
|
||||
export function startServers(data) {
|
||||
return api.post('/servers/start', data)
|
||||
}
|
||||
|
||||
export function getGadgets() {
|
||||
return api.get('/gadgets')
|
||||
}
|
||||
|
||||
export function generatePayload(data) {
|
||||
return api.post('/payload/generate', data)
|
||||
}
|
||||
|
||||
export function updateConfig(data) {
|
||||
return api.post('/config/update', data)
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
return api.get('/config')
|
||||
}
|
||||
|
||||
export default api
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { getAuthToken } from '../api'
|
||||
|
||||
export default function ProtectedRoute({ children }) {
|
||||
if (!getAuthToken()) {
|
||||
return <Navigate to="/login" replace />
|
||||
}
|
||||
return children
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { createContext, useContext, useState, useEffect } from 'react'
|
||||
|
||||
const ThemeContext = createContext()
|
||||
|
||||
function getInitialTheme() {
|
||||
try {
|
||||
const saved = localStorage.getItem('jyso_theme')
|
||||
if (saved === 'dark' || saved === 'light') return saved
|
||||
} catch (e) { /* ignore */ }
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
return 'dark'
|
||||
}
|
||||
return 'light'
|
||||
}
|
||||
|
||||
export function ThemeProvider({ children }) {
|
||||
const [theme, setTheme] = useState(getInitialTheme)
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('data-theme', theme)
|
||||
try { localStorage.setItem('jyso_theme', theme) } catch (e) { /* ignore */ }
|
||||
}, [theme])
|
||||
|
||||
function toggleTheme() {
|
||||
setTheme(prev => prev === 'light' ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme, toggleTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const ctx = useContext(ThemeContext)
|
||||
if (!ctx) throw new Error('useTheme must be inside ThemeProvider')
|
||||
return ctx
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--spacing-xs: 6px;
|
||||
--spacing-sm: 10px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
--spacing-xl: 36px;
|
||||
--spacing-2xl: 48px;
|
||||
|
||||
--bg-primary: #F2F2F7;
|
||||
--bg-input: rgba(242, 242, 247, 0.64);
|
||||
--text-primary: #1D1D1F;
|
||||
--text-secondary: #6E6E73;
|
||||
--text-muted: #AEAEB2;
|
||||
--border-color: rgba(60, 60, 67, 0.10);
|
||||
--accent: #007AFF;
|
||||
--accent-hover: #0062CC;
|
||||
--accent-glow: rgba(0, 122, 255, 0.15);
|
||||
--success: #34C759;
|
||||
--success-bg: rgba(52, 199, 89, 0.08);
|
||||
--danger: #FF3B30;
|
||||
--danger-bg: rgba(255, 59, 48, 0.06);
|
||||
--card-gap: 20px;
|
||||
--card-radius: 24px;
|
||||
--btn-radius: 13px;
|
||||
--input-radius: 13px;
|
||||
--font-mono: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
|
||||
--transition-fast: 200ms cubic-bezier(0.22, 0.1, 0.22, 1);
|
||||
--transition-fluid: 420ms cubic-bezier(0.22, 0.1, 0.22, 1);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--bg-primary: #000000;
|
||||
--bg-input: rgba(28, 28, 30, 0.60);
|
||||
--text-primary: #F5F5F7;
|
||||
--text-secondary: #98989D;
|
||||
--text-muted: #636366;
|
||||
--border-color: rgba(84, 84, 88, 0.24);
|
||||
--accent: #0A84FF;
|
||||
--accent-hover: #409CFF;
|
||||
--accent-glow: rgba(10, 132, 255, 0.20);
|
||||
--success: #30D158;
|
||||
--success-bg: rgba(48, 209, 88, 0.10);
|
||||
--danger: #FF453A;
|
||||
--danger-bg: rgba(255, 69, 58, 0.08);
|
||||
}
|
||||
|
||||
html {
|
||||
background: var(--bg-primary);
|
||||
transition: background var(--transition-fluid);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
transition: background var(--transition-fluid), color var(--transition-fluid);
|
||||
}
|
||||
|
||||
code, pre, .mono {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.dashboard-bg {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--bg-primary);
|
||||
transition: background var(--transition-fluid);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.page-shell {
|
||||
max-width: 752px;
|
||||
margin: 0 auto;
|
||||
padding: 64px var(--spacing-lg) 48px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 28px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: -0.3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.logo-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
background: var(--accent);
|
||||
display: inline-block;
|
||||
box-shadow: 0 0 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
width: 46px;
|
||||
height: 28px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: background var(--transition-fast), border-color var(--transition-fast);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.theme-toggle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: var(--accent);
|
||||
transition: transform var(--transition-fast);
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .theme-toggle::after {
|
||||
transform: translateX(18px);
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 7px 16px;
|
||||
border-radius: var(--btn-radius);
|
||||
cursor: pointer;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.logout-btn:hover {
|
||||
background: var(--bg-input);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.glass-card h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
letter-spacing: -0.2px;
|
||||
transition: border-color var(--transition-fluid);
|
||||
}
|
||||
|
||||
.status-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(164px, 1fr));
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 14px;
|
||||
transition: background var(--transition-fluid), border-color var(--transition-fluid);
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.status-online { color: var(--success); }
|
||||
|
||||
.status-offline { color: var(--danger); }
|
||||
|
||||
.login-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: var(--bg-primary);
|
||||
transition: background var(--transition-fluid);
|
||||
}
|
||||
|
||||
.login-inner {
|
||||
padding: 52px 44px 44px;
|
||||
}
|
||||
|
||||
.login-inner h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
text-align: center;
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: -0.6px;
|
||||
transition: color var(--transition-fluid);
|
||||
}
|
||||
|
||||
.login-inner .subtitle {
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.login-inner .tagline {
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 6px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--input-radius);
|
||||
color: var(--text-primary);
|
||||
font-size: 15px;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 400;
|
||||
transition: all var(--transition-fast);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-group input::placeholder,
|
||||
.form-group textarea::placeholder {
|
||||
color: var(--text-muted);
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group select:focus,
|
||||
.form-group textarea:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-glow);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 18px;
|
||||
border: none;
|
||||
border-radius: var(--btn-radius);
|
||||
font-size: 15px;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
letter-spacing: -0.2px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: #FFFFFF;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
box-shadow: 0 2px 14px var(--accent-glow);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: var(--bg-input);
|
||||
color: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg-input);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.btn-secondary.active-tab {
|
||||
background: var(--accent-glow);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger);
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--danger);
|
||||
transition: background var(--transition-fluid), color var(--transition-fluid), border-color var(--transition-fluid);
|
||||
}
|
||||
|
||||
.success-msg {
|
||||
background: var(--success-bg);
|
||||
color: var(--success);
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid var(--success);
|
||||
transition: background var(--transition-fluid), color var(--transition-fluid), border-color var(--transition-fluid);
|
||||
}
|
||||
|
||||
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
|
||||
.control-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-bar .btn { padding: 8px 16px; font-size: 13px; }
|
||||
|
||||
.config-form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.config-form .form-group { margin-bottom: 0; }
|
||||
|
||||
.config-form .btn { grid-column: span 2; margin-top: 14px; }
|
||||
|
||||
.gadget-list {
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 14px;
|
||||
transition: background var(--transition-fluid), border-color var(--transition-fluid);
|
||||
}
|
||||
|
||||
.gadget-item {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
font-size: 13px;
|
||||
font-family: var(--font-mono);
|
||||
cursor: pointer;
|
||||
transition: background var(--transition-fast), color var(--transition-fast);
|
||||
}
|
||||
|
||||
.gadget-item:last-child { border-bottom: none; }
|
||||
|
||||
.gadget-item:hover { background: var(--bg-primary); }
|
||||
|
||||
.gadget-item.selected {
|
||||
background: var(--accent-glow);
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.payload-output {
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 14px;
|
||||
white-space: pre-wrap;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
accent-color: var(--accent);
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label.form-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pill-sun, .pill-moon {
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-shell { padding: 48px 16px 32px; }
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
.config-form { grid-template-columns: 1fr; }
|
||||
.config-form .btn { grid-column: span 1; }
|
||||
.login-inner { padding: 36px 28px 32px; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
transition-duration: 0ms !important;
|
||||
animation-duration: 0ms !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { HashRouter } from 'react-router-dom'
|
||||
import { ThemeProvider } from './context/ThemeContext'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider>
|
||||
<HashRouter>
|
||||
<App />
|
||||
</HashRouter>
|
||||
</ThemeProvider>
|
||||
</React.StrictMode>
|
||||
)
|
||||
@@ -0,0 +1,329 @@
|
||||
import { useState, useEffect, useMemo, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import LiquidGlass from 'liquid-glass-react'
|
||||
import { useTheme } from '../context/ThemeContext'
|
||||
import {
|
||||
getStatus, startServers as apiStartServers, getGadgets,
|
||||
generatePayload as apiGenerate, updateConfig, setAuthToken
|
||||
} from '../api'
|
||||
|
||||
export default function Dashboard() {
|
||||
const navigate = useNavigate()
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
const containerRef = useRef(null)
|
||||
const [status, setStatus] = useState({})
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [msg, setMsg] = useState({ success: '', error: '' })
|
||||
const [activeTab, setActiveTab] = useState('servers')
|
||||
const [gadgets, setGadgets] = useState([])
|
||||
const [gadgetSearch, setGadgetSearch] = useState('')
|
||||
const [selectedGadget, setSelectedGadget] = useState('')
|
||||
const [payloadCmd, setPayloadCmd] = useState('')
|
||||
const [payloadResult, setPayloadResult] = useState('')
|
||||
|
||||
const [serverCfg, setServerCfg] = useState({
|
||||
ldap: false, ldaps: false, http: false, rmi: false
|
||||
})
|
||||
|
||||
const [configForm, setConfigForm] = useState({
|
||||
ip: '', ldapPort: 1389, ldapsPort: 1669, httpPort: 3456, rmiPort: 1099, codeBase: ''
|
||||
})
|
||||
|
||||
const filteredGadgets = useMemo(() => {
|
||||
if (!gadgetSearch) return gadgets
|
||||
return gadgets.filter(g => g.name.toLowerCase().includes(gadgetSearch.toLowerCase()))
|
||||
}, [gadgetSearch, gadgets])
|
||||
|
||||
async function loadStatus() {
|
||||
try {
|
||||
const res = await getStatus()
|
||||
setStatus(res.data)
|
||||
setConfigForm(prev => ({
|
||||
...prev,
|
||||
ip: res.data.ip || '',
|
||||
ldapPort: res.data.ldapPort || 1389,
|
||||
ldapsPort: res.data.ldapsPort || 1669,
|
||||
httpPort: res.data.httpPort || 3456,
|
||||
rmiPort: res.data.rmiPort || 1099,
|
||||
codeBase: res.data.codeBase || ''
|
||||
}))
|
||||
} catch (e) {
|
||||
if (e.response && e.response.status === 401) {
|
||||
setAuthToken(null)
|
||||
navigate('/login', { replace: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGadgets() {
|
||||
try {
|
||||
const res = await getGadgets()
|
||||
setGadgets(res.data)
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
async function handleStartServers() {
|
||||
setLoading(true)
|
||||
setMsg({ success: '', error: '' })
|
||||
try {
|
||||
const res = await apiStartServers({
|
||||
ldap: serverCfg.ldap, ldaps: serverCfg.ldaps,
|
||||
http: serverCfg.http, rmi: serverCfg.rmi,
|
||||
ip: configForm.ip, ldapPort: configForm.ldapPort,
|
||||
ldapsPort: configForm.ldapsPort, httpPort: configForm.httpPort,
|
||||
rmiPort: configForm.rmiPort
|
||||
})
|
||||
if (res.data.success) {
|
||||
setMsg({ success: 'Servers starting: ' + (res.data.started.join(', ') || 'none'), error: '' })
|
||||
}
|
||||
setTimeout(loadStatus, 2000)
|
||||
} catch (e) {
|
||||
setMsg({ success: '', error: 'Failed to start servers' })
|
||||
} finally { setLoading(false) }
|
||||
}
|
||||
|
||||
async function handleSaveConfig() {
|
||||
setLoading(true)
|
||||
setMsg({ success: '', error: '' })
|
||||
try {
|
||||
await updateConfig(configForm)
|
||||
setMsg({ success: 'Configuration saved', error: '' })
|
||||
loadStatus()
|
||||
} catch (e) {
|
||||
setMsg({ success: '', error: 'Failed to save configuration' })
|
||||
} finally { setLoading(false) }
|
||||
}
|
||||
|
||||
async function handleGeneratePayload() {
|
||||
setLoading(true)
|
||||
setMsg({ success: '', error: '' })
|
||||
try {
|
||||
const res = await apiGenerate({ gadget: selectedGadget, command: payloadCmd })
|
||||
if (res.data.success) {
|
||||
setPayloadResult(res.data.message || 'Payload generated successfully')
|
||||
setMsg({ success: 'Payload generated', error: '' })
|
||||
} else {
|
||||
setPayloadResult('Error: ' + (res.data.error || 'unknown'))
|
||||
setMsg({ success: '', error: res.data.error || 'Generation failed' })
|
||||
}
|
||||
} catch (e) {
|
||||
setMsg({ success: '', error: 'Failed to generate payload' })
|
||||
} finally { setLoading(false) }
|
||||
}
|
||||
|
||||
function logout() {
|
||||
setAuthToken(null)
|
||||
navigate('/login', { replace: true })
|
||||
}
|
||||
|
||||
useEffect(() => { loadStatus(); loadGadgets() }, [])
|
||||
|
||||
const liquidProps = {
|
||||
mouseContainer: containerRef,
|
||||
elasticity: 0.07,
|
||||
blurAmount: 0.042,
|
||||
saturation: 120,
|
||||
displacementScale: 36,
|
||||
cornerRadius: 24,
|
||||
overLight: theme === 'light',
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
<div className="dashboard-bg" />
|
||||
|
||||
<div className="page-shell">
|
||||
<LiquidGlass {...liquidProps} style={{ marginBottom: 20 }}>
|
||||
<div className="header">
|
||||
<h1>
|
||||
<span className="logo-dot" />
|
||||
JYso
|
||||
</h1>
|
||||
<div className="header-right">
|
||||
<button
|
||||
className="theme-toggle"
|
||||
onClick={toggleTheme}
|
||||
aria-label={theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode'}
|
||||
title={theme === 'light' ? 'Dark mode' : 'Light mode'}
|
||||
>
|
||||
<span className="pill-sun" style={{ position: 'absolute', top: 4, left: 5, opacity: theme === 'dark' ? 0.35 : 1, transition: 'opacity 0.2s' }}>
|
||||
☀️
|
||||
</span>
|
||||
<span className="pill-moon" style={{ position: 'absolute', top: 4, right: 5, opacity: theme === 'dark' ? 1 : 0.35, transition: 'opacity 0.2s' }}>
|
||||
🌙
|
||||
</span>
|
||||
</button>
|
||||
<button className="logout-btn" onClick={logout}>Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</LiquidGlass>
|
||||
|
||||
{msg.success && <div className="success-msg">{msg.success}</div>}
|
||||
{msg.error && <div className="error-msg">{msg.error}</div>}
|
||||
|
||||
<LiquidGlass {...liquidProps} style={{ marginBottom: 20 }}>
|
||||
<div className="glass-card">
|
||||
<h2>Server Status</h2>
|
||||
<div className="status-grid">
|
||||
<div className="status-item">
|
||||
<span className="status-label">LDAP ({status.ldapPort})</span>
|
||||
<span className={'status-value ' + (status.ldapRunning ? 'status-online' : 'status-offline')}>
|
||||
{status.ldapRunning ? 'ONLINE' : 'OFFLINE'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="status-label">LDAPS ({status.ldapsPort})</span>
|
||||
<span className={'status-value ' + (status.ldapsRunning ? 'status-online' : 'status-offline')}>
|
||||
{status.ldapsRunning ? 'ONLINE' : 'OFFLINE'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="status-label">HTTP ({status.httpPort})</span>
|
||||
<span className={'status-value ' + (status.httpRunning ? 'status-online' : 'status-offline')}>
|
||||
{status.httpRunning ? 'ONLINE' : 'OFFLINE'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="status-label">RMI ({status.rmiPort})</span>
|
||||
<span className={'status-value ' + (status.rmiRunning ? 'status-online' : 'status-offline')}>
|
||||
{status.rmiRunning ? 'ONLINE' : 'OFFLINE'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="status-label">IP Address</span>
|
||||
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.ip || '0.0.0.0'}</span>
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="status-label">Version</span>
|
||||
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.version || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LiquidGlass>
|
||||
|
||||
<LiquidGlass {...liquidProps}>
|
||||
<div className="glass-card">
|
||||
<h2>Controls</h2>
|
||||
<div className="control-bar">
|
||||
<button className="btn btn-primary" style={{ width: 'auto' }} onClick={loadStatus}>
|
||||
Refresh Status
|
||||
</button>
|
||||
<button
|
||||
className={'btn btn-secondary' + (activeTab === 'servers' ? ' active-tab' : '')}
|
||||
onClick={() => setActiveTab('servers')}>Servers</button>
|
||||
<button
|
||||
className={'btn btn-secondary' + (activeTab === 'config' ? ' active-tab' : '')}
|
||||
onClick={() => setActiveTab('config')}>Configuration</button>
|
||||
<button
|
||||
className={'btn btn-secondary' + (activeTab === 'payload' ? ' active-tab' : '')}
|
||||
onClick={() => setActiveTab('payload')}>Payload Generator</button>
|
||||
</div>
|
||||
|
||||
{activeTab === 'servers' && (
|
||||
<div>
|
||||
<div className="grid-2">
|
||||
<label className="form-group">
|
||||
<input type="checkbox" checked={serverCfg.ldap}
|
||||
onChange={e => setServerCfg({ ...serverCfg, ldap: e.target.checked })} /> LDAP
|
||||
</label>
|
||||
<label className="form-group">
|
||||
<input type="checkbox" checked={serverCfg.ldaps}
|
||||
onChange={e => setServerCfg({ ...serverCfg, ldaps: e.target.checked })} /> LDAPS
|
||||
</label>
|
||||
<label className="form-group">
|
||||
<input type="checkbox" checked={serverCfg.http}
|
||||
onChange={e => setServerCfg({ ...serverCfg, http: e.target.checked })} /> HTTP
|
||||
</label>
|
||||
<label className="form-group">
|
||||
<input type="checkbox" checked={serverCfg.rmi}
|
||||
onChange={e => setServerCfg({ ...serverCfg, rmi: e.target.checked })} /> RMI
|
||||
</label>
|
||||
</div>
|
||||
<button className="btn btn-primary" style={{ marginTop: 16 }} onClick={handleStartServers} disabled={loading}>
|
||||
{loading ? 'Starting...' : 'Start Selected Servers'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'config' && (
|
||||
<div>
|
||||
<div className="config-form">
|
||||
<div className="form-group">
|
||||
<label>IP Address</label>
|
||||
<input type="text" value={configForm.ip}
|
||||
onChange={e => setConfigForm({ ...configForm, ip: e.target.value })} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>LDAP Port</label>
|
||||
<input type="number" value={configForm.ldapPort}
|
||||
onChange={e => setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>LDAPS Port</label>
|
||||
<input type="number" value={configForm.ldapsPort}
|
||||
onChange={e => setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>HTTP Port</label>
|
||||
<input type="number" value={configForm.httpPort}
|
||||
onChange={e => setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>RMI Port</label>
|
||||
<input type="number" value={configForm.rmiPort}
|
||||
onChange={e => setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>CodeBase URL</label>
|
||||
<input type="text" value={configForm.codeBase} placeholder="http://x.x.x.x:port/"
|
||||
onChange={e => setConfigForm({ ...configForm, codeBase: e.target.value })} />
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={handleSaveConfig} disabled={loading}>
|
||||
{loading ? 'Saving...' : 'Save Configuration'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'payload' && (
|
||||
<div className="grid-2">
|
||||
<div>
|
||||
<h3 style={{ fontSize: 13, marginBottom: 10, color: 'var(--text-secondary)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
||||
Select Gadget
|
||||
</h3>
|
||||
<div className="form-group">
|
||||
<input type="text" placeholder="Search..." value={gadgetSearch}
|
||||
onChange={e => setGadgetSearch(e.target.value)} />
|
||||
</div>
|
||||
<div className="gadget-list">
|
||||
{filteredGadgets.map(g => (
|
||||
<div key={g.name}
|
||||
className={'gadget-item' + (selectedGadget === g.name ? ' selected' : '')}
|
||||
onClick={() => setSelectedGadget(g.name)}>
|
||||
{g.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="form-group">
|
||||
<label>Command</label>
|
||||
<textarea rows={4} value={payloadCmd} placeholder="e.g. whoami"
|
||||
onChange={e => setPayloadCmd(e.target.value)} />
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={handleGeneratePayload}
|
||||
disabled={loading || !selectedGadget}>
|
||||
{loading ? 'Generating...' : 'Generate Payload'}
|
||||
</button>
|
||||
{payloadResult && (
|
||||
<div className="payload-output">{payloadResult}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</LiquidGlass>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import LiquidGlass from 'liquid-glass-react'
|
||||
import { login, setAuthToken } from '../api'
|
||||
import { useTheme } from '../context/ThemeContext'
|
||||
|
||||
export default function Login() {
|
||||
const navigate = useNavigate()
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
const containerRef = useRef(null)
|
||||
const [username, setUsername] = useState('qi')
|
||||
const [password, setPassword] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await login(username, password)
|
||||
if (res.data && res.data.token) {
|
||||
setAuthToken(res.data.token)
|
||||
navigate('/dashboard', { replace: true })
|
||||
} else {
|
||||
setError('Unexpected response from server')
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.response) {
|
||||
if (e.response.status === 401) {
|
||||
setError('Invalid username or password.')
|
||||
} else {
|
||||
setError('Server error: ' + e.response.status)
|
||||
}
|
||||
} else if (e.request) {
|
||||
setError('Cannot connect to server. Is it running?')
|
||||
} else {
|
||||
setError('Error: ' + e.message)
|
||||
}
|
||||
setPassword('')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="login-wrapper" ref={containerRef}>
|
||||
<div style={{ position: 'fixed', top: 22, right: 22, zIndex: 200 }}>
|
||||
<button
|
||||
className="theme-toggle"
|
||||
onClick={toggleTheme}
|
||||
aria-label={theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode'}
|
||||
title={theme === 'light' ? 'Dark mode' : 'Light mode'}
|
||||
/>
|
||||
</div>
|
||||
<LiquidGlass
|
||||
mouseContainer={containerRef}
|
||||
elasticity={0.10}
|
||||
blurAmount={0.045}
|
||||
saturation={128}
|
||||
displacementScale={44}
|
||||
cornerRadius={28}
|
||||
aberrationIntensity={1.8}
|
||||
overLight={theme === 'light'}
|
||||
style={{ width: 380 }}
|
||||
>
|
||||
<div className="login-inner">
|
||||
<h1>
|
||||
<span className="logo-dot" style={{ width: 12, height: 12, borderRadius: 6 }} />
|
||||
JYso
|
||||
</h1>
|
||||
<p className="subtitle">JNDI Exploitation Toolkit</p>
|
||||
<p className="tagline">Sign in to access the dashboard</p>
|
||||
{error && <div className="error-msg">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label>Username</label>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
placeholder="Enter username"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
placeholder="Enter password"
|
||||
/>
|
||||
</div>
|
||||
<button className="btn btn-primary" type="submit" disabled={loading}>
|
||||
{loading ? 'Logging in...' : 'Sign In'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</LiquidGlass>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: '../resources/static',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.qi4l.JYso.web;
|
||||
|
||||
import com.qi4l.JYso.HTTPServer;
|
||||
import com.qi4l.JYso.LdapServer;
|
||||
import com.qi4l.JYso.LdapsServer;
|
||||
import com.qi4l.JYso.RMIServer;
|
||||
import com.qi4l.JYso.gadgets.Config.Config;
|
||||
import com.qi4l.JYso.web.config.JYsoWebPasswordProvider;
|
||||
import com.qi4l.JYso.web.config.WebPasswordGenerator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static com.qi4l.JYso.gadgets.Config.Config.logo;
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = "com.qi4l.JYso.web")
|
||||
public class JYsoWebApplication {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(JYsoWebApplication.class);
|
||||
|
||||
public static String webPassword;
|
||||
public static boolean jndiServersStarted = false;
|
||||
|
||||
public static void start(String[] args) {
|
||||
logo();
|
||||
|
||||
webPassword = WebPasswordGenerator.generatePassword();
|
||||
JYsoWebPasswordProvider.setPassword(webPassword);
|
||||
|
||||
Config.applyCmdArgs(filterWebArgs(args));
|
||||
|
||||
int webPort = 8080;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if ("-wP".equals(args[i]) || "--webPort".equals(args[i])) {
|
||||
if (i + 1 < args.length) {
|
||||
webPort = Integer.parseInt(args[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(ansi().render("@|green [+]|@ Web GUI starting >> http://127.0.0.1:" + webPort));
|
||||
System.out.println(ansi().render("@|yellow [+]|@ ============================================"));
|
||||
System.out.println(ansi().render("@|yellow [+]|@ Username: qi"));
|
||||
System.out.println(ansi().render("@|yellow [+]|@ Password: " + webPassword));
|
||||
System.out.println(ansi().render("@|yellow [+]|@ ============================================"));
|
||||
|
||||
startJndiServers(args);
|
||||
|
||||
Properties props = new Properties();
|
||||
props.put("server.port", String.valueOf(webPort));
|
||||
props.put("spring.main.banner-mode", "off");
|
||||
props.put("spring.main.web-application-type", "servlet");
|
||||
|
||||
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(JYsoWebApplication.class)
|
||||
.web(WebApplicationType.SERVLET)
|
||||
.properties(props)
|
||||
.run(filterWebArgs(args));
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
log.info("Shutting down...");
|
||||
ctx.close();
|
||||
latch.countDown();
|
||||
}));
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] filterWebArgs(String[] args) {
|
||||
java.util.List<String> filtered = new java.util.ArrayList<>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
if ("-w".equals(arg) || "--web".equals(arg)) {
|
||||
} else if ("-wP".equals(arg) || "--webPort".equals(arg)) {
|
||||
i++;
|
||||
} else if ("--with-jndi".equals(arg)) {
|
||||
} else {
|
||||
filtered.add(arg);
|
||||
}
|
||||
}
|
||||
return filtered.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static void startJndiServers(String[] args) {
|
||||
for (String arg : args) {
|
||||
if ("--with-jndi".equals(arg)) {
|
||||
jndiServersStarted = true;
|
||||
new Thread(() -> {
|
||||
try { LdapServer.start(); } catch (Exception e) { log.error("LDAP start failed", e); }
|
||||
}, "ldap-server").start();
|
||||
new Thread(() -> {
|
||||
try { HTTPServer.start(); } catch (Exception e) { log.error("HTTP start failed", e); }
|
||||
}, "http-server").start();
|
||||
new Thread(() -> {
|
||||
try { RMIServer.start(); } catch (Exception e) { log.error("RMI start failed", e); }
|
||||
}, "rmi-server").start();
|
||||
if (Config.TLSProxy) {
|
||||
new Thread(() -> {
|
||||
try { LdapsServer.start(); } catch (Exception e) { log.error("LDAPS start failed", e); }
|
||||
}, "ldaps-server").start();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.qi4l.JYso.web.config;
|
||||
|
||||
public class JYsoWebPasswordProvider {
|
||||
|
||||
private static String password;
|
||||
|
||||
public static void setPassword(String pwd) {
|
||||
password = pwd;
|
||||
}
|
||||
|
||||
public static String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.qi4l.JYso.web.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InMemoryUserDetailsManager userDetailsService() {
|
||||
UserDetails user = User.builder()
|
||||
.username("qi")
|
||||
.password(passwordEncoder().encode(JYsoWebPasswordProvider.getPassword()))
|
||||
.roles("ADMIN")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(user);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.httpBasic().disable()
|
||||
.formLogin().disable()
|
||||
.logout().disable()
|
||||
.addFilterBefore(new TokenAuthFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.qi4l.JYso.web.config;
|
||||
|
||||
import com.qi4l.JYso.web.controller.AuthController;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class TokenAuthFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
String path = request.getRequestURI();
|
||||
if (!path.startsWith("/api/") || path.equals("/api/auth/login")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().write("{\"error\":\"Unauthorized\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
if (!AuthController.validateToken(token)) {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().write("{\"error\":\"Invalid token\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.qi4l.JYso.web.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/api/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE")
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations("classpath:/static/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/").setViewName("forward:/index.html");
|
||||
registry.addViewController("/{path:[^\\.]*}").setViewName("forward:/index.html");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.qi4l.JYso.web.config;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class WebPasswordGenerator {
|
||||
|
||||
private static final String CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$%^&*";
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
public static String generatePassword() {
|
||||
StringBuilder sb = new StringBuilder(13);
|
||||
for (int i = 0; i < 13; i++) {
|
||||
sb.append(CHARS.charAt(RANDOM.nextInt(CHARS.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.qi4l.JYso.web.controller;
|
||||
|
||||
import com.qi4l.JYso.HTTPServer;
|
||||
import com.qi4l.JYso.LdapServer;
|
||||
import com.qi4l.JYso.LdapsServer;
|
||||
import com.qi4l.JYso.RMIServer;
|
||||
import com.qi4l.JYso.Starter;
|
||||
import com.qi4l.JYso.gadgets.Config.Config;
|
||||
import com.qi4l.JYso.gadgets.Config.ysoserial;
|
||||
import com.qi4l.JYso.gadgets.ObjectPayload;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class ApiController {
|
||||
|
||||
@GetMapping("/status")
|
||||
public Map<String, Object> getStatus() {
|
||||
Map<String, Object> status = new LinkedHashMap<>();
|
||||
status.put("ldapRunning", LdapServer.isRunning);
|
||||
status.put("ldapsRunning", LdapsServer.isRunning);
|
||||
status.put("httpRunning", HTTPServer.isRunning);
|
||||
status.put("rmiRunning", RMIServer.isRunning);
|
||||
status.put("ip", Config.ip);
|
||||
status.put("ldapPort", Config.ldapPort);
|
||||
status.put("ldapsPort", Config.ldapsPort);
|
||||
status.put("httpPort", Config.httpPort);
|
||||
status.put("rmiPort", Config.rmiPort);
|
||||
status.put("codeBase", Config.codeBase);
|
||||
status.put("version", "1.3.7");
|
||||
return status;
|
||||
}
|
||||
|
||||
@PostMapping("/servers/start")
|
||||
public Map<String, Object> startServers(@RequestBody Map<String, Object> body) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
List<String> started = new ArrayList<>();
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
boolean startLdap = body.get("ldap") != null && Boolean.TRUE.equals(body.get("ldap"));
|
||||
boolean startLdaps = body.get("ldaps") != null && Boolean.TRUE.equals(body.get("ldaps"));
|
||||
boolean startHttp = body.get("http") != null && Boolean.TRUE.equals(body.get("http"));
|
||||
boolean startRmi = body.get("rmi") != null && Boolean.TRUE.equals(body.get("rmi"));
|
||||
|
||||
if (body.containsKey("ip")) Config.ip = (String) body.get("ip");
|
||||
if (body.containsKey("ldapPort")) Config.ldapPort = (Integer) body.get("ldapPort");
|
||||
if (body.containsKey("ldapsPort")) Config.ldapsPort = (Integer) body.get("ldapsPort");
|
||||
if (body.containsKey("httpPort")) Config.httpPort = (Integer) body.get("httpPort");
|
||||
if (body.containsKey("rmiPort")) Config.rmiPort = (Integer) body.get("rmiPort");
|
||||
|
||||
if (startLdap && !LdapServer.isRunning) {
|
||||
new Thread(() -> {
|
||||
try { LdapServer.start(); } catch (Exception ignored) {}
|
||||
}, "ldap-starter").start();
|
||||
started.add("LDAP");
|
||||
}
|
||||
if (startHttp && !HTTPServer.isRunning) {
|
||||
new Thread(() -> {
|
||||
try { HTTPServer.start(); } catch (Exception ignored) {}
|
||||
}, "http-starter").start();
|
||||
started.add("HTTP");
|
||||
}
|
||||
if (startLdaps && !LdapsServer.isRunning) {
|
||||
new Thread(() -> {
|
||||
try { LdapsServer.start(); } catch (Exception ignored) {}
|
||||
}, "ldaps-starter").start();
|
||||
started.add("LDAPS");
|
||||
}
|
||||
if (startRmi && !RMIServer.isRunning) {
|
||||
new Thread(() -> {
|
||||
try { RMIServer.start(); } catch (Exception ignored) {}
|
||||
}, "rmi-starter").start();
|
||||
started.add("RMI");
|
||||
}
|
||||
|
||||
result.put("started", started);
|
||||
result.put("errors", errors);
|
||||
result.put("success", true);
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/gadgets")
|
||||
public List<Map<String, Object>> listGadgets() {
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
for (String name : Starter.caseInsensitiveObjectPayloadMap.keySet()) {
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
String shortName = name.substring(name.lastIndexOf('.') + 1);
|
||||
item.put("name", shortName);
|
||||
item.put("fullName", name);
|
||||
list.add(item);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@PostMapping("/payload/generate")
|
||||
public Map<String, Object> generatePayload(@RequestBody Map<String, Object> body) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
|
||||
String gadget = (String) body.get("gadget");
|
||||
String command = (String) body.get("command");
|
||||
|
||||
if (gadget == null || command == null) {
|
||||
result.put("success", false);
|
||||
result.put("error", "gadget and command are required");
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
Starter.JYsoMode = true;
|
||||
String[] args = new String[]{
|
||||
"-y",
|
||||
"-g", gadget,
|
||||
"-p", command
|
||||
};
|
||||
ysoserial.run(args);
|
||||
result.put("success", true);
|
||||
result.put("message", "Payload generated successfully");
|
||||
} catch (Exception e) {
|
||||
result.put("success", false);
|
||||
result.put("error", e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/config/update")
|
||||
public Map<String, Object> updateConfig(@RequestBody Map<String, Object> body) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
|
||||
if (body.containsKey("ip")) Config.ip = (String) body.get("ip");
|
||||
if (body.containsKey("ldapPort")) Config.ldapPort = (Integer) body.get("ldapPort");
|
||||
if (body.containsKey("ldapsPort")) Config.ldapsPort = (Integer) body.get("ldapsPort");
|
||||
if (body.containsKey("httpPort")) Config.httpPort = (Integer) body.get("httpPort");
|
||||
if (body.containsKey("rmiPort")) Config.rmiPort = (Integer) body.get("rmiPort");
|
||||
if (body.containsKey("command")) Config.command = (String) body.get("command");
|
||||
if (body.containsKey("codeBase")) Config.codeBase = (String) body.get("codeBase");
|
||||
|
||||
result.put("success", true);
|
||||
result.put("config", getStatus());
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/config")
|
||||
public Map<String, Object> getConfig() {
|
||||
return getStatus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.qi4l.JYso.web.controller;
|
||||
|
||||
import com.qi4l.JYso.web.config.JYsoWebPasswordProvider;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
private static final ConcurrentHashMap<String, String> tokens = new ConcurrentHashMap<>();
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@RequestBody Map<String, String> body) {
|
||||
String username = body.get("username");
|
||||
String password = body.get("password");
|
||||
|
||||
if (!"qi".equals(username) || !JYsoWebPasswordProvider.getPassword().equals(password)) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("error", "Invalid credentials");
|
||||
return ResponseEntity.status(401).body(error);
|
||||
}
|
||||
|
||||
String token = UUID.randomUUID().toString();
|
||||
tokens.put(token, username);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
result.put("username", username);
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
public static boolean validateToken(String token) {
|
||||
return token != null && tokens.containsKey(token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
spring.application.name=JYso
|
||||
server.port=8080
|
||||
spring.main.web-application-type=servlet
|
||||
spring.main.banner-mode=off
|
||||
|
||||
logging.level.root=ERROR
|
||||
logging.level.org.springframework=ERROR
|
||||
logging.level.org.apache=ERROR
|
||||
logging.level.org.hibernate=ERROR
|
||||
logging.level.org.eclipse=ERROR
|
||||
logging.level.org.jboss=ERROR
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="OFF">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%msg%n" />
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="error">
|
||||
<AppenderRef ref="Console" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
Reference in New Issue
Block a user