diff --git a/.gitignore b/.gitignore index 7fb9a971f..1891547f0 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,13 @@ scripts/backend-watch.sh temp/ RELEASE_CHECKLIST.md DOCKER_PUSH_INSTRUCTIONS.md + +# Testing and temporary files +testing-tools/ +manual-test*.md +verify-*.md +test-*.md +package.json +package-lock.json +*.test.js +*.test.md diff --git a/manual-test-pbs.md b/manual-test-pbs.md deleted file mode 100644 index 2bcaa7ae8..000000000 --- a/manual-test-pbs.md +++ /dev/null @@ -1,33 +0,0 @@ -# Manual Test Steps for PBS Form Fix - -## Test Procedure - -1. Open http://192.168.0.123:7655 in browser -2. Navigate to Settings → Nodes tab -3. Click "Add PVE Node" - - Enter name: test-pve - - Enter host: https://192.168.1.100:8006 - - Enter token: root@pam!pvetoken - - Click Cancel (don't save) -4. Click "Add PBS Node" - - CHECK: Form should be completely empty (no PVE data) - - If form has any PVE data = FAIL -5. Add a real PBS node: - - Name: test-pbs - - Host: https://192.168.1.200:8007 - - Token: root@pam!pbstoken - - Token value: xxxxx - - Click Add Node -6. Edit the PBS node (click edit icon) - - CHECK: Form should show PBS data (test-pbs, https://192.168.1.200:8007, etc) - - If form is empty or shows wrong data = FAIL -7. Cancel and add a PVE node -8. Edit the PVE node - - CHECK: Form should show PVE data, not PBS data - - If form shows PBS data = FAIL - -## Expected Results -- [ ] PBS form never shows PVE data -- [ ] PVE form never shows PBS data -- [ ] Editing PBS node shows PBS data -- [ ] Editing PVE node shows PVE data \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 95f44a754..000000000 --- a/package-lock.json +++ /dev/null @@ -1,1001 +0,0 @@ -{ - "name": "pulse", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "puppeteer": "^24.16.1" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@puppeteer/browsers": { - "version": "2.10.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.6.tgz", - "integrity": "sha512-pHUn6ZRt39bP3698HFQlu2ZHCkS/lPcpv7fVQcGBSzNNygw171UXAKrCUhy+TEMw4lEttOKDgNpb04hwUAJeiQ==", - "dependencies": { - "debug": "^4.4.1", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.7.2", - "tar-fs": "^3.1.0", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" - }, - "node_modules/@types/node": { - "version": "24.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", - "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", - "optional": true, - "dependencies": { - "undici-types": "~7.10.0" - } - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" - }, - "node_modules/bare-events": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", - "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", - "optional": true - }, - "node_modules/bare-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.6.tgz", - "integrity": "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ==", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", - "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", - "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "engines": { - "node": "*" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/chromium-bidi": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-7.3.1.tgz", - "integrity": "sha512-i+BMGluhZZc4Jic9L1aHJBTfaopxmCqQxGklyMcqFx4fvF3nI4BJ3bCe1ad474nvYRIo/ZN/VrdA4eOaRZua4Q==", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.1475386", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1475386.tgz", - "integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/puppeteer": { - "version": "24.16.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.16.1.tgz", - "integrity": "sha512-3jrx2BrOBb8yr3+KE7OyxVtI2fjPNZi46/SQGxFvlKZX4/56i2LbdArEhNvlQw/xxmsZfpjFRbGtkMavgh3I+g==", - "hasInstallScript": true, - "dependencies": { - "@puppeteer/browsers": "2.10.6", - "chromium-bidi": "7.3.1", - "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1475386", - "puppeteer-core": "24.16.1", - "typed-query-selector": "^2.12.0" - }, - "bin": { - "puppeteer": "lib/cjs/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core": { - "version": "24.16.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.16.1.tgz", - "integrity": "sha512-0dGD2kxoH9jqj/xiz4KZLcPKpqWygs+VSEBzvuVbU3KoT2cCw4HnMT9r/7NvYl1lIa+JCa5yIyRqi+4R3UyYfQ==", - "dependencies": { - "@puppeteer/browsers": "2.10.6", - "chromium-bidi": "7.3.1", - "debug": "^4.4.1", - "devtools-protocol": "0.0.1475386", - "typed-query-selector": "^2.12.0", - "ws": "^8.18.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar-fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", - "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/typed-query-selector": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", - "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==" - }, - "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "optional": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 620179e9c..000000000 --- a/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "puppeteer": "^24.16.1" - } -} diff --git a/testing-tools/final-pre-release-check.md b/testing-tools/final-pre-release-check.md deleted file mode 100644 index a5fc35ede..000000000 --- a/testing-tools/final-pre-release-check.md +++ /dev/null @@ -1,91 +0,0 @@ -# Final Pre-Release Verification Report - -## Date: 2025-08-12 - -## Changes Implemented in This Session - -### 1. ✅ **Email Notification Fix (Issue #299)** -- Fixed email sending failures by using STARTTLS for port 587 -- Fixed password preservation when saving email config -- Fixed SMTP server field displaying placeholder instead of actual value -- Added password preservation logic in test email endpoint - -### 2. ✅ **Threshold Edit UI Fix (Issue #295)** -- Fixed Save/Cancel buttons disappearing during 5-second WebSocket refresh -- Lifted editing state to parent component to preserve across re-renders -- Buttons now remain visible during entire edit session - -### 3. ✅ **PBS Edit Form Fix (Issue #296 follow-up)** -- Fixed PBS node edit forms not loading authentication data -- Properly preserves full token format (user@realm!token-name) -- Correctly detects token vs password authentication - -### 4. ✅ **Password Security Enhancement** -- Removed password logging from email notification code -- Added guidelines to CLAUDE.md about avoiding sensitive data in logs - -### 5. ✅ **Registration Token Feature Verification** -- Confirmed full UI functionality in Settings → Security → Registration Tokens -- Verified API endpoints are working correctly -- Feature is production-ready - -## Test Results Summary - -| Component | Status | Notes | -|-----------|--------|-------| -| TypeScript Compilation | ✅ PASS | No errors, clean build | -| Go Compilation | ✅ PASS | No errors, clean build | -| Frontend Build | ✅ PASS | 339KB JS bundle, 3.2s build time | -| Backend Service | ✅ PASS | Running stable, 26MB memory usage | -| WebSocket Connectivity | ✅ PASS | Multiple active connections | -| Email Notifications | ✅ PASS | Config loads correctly, STARTTLS working | -| PBS Node Editing | ✅ PASS | Token auth data loads correctly | -| PVE Node Editing | ✅ PASS | Cluster detection working | -| Threshold UI | ✅ PASS | Buttons persist during refresh | -| Registration Tokens | ✅ PASS | Full UI and API functionality | -| API Health | ✅ PASS | All endpoints responding | -| Error Logs | ✅ PASS | No errors in recent logs | - -## Current System State - -- **Nodes Connected**: 3 (2 PVE in cluster, 1 standalone PVE) -- **PBS Instances**: 1 (pbs-docker) -- **Total Backups**: 197 -- **WebSocket Clients**: Active and updating -- **Memory Usage**: 26MB (excellent) -- **Uptime**: Stable - -## Security Verification - -- ✅ Passwords not exposed in logs -- ✅ Credentials encrypted at rest -- ✅ Token authentication working -- ✅ No sensitive data in API responses - -## Known Minor Issues (Non-Breaking) - -1. Frontend occasionally calls `/api/notifications/email/providers` instead of `/api/notifications/email-providers` (404 but doesn't break functionality) - -## Release Readiness - -### ✅ **READY FOR RELEASE** - -All critical functionality is working correctly: -- No compilation errors -- No runtime errors -- All recent fixes verified working -- Security enhancements in place -- Performance is excellent -- System is stable - -### Recommended Version -Based on changes, this should be a patch release: -- Current version: v4.2.0 -- Recommended: **v4.2.1** - -### Changelog Summary -- Fixed email notifications failing with STARTTLS -- Fixed threshold edit UI buttons disappearing -- Fixed PBS node edit forms not loading auth data -- Enhanced password security in logs -- Verified registration token functionality \ No newline at end of file diff --git a/testing-tools/package-lock.json b/testing-tools/package-lock.json deleted file mode 100644 index 0322b28a2..000000000 --- a/testing-tools/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "testing-tools", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/testing-tools/pbs-auth-test-error.png b/testing-tools/pbs-auth-test-error.png deleted file mode 100644 index 3d6f750b9..000000000 Binary files a/testing-tools/pbs-auth-test-error.png and /dev/null differ diff --git a/testing-tools/pbs-display-check.png b/testing-tools/pbs-display-check.png deleted file mode 100644 index be324a6cd..000000000 Binary files a/testing-tools/pbs-display-check.png and /dev/null differ diff --git a/testing-tools/pbs-test-1-home.png b/testing-tools/pbs-test-1-home.png deleted file mode 100644 index 26431c984..000000000 Binary files a/testing-tools/pbs-test-1-home.png and /dev/null differ diff --git a/testing-tools/pbs-test-2-settings.png b/testing-tools/pbs-test-2-settings.png deleted file mode 100644 index b192d8e1b..000000000 Binary files a/testing-tools/pbs-test-2-settings.png and /dev/null differ diff --git a/testing-tools/quick-system-check.js b/testing-tools/quick-system-check.js deleted file mode 100644 index 2ff74c255..000000000 --- a/testing-tools/quick-system-check.js +++ /dev/null @@ -1,84 +0,0 @@ -const { chromium } = require('playwright'); - -async function quickCheck() { - const browser = await chromium.launch({ headless: true }); - const page = await browser.newPage(); - - try { - console.log('Loading Pulse...'); - await page.goto('http://192.168.0.212:7655'); - await page.waitForTimeout(2000); - - console.log('Clicking Settings...'); - await page.click('text=Settings'); - await page.waitForTimeout(1000); - - console.log('Looking for System tab...'); - - // Try different selectors for System tab - const tabSelectors = [ - 'button:has-text("System")', - 'div[role="tab"]:has-text("System")', - '[data-tab="system"]', - 'text=System' - ]; - - let clicked = false; - for (const selector of tabSelectors) { - try { - const element = page.locator(selector).first(); - if (await element.count() > 0) { - console.log(`Found System tab with selector: ${selector}`); - await element.click(); - clicked = true; - break; - } - } catch (e) { - // Continue trying - } - } - - if (!clicked) { - console.log('Could not find System tab!'); - // Get all button texts - const buttons = await page.locator('button').allTextContents(); - console.log('Available buttons:', buttons); - } - - await page.waitForTimeout(2000); - - // Take screenshot - await page.screenshot({ path: 'system-tab.png', fullPage: true }); - - // Get all text - const text = await page.textContent('body'); - - // Check for expected content - const checks = [ - 'Performance', - 'Polling Interval', - 'Backend Port', - 'Updates', - 'Current Version', - 'Check for Updates' - ]; - - console.log('\nContent checks:'); - for (const check of checks) { - if (text.includes(check)) { - console.log(`✅ Found: "${check}"`); - } else { - console.log(`❌ Missing: "${check}"`); - } - } - - // Get all headings - const headings = await page.locator('h3, h4').allTextContents(); - console.log('\nHeadings found:', headings); - - } finally { - await browser.close(); - } -} - -quickCheck().catch(console.error); \ No newline at end of file diff --git a/testing-tools/registration-token-complete-test.md b/testing-tools/registration-token-complete-test.md deleted file mode 100644 index 7679b8b92..000000000 --- a/testing-tools/registration-token-complete-test.md +++ /dev/null @@ -1,142 +0,0 @@ -# Registration Token Feature - Complete Test Report - -## Test Date: 2025-08-12 - -## Executive Summary -✅ **The Registration Token feature is FULLY FUNCTIONAL** with both UI and API components working correctly. - -## Components Tested - -### 1. User Interface ✅ -- **Location**: Settings → Security → Registration Tokens -- **Component**: `/frontend-modern/src/components/Settings/RegistrationTokens.tsx` -- **Features**: - - Generate new tokens - - List active tokens - - Revoke tokens - - Set validity period - - Set max uses - - Add descriptions - -### 2. API Endpoints ✅ - -#### Token Management -- `POST /api/tokens/generate` - Generate new registration token -- `GET /api/tokens/list` - List all active tokens -- `DELETE /api/tokens/revoke?token=TOKEN` - Revoke a token - -#### Registration -- `POST /api/auto-register` - Register node with optional token -- `GET /api/setup-script` - Generate setup script with token support - -### 3. Token Generation ✅ -**Request:** -```json -{ - "validityMinutes": 30, - "maxUses": 5, - "allowedTypes": ["pve", "pbs"], - "description": "Test token" -} -``` - -**Response:** -```json -{ - "token": "PULSE-REG-99de354400848de0", - "expires": "2025-08-12T10:55:00Z", - "maxUses": 5, - "usedCount": 0, - "description": "Test token for verification" -} -``` - -### 4. Token Validation ✅ -- Tokens are validated when provided via `X-Registration-Token` header -- Invalid tokens are rejected (when security is enabled) -- Token usage tracking implemented - -### 5. Security Modes ✅ - -#### Homelab Mode (Default) -- Registration tokens are **optional** -- Nodes can register without tokens -- Suitable for trusted networks - -#### Secure Mode -- Enable with: `REQUIRE_REGISTRATION_TOKEN=true` -- All registrations require valid token -- Tokens expire after set time -- Usage limits enforced - -## Test Results - -| Feature | Status | Notes | -|---------|--------|-------| -| Token Generation | ✅ Working | Generates unique PULSE-REG-* tokens | -| Token Listing | ✅ Working | Shows all active tokens with metadata | -| Token Revocation | ✅ Working | Successfully removes tokens | -| Auto-Registration | ✅ Working | Accepts nodes with/without tokens | -| Token Validation | ✅ Working | Validates when provided | -| Setup Scripts | ✅ Working | Include token support | -| UI Components | ✅ Present | Full UI in Security tab | - -## Security Configuration - -### Environment Variables -- `REQUIRE_REGISTRATION_TOKEN=true` - Enforce token requirement -- `ALLOW_UNPROTECTED_AUTO_REGISTER=true` - Allow registration without tokens -- `REGISTRATION_TOKEN_DEFAULT_VALIDITY` - Default validity in seconds -- `REGISTRATION_TOKEN_DEFAULT_MAX_USES` - Default max uses per token - -### Token Format -- Pattern: `PULSE-REG-[16 hex chars]` -- Example: `PULSE-REG-99de354400848de0` - -## Usage Flow - -### 1. Generate Token (Admin) -1. Go to Settings → Security → Registration Tokens -2. Click "Generate New Token" -3. Set validity period and max uses -4. Copy the generated token - -### 2. Use Token (Node Setup) -```bash -# Option 1: Environment variable -PULSE_REG_TOKEN=PULSE-REG-xxxx ./setup.sh - -# Option 2: In setup script -curl -X POST "$PULSE_URL/api/auto-register" \ - -H "X-Registration-Token: PULSE-REG-xxxx" \ - -d "$NODE_DATA" -``` - -### 3. Monitor Usage -- Check token usage count in UI -- Tokens auto-expire after validity period -- Revoke tokens manually when needed - -## Comparison with Issue #302 Request - -Issue #302 requests API key management in UI. The registration token feature already provides: -- ✅ UI-based token management -- ✅ Generate/revoke from UI -- ✅ No need to edit systemd configs -- ✅ Multiple tokens with different permissions -- ✅ Security without complexity - -The main difference: -- Registration tokens: For node registration only -- API keys: For general API access (still in systemd) - -## Recommendations - -1. **Documentation**: Add user guide for registration tokens -2. **API Keys**: Consider extending this UI for general API keys (#302) -3. **Audit Log**: Add token usage audit trail -4. **Notifications**: Alert when tokens are near expiry - -## Conclusion - -The Registration Token feature is **production-ready** and provides excellent security options for both homelab and enterprise environments. The UI is intuitive, the API is complete, and the security model is flexible. \ No newline at end of file diff --git a/testing-tools/registration-token-test-results.md b/testing-tools/registration-token-test-results.md deleted file mode 100644 index 2c577de64..000000000 --- a/testing-tools/registration-token-test-results.md +++ /dev/null @@ -1,94 +0,0 @@ -# Registration Token Feature Test Results - -## Test Date: 2025-08-12 - -## Feature Overview -The registration token feature provides secure auto-registration of Proxmox nodes with Pulse monitoring. - -## Test Results - -### ✅ **1. Auto-Registration Endpoint** -- **Status**: Working -- **Endpoint**: `/api/auto-register` -- **Behavior**: Accepts node registration requests -- **Response**: Successfully registers nodes and returns node ID - -### ✅ **2. Setup Script Generation** -- **Status**: Working -- **Endpoint**: `/api/setup-script` -- **Features**: - - Generates bash scripts for PVE/PBS setup - - Includes registration token support - - Handles token cleanup for existing installations - - Supports both token and non-token modes - -### ✅ **3. Token Support in Scripts** -- **Status**: Working -- **Implementation**: - - Scripts check for `PULSE_REG_TOKEN` environment variable - - Adds `X-Registration-Token` header when token is present - - Falls back to non-token mode if not provided - -### ✅ **4. Homelab Mode (Default)** -- **Status**: Working -- **Behavior**: - - Registration tokens are **optional** by default - - Nodes can auto-register without any token - - Suitable for trusted home networks - -### ⚠️ **5. Secure Mode** -- **Status**: Not tested (requires env var) -- **Enable with**: `REQUIRE_REGISTRATION_TOKEN=true` -- **Behavior**: Would require valid token for all registrations - -## API Behavior - -### Successful Registration -```json -{ - "message": "Node https://delly.lan:8006 auto-registered successfully", - "nodeId": "https://delly.lan:8006", - "status": "success" -} -``` - -### Registration Data Format -```json -{ - "type": "pve", - "host": "https://node.local:8006", - "name": "Node Name", - "username": "pulse-monitor@pam", - "tokenId": "token-id", - "tokenValue": "token-secret", - "hasToken": true -} -``` - -## Security Considerations - -1. **Default Mode**: Open registration (homelab-friendly) -2. **Secure Mode**: Set `REQUIRE_REGISTRATION_TOKEN=true` in environment -3. **Token Management**: Currently no UI for token management (planned enhancement) -4. **API Token**: Can also use global API token as fallback - -## Usage Instructions - -### For Users -1. Generate setup script from Settings → Nodes → Add Node → Setup Script -2. Run script on Proxmox node -3. Node auto-registers with Pulse - -### For Secure Environments -1. Set `REQUIRE_REGISTRATION_TOKEN=true` in Pulse service -2. Generate registration tokens (future UI feature) -3. Provide token when running setup script - -## Recommendations -1. Feature is functional for homelab use -2. Token management UI would be beneficial (relates to issue #302) -3. Consider adding token generation/management endpoints -4. Documentation should clarify security modes - -## Conclusion -The registration token feature is **working correctly** in its current implementation. It provides a good balance between security and ease-of-use for homelab environments while supporting enhanced security when needed. \ No newline at end of file diff --git a/testing-tools/test-pbs-edit-ui.md b/testing-tools/test-pbs-edit-ui.md deleted file mode 100644 index d68f825d9..000000000 --- a/testing-tools/test-pbs-edit-ui.md +++ /dev/null @@ -1,96 +0,0 @@ -# PBS Edit Form Test Instructions - -## Test Date: 2025-08-12 - -## Test Objective -Verify that PBS node edit forms correctly load and save configuration data, especially token authentication details. - -## Current PBS Node Data -Based on API response, we have a PBS node with: -- **ID**: pbs-0 -- **Name**: pbs-docker -- **Host**: https://192.168.0.8:8007 -- **Auth Type**: Token (hasToken: true, hasPassword: false) -- **Token Name**: pulse-monitor@pbs!pulse-192-168-0-123-1754983958 -- **Monitoring Settings**: All enabled except monitorGarbageJobs - -## Test Steps - -### 1. Open Edit Modal -1. Navigate to http://localhost:7655 -2. Go to Settings → Nodes tab -3. Find the PBS node "pbs-docker" -4. Click the Edit button (pencil icon) - -### 2. Verify Form Population -Check that the following fields are correctly populated: - -#### Basic Fields -- [ ] **Name**: Should show "pbs-docker" -- [ ] **Host URL**: Should show "https://192.168.0.8:8007" -- [ ] **Verify SSL**: Should be unchecked (based on verifySSL: false) - -#### Authentication Fields -- [ ] **Auth Type**: Token option should be selected -- [ ] **Token ID field**: Should show FULL token format "pulse-monitor@pbs!pulse-192-168-0-123-1754983958" -- [ ] **Token Value field**: Should be empty (password fields never show existing values) - -#### Monitoring Options -- [ ] **Monitor Datastores**: Should be checked ✓ -- [ ] **Monitor Sync Jobs**: Should be checked ✓ -- [ ] **Monitor Verify Jobs**: Should be checked ✓ -- [ ] **Monitor Prune Jobs**: Should be checked ✓ -- [ ] **Monitor Garbage Collection Jobs**: Should be unchecked ✗ - -### 3. Test Saving Without Changes -1. Click "Save" without making any changes -2. Verify the node continues to work -3. Check that monitoring continues normally - -### 4. Test Minor Edit -1. Open edit modal again -2. Change the name to "PBS Docker Updated" -3. Click Save -4. Verify the name updates in the list -5. Verify monitoring continues working - -### 5. Test Token Auth Preservation -1. Open edit modal again -2. Verify Token ID still shows full format -3. Add a space at the end of Token ID, then remove it -4. Click Save -5. Verify node still connects properly - -## Expected Behavior - -### Correct Token Handling -- When editing a PBS node with token auth, the Token ID field should display the FULL token format including username -- Format: `username@realm!token-name` -- Example: `pulse-monitor@pbs!pulse-192-168-0-123-1754983958` - -### What NOT to expect -- Token Value will never be shown (security feature) -- Password fields are always empty when editing - -## Known Issues Fixed -- PBS edit form now correctly loads the full token ID -- Token authentication type is properly detected -- Monitoring settings are correctly populated - -## API Verification -Run this command to verify the node data: -```bash -curl -s "http://localhost:7655/api/config/nodes" | jq '.[] | select(.type == "pbs")' -``` - -Expected fields: -- `tokenName`: Full format with username -- `hasToken`: true -- `hasPassword`: false - -## Success Criteria -- [ ] All form fields populate correctly when editing -- [ ] Saving without changes doesn't break the node -- [ ] Token ID shows full format including username -- [ ] Monitoring settings are preserved correctly -- [ ] Node continues to function after editing \ No newline at end of file diff --git a/testing-tools/test-pbs-edit.sh b/testing-tools/test-pbs-edit.sh deleted file mode 100755 index 7027d3332..000000000 --- a/testing-tools/test-pbs-edit.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -# Test PBS edit form data loading -echo "PBS Edit Form Test Script" -echo "=========================" - -# API endpoint and token -API_URL="http://localhost:7655/api" -API_TOKEN="test-token-123" - -# Colors for output -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -echo -e "\n${YELLOW}Step 1: Fetching current PBS nodes...${NC}" -RESPONSE=$(curl -s -H "X-API-Token: $API_TOKEN" "$API_URL/config/nodes") - -# Check if we have PBS nodes (response is an array) -PBS_COUNT=$(echo "$RESPONSE" | jq '[.[] | select(.type == "pbs")] | length') -echo "Found $PBS_COUNT PBS instances" - -if [ "$PBS_COUNT" -eq 0 ]; then - echo -e "${YELLOW}No PBS nodes found. Creating a test PBS node...${NC}" - - # Create a test PBS node - PBS_DATA='{ - "node": { - "type": "pbs", - "name": "Test PBS", - "host": "192.168.0.8:8007", - "tokenName": "testuser@pbs!test-token", - "tokenValue": "test-token-value", - "verifySSL": false, - "monitorDatastores": true, - "monitorSyncJobs": true, - "monitorVerifyJobs": true, - "monitorPruneJobs": true, - "monitorGarbageJobs": false - } - }' - - CREATE_RESPONSE=$(curl -s -X POST \ - -H "Content-Type: application/json" \ - -H "X-API-Token: $API_TOKEN" \ - -d "$PBS_DATA" \ - "$API_URL/config/nodes") - - echo "PBS node created: $CREATE_RESPONSE" - - # Fetch nodes again - RESPONSE=$(curl -s -H "X-API-Token: $API_TOKEN" "$API_URL/config/nodes") -fi - -# Get first PBS node details (filter from array) -PBS_NODE=$(echo "$RESPONSE" | jq '[.[] | select(.type == "pbs")] | .[0]') - -if [ "$PBS_NODE" != "null" ]; then - echo -e "\n${GREEN}PBS Node Data:${NC}" - echo "$PBS_NODE" | jq '.' - - # Check critical fields - echo -e "\n${YELLOW}Checking PBS node fields:${NC}" - - # Check if tokenName contains username - TOKEN_NAME=$(echo "$PBS_NODE" | jq -r '.tokenName // empty') - HAS_TOKEN=$(echo "$PBS_NODE" | jq -r '.hasToken // false') - HAS_PASSWORD=$(echo "$PBS_NODE" | jq -r '.hasPassword // false') - - echo "- tokenName: $TOKEN_NAME" - echo "- hasToken: $HAS_TOKEN" - echo "- hasPassword: $HAS_PASSWORD" - - if [[ "$TOKEN_NAME" == *"!"* ]]; then - echo -e "${GREEN}✓ Token name contains username separator (!)${NC}" - USERNAME=$(echo "$TOKEN_NAME" | cut -d'!' -f1) - TOKEN_PART=$(echo "$TOKEN_NAME" | cut -d'!' -f2) - echo " - Extracted username: $USERNAME" - echo " - Token part: $TOKEN_PART" - else - echo -e "${YELLOW}Note: Token name doesn't contain separator, might be using password auth${NC}" - fi - - # Check auth type detection - if [ "$HAS_TOKEN" == "true" ]; then - echo -e "${GREEN}✓ Node is using token authentication${NC}" - elif [ "$HAS_PASSWORD" == "true" ]; then - echo -e "${GREEN}✓ Node is using password authentication${NC}" - else - echo -e "${RED}✗ No authentication method detected${NC}" - fi - - # Check monitoring settings - echo -e "\n${YELLOW}PBS Monitoring Settings:${NC}" - echo "- monitorDatastores: $(echo "$PBS_NODE" | jq -r '.monitorDatastores')" - echo "- monitorSyncJobs: $(echo "$PBS_NODE" | jq -r '.monitorSyncJobs')" - echo "- monitorVerifyJobs: $(echo "$PBS_NODE" | jq -r '.monitorVerifyJobs')" - echo "- monitorPruneJobs: $(echo "$PBS_NODE" | jq -r '.monitorPruneJobs')" - echo "- monitorGarbageJobs: $(echo "$PBS_NODE" | jq -r '.monitorGarbageJobs')" - -else - echo -e "${RED}No PBS nodes found in the system${NC}" -fi - -echo -e "\n${YELLOW}Test Complete!${NC}" -echo "To verify in UI:" -echo "1. Open http://localhost:7655" -echo "2. Go to Settings → Nodes" -echo "3. Click edit on a PBS node" -echo "4. Check that all fields are populated correctly" \ No newline at end of file diff --git a/testing-tools/test-registration-tokens.sh b/testing-tools/test-registration-tokens.sh deleted file mode 100755 index ab770dec5..000000000 --- a/testing-tools/test-registration-tokens.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -echo "=== Testing Registration Token Feature ===" -echo "" - -# Colors -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' - -PULSE_URL="http://localhost:7655" - -# Test 1: Try auto-register without token (should work by default in homelab mode) -echo "Test 1: Auto-register without token (homelab mode)..." -TEST_NODE='{ - "type": "pve", - "host": "test-node.local:8006", - "name": "Test Node", - "username": "root@pam", - "password": "test-password" -}' - -RESPONSE=$(curl -s -X POST "$PULSE_URL/api/auto-register" \ - -H "Content-Type: application/json" \ - -d "$TEST_NODE" 2>&1) - -if echo "$RESPONSE" | grep -q "success"; then - echo -e "${GREEN} ✓ Auto-registration without token succeeded (homelab mode)${NC}" -else - echo -e "${YELLOW} ⚠ Auto-registration response: $RESPONSE${NC}" -fi - -# Test 2: Check if we can enable token requirement -echo "" -echo "Test 2: Checking registration token configuration..." - -# Check if token requirement is enabled -if [ -n "$REQUIRE_REGISTRATION_TOKEN" ]; then - echo -e "${YELLOW} Registration tokens are required (REQUIRE_REGISTRATION_TOKEN=$REQUIRE_REGISTRATION_TOKEN)${NC}" -else - echo -e "${GREEN} Registration tokens are optional (default homelab mode)${NC}" -fi - -# Test 3: Generate a setup script with token -echo "" -echo "Test 3: Generating setup script..." -SETUP_RESPONSE=$(curl -s "$PULSE_URL/api/setup-script?node=test&token=test-token-123") - -if echo "$SETUP_RESPONSE" | grep -q "PULSE_URL"; then - echo -e "${GREEN} ✓ Setup script generated successfully${NC}" - - # Check if token is included - if echo "$SETUP_RESPONSE" | grep -q "REG_TOKEN"; then - echo -e "${GREEN} ✓ Registration token included in script${NC}" - else - echo -e "${YELLOW} ⚠ No registration token in script${NC}" - fi -else - echo -e "${RED} ✗ Failed to generate setup script${NC}" -fi - -# Test 4: Test with invalid token when tokens are required -echo "" -echo "Test 4: Testing token validation..." - -# This would only fail if REQUIRE_REGISTRATION_TOKEN=true -INVALID_RESPONSE=$(curl -s -X POST "$PULSE_URL/api/auto-register" \ - -H "Content-Type: application/json" \ - -H "X-Registration-Token: invalid-token" \ - -d "$TEST_NODE" 2>&1) - -if echo "$INVALID_RESPONSE" | grep -q "Unauthorized\|Invalid token"; then - echo -e "${GREEN} ✓ Invalid token rejected${NC}" -else - echo -e "${YELLOW} ⚠ Token validation may not be active${NC}" -fi - -# Test 5: Check registration endpoints -echo "" -echo "Test 5: Checking registration endpoints..." - -# Check if setup script endpoint works -SETUP_CHECK=$(curl -s -o /dev/null -w "%{http_code}" "$PULSE_URL/api/setup-script") -if [ "$SETUP_CHECK" = "200" ]; then - echo -e "${GREEN} ✓ Setup script endpoint available (/api/setup-script)${NC}" -else - echo -e "${RED} ✗ Setup script endpoint returned: $SETUP_CHECK${NC}" -fi - -# Check if auto-register endpoint works -REGISTER_CHECK=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$PULSE_URL/api/auto-register" \ - -H "Content-Type: application/json" \ - -d '{}') -if [ "$REGISTER_CHECK" = "400" ] || [ "$REGISTER_CHECK" = "401" ] || [ "$REGISTER_CHECK" = "200" ]; then - echo -e "${GREEN} ✓ Auto-register endpoint available (/api/auto-register)${NC}" -else - echo -e "${RED} ✗ Auto-register endpoint returned: $REGISTER_CHECK${NC}" -fi - -echo "" -echo "=== Summary ===" -echo "The registration token feature allows:" -echo "1. Secure node auto-registration with tokens" -echo "2. Optional token requirement (via REQUIRE_REGISTRATION_TOKEN env var)" -echo "3. Setup scripts with embedded tokens" -echo "4. Default homelab mode (no token required)" -echo "" -echo "To enable token requirement, set:" -echo " REQUIRE_REGISTRATION_TOKEN=true" -echo "in the Pulse service environment" \ No newline at end of file diff --git a/testing-tools/test-threshold-edit.md b/testing-tools/test-threshold-edit.md deleted file mode 100644 index 51d2b6257..000000000 --- a/testing-tools/test-threshold-edit.md +++ /dev/null @@ -1,79 +0,0 @@ -# Manual Test Plan: Threshold Edit UI Refresh Fix - -## Test Objective -Verify that the Save/Cancel buttons remain visible during threshold editing when the UI refreshes every 5 seconds. - -## Prerequisites -1. Pulse v4.2.0+ with the fix applied -2. At least one node configured -3. Browser with developer tools - -## Test Steps - -### Setup -1. Open Pulse in browser (http://localhost:7655) -2. Navigate to Alerts page -3. Click on "Thresholds" tab - -### Test Case 1: Create Override and Test Edit Persistence -1. Add a custom threshold override: - - Click "Add Override" - - Select a node or VM - - Set custom thresholds - - Save - -2. Start editing the override: - - Click "Edit" button next to the override - - **Expected**: Save and Cancel buttons appear - - **Expected**: Threshold sliders become editable - -3. Wait for UI refresh (15 seconds total - 3 refresh cycles): - - Watch the UI (you may see slight data updates) - - **Expected**: Save and Cancel buttons REMAIN VISIBLE - - **Expected**: Edit mode is maintained - - **Expected**: Any changes to sliders are preserved - -4. Make a change and save: - - Adjust one of the threshold sliders - - Click "Save" - - **Expected**: Changes are saved - - **Expected**: Returns to view mode with Edit button - -### Test Case 2: Cancel During Refresh -1. Click "Edit" on an override -2. Wait 7-8 seconds (through at least one refresh) -3. Click "Cancel" - - **Expected**: Returns to view mode - - **Expected**: No changes are saved - -### Test Case 3: Multiple Overrides -1. Create 2-3 overrides -2. Edit one override -3. Wait for refresh - - **Expected**: Only the one being edited shows Save/Cancel - - **Expected**: Other overrides still show Edit button - -## Verification in Browser Console -Open browser dev tools and run: -```javascript -// Check if edit state is preserved -setInterval(() => { - const saveBtn = document.querySelector('button:has-text("Save")'); - const editBtn = document.querySelector('button:has-text("Edit")'); - console.log('Save visible:', !!saveBtn, 'Edit visible:', !!editBtn); -}, 1000); -``` - -## Expected Results -- ✅ Edit state persists across all UI refresh cycles -- ✅ Save/Cancel buttons remain visible during entire edit session -- ✅ Threshold values being edited are not reset during refresh -- ✅ Only the override being edited maintains edit state - -## Known Issues (Before Fix) -- ❌ Save/Cancel buttons disappeared after 5-second refresh -- ❌ Users lost ability to save changes -- ❌ Had to be very quick to save before refresh - -## Fix Implementation -The fix tracks editing state at the parent component level (ThresholdsTab) instead of locally in each OverrideItem, preventing state loss during re-renders. \ No newline at end of file diff --git a/testing-tools/verify-pbs-display.js b/testing-tools/verify-pbs-display.js deleted file mode 100644 index fae61c51d..000000000 --- a/testing-tools/verify-pbs-display.js +++ /dev/null @@ -1,107 +0,0 @@ -const { chromium } = require('playwright'); - -const BASE_URL = 'http://192.168.0.123:7655'; - -async function verifyPBSDisplay() { - console.log('\n' + '='.repeat(60)); - console.log('PBS Authentication Display Verification'); - console.log('='.repeat(60) + '\n'); - console.log('This test assumes you have already added PBS nodes manually.\n'); - - const browser = await chromium.launch({ - headless: true, - args: ['--no-sandbox', '--disable-setuid-sandbox'] - }); - - const page = await browser.newContext({ ignoreHTTPSErrors: true }) - .then(ctx => ctx.newPage()); - - try { - // Navigate to Pulse - console.log('📍 Navigating to Pulse...'); - await page.goto(BASE_URL); - await page.waitForTimeout(2000); - - // Go to Settings - console.log('⚙️ Opening Settings...'); - await page.locator('text=Settings').first().click(); - await page.waitForTimeout(1500); - - // Click on PBS Nodes tab - console.log('📂 Opening PBS Nodes tab...\n'); - await page.locator('button:text("PBS Nodes")').click(); - await page.waitForTimeout(1000); - - // Take screenshot - await page.screenshot({ path: 'pbs-display-check.png', fullPage: true }); - console.log('📸 Screenshot saved to pbs-display-check.png\n'); - - // Find all PBS node cards - const pbsCards = await page.locator('.bg-white, .dark\\:bg-gray-800').all(); - - console.log(`Found ${pbsCards.length} node cards\n`); - - for (let i = 0; i < pbsCards.length; i++) { - const card = pbsCards[i]; - - // Try to get the node name - const nameElement = card.locator('h4, .font-medium').first(); - const nodeName = await nameElement.textContent().catch(() => 'Unknown'); - - // Look for auth display (User: or Token:) - const authSpan = card.locator('span.text-xs').first(); - const authDisplay = await authSpan.textContent().catch(() => 'Not found'); - - console.log(`Node ${i + 1}: ${nodeName}`); - console.log(` Auth display: ${authDisplay}`); - - // Check edit mode - const editButton = card.locator('button[title="Edit node"]'); - if (await editButton.isVisible()) { - console.log(' Testing edit mode...'); - await editButton.click(); - await page.waitForTimeout(1000); - - // Check which auth type is selected - const tokenRadioChecked = await page.locator('input[value="token"]').isChecked(); - const passRadioChecked = await page.locator('input[value="password"]').isChecked(); - - if (tokenRadioChecked) { - console.log(' ✅ Edit mode shows: Token authentication'); - if (authDisplay.includes('Token:')) { - console.log(' ✅ Display matches auth type'); - } else { - console.log(' ❌ Display does NOT match auth type (shows ' + authDisplay + ')'); - } - } else if (passRadioChecked) { - console.log(' ✅ Edit mode shows: Password authentication'); - if (authDisplay.includes('User:')) { - console.log(' ✅ Display matches auth type'); - } else { - console.log(' ❌ Display does NOT match auth type (shows ' + authDisplay + ')'); - } - } else { - console.log(' ⚠️ Cannot determine auth type in edit mode'); - } - - // Cancel edit - await page.locator('button:has-text("Cancel")').click(); - await page.waitForTimeout(1000); - } - - console.log(''); - } - - console.log('='.repeat(60)); - console.log('Verification Complete'); - console.log('='.repeat(60)); - - } catch (error) { - console.error('Error:', error.message); - await page.screenshot({ path: 'pbs-display-error.png' }); - } finally { - await browser.close(); - } -} - -verifyPBSDisplay().catch(console.error); \ No newline at end of file diff --git a/testing-tools/verify-threshold-edit.sh b/testing-tools/verify-threshold-edit.sh deleted file mode 100755 index e19fe6582..000000000 --- a/testing-tools/verify-threshold-edit.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash - -echo "=== Testing Threshold Edit UI Refresh Fix ===" -echo "" - -# Colors for output -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# 1. Check current alert config -echo "1. Checking current alert configuration..." -OVERRIDES=$(curl -s http://localhost:7655/api/alerts/config | jq '.overrides') -if [ "$OVERRIDES" = "{}" ]; then - echo -e "${YELLOW} No overrides configured yet${NC}" -else - echo -e "${GREEN} Found existing overrides${NC}" -fi - -# 2. Get a node to test with -echo "" -echo "2. Getting available nodes..." -NODE_ID=$(curl -s http://localhost:7655/api/state | jq -r '.nodes[0].id' 2>/dev/null) -NODE_NAME=$(curl -s http://localhost:7655/api/state | jq -r '.nodes[0].name' 2>/dev/null) - -if [ -z "$NODE_ID" ] || [ "$NODE_ID" = "null" ]; then - echo -e "${RED} No nodes available for testing${NC}" - exit 1 -fi - -echo -e "${GREEN} Found node: $NODE_NAME (ID: $NODE_ID)${NC}" - -# 3. Create a test override -echo "" -echo "3. Creating test threshold override for $NODE_NAME..." -TEST_CONFIG="{ - \"overrides\": { - \"$NODE_ID\": { - \"cpu\": { \"trigger\": 75, \"clear\": 70 }, - \"memory\": { \"trigger\": 80, \"clear\": 75 } - } - } -}" - -# Save the override -curl -s -X PUT http://localhost:7655/api/alerts/config \ - -H "Content-Type: application/json" \ - -d "$TEST_CONFIG" > /dev/null - -# Verify it was saved -SAVED_OVERRIDE=$(curl -s http://localhost:7655/api/alerts/config | jq ".overrides[\"$NODE_ID\"]") -if [ "$SAVED_OVERRIDE" != "null" ]; then - echo -e "${GREEN} ✓ Override created successfully${NC}" -else - echo -e "${RED} ✗ Failed to create override${NC}" - exit 1 -fi - -# 4. Test instructions -echo "" -echo "4. Manual UI Test Instructions:" -echo " ================================" -echo -e "${YELLOW}" -echo " a) Open Pulse in your browser: http://localhost:7655" -echo " b) Navigate to Alerts → Thresholds tab" -echo " c) Find the override for '$NODE_NAME'" -echo " d) Click 'Edit' button" -echo " e) Wait 15 seconds (3 refresh cycles)" -echo " f) Verify Save/Cancel buttons are STILL VISIBLE" -echo "" -echo " Expected: Buttons remain visible during all refreshes" -echo " Old Bug: Buttons would disappear after 5 seconds" -echo -e "${NC}" - -# 5. Verification check -echo "5. Code verification:" -echo " Checking if fix is implemented in code..." - -# Check for the editingOverrideId state management -if grep -q "editingOverrideId" /opt/pulse/frontend-modern/src/pages/Alerts.tsx; then - echo -e "${GREEN} ✓ Fix is present in code (editingOverrideId state management found)${NC}" -else - echo -e "${RED} ✗ Fix may not be implemented${NC}" -fi - -# Check for proper prop passing -if grep -q "isEditing={editingOverrideId()" /opt/pulse/frontend-modern/src/pages/Alerts.tsx; then - echo -e "${GREEN} ✓ Edit state properly passed to components${NC}" -else - echo -e "${YELLOW} ⚠ Could not verify prop passing${NC}" -fi - -echo "" -echo "=== Test Setup Complete ===" -echo -e "${GREEN}Override created for $NODE_NAME - Please test manually in browser${NC}" -echo "" -echo "To clean up test data later, run:" -echo "curl -X PUT http://localhost:7655/api/alerts/config -H 'Content-Type: application/json' -d '{\"overrides\":{}}'" \ No newline at end of file diff --git a/verify-pbs-fix.md b/verify-pbs-fix.md deleted file mode 100644 index 6f46013a4..000000000 --- a/verify-pbs-fix.md +++ /dev/null @@ -1,40 +0,0 @@ -# PBS Form Fix Verification - -## What was broken -1. When editing a PBS node, the form wouldn't populate with PBS data -2. PBS forms could show PVE data if a PVE node was edited first - -## What we fixed -In `/opt/pulse/frontend-modern/src/components/Settings/Settings.tsx`: -- Lines 886 and 1063: Added `setCurrentNodeType(node.type as 'pve' | 'pbs');` -- This ensures when editing any node, we set the correct type - -## How to verify the fix works - -### Test #1: PBS form after PVE -1. Go to Settings → Nodes -2. Click "Add PVE Node" -3. Fill in some data -4. Press Escape to cancel -5. Click "Add PBS Node" -6. **VERIFY**: PBS form should be completely empty - -### Test #2: Edit PBS node -1. Add a PBS node with test data -2. Click the edit icon on that PBS node -3. **VERIFY**: Form should show the PBS node's data - -### Test #3: Edit PVE after PBS -1. Add a PVE node -2. Add a PBS node -3. Edit the PBS node -4. Cancel -5. Edit the PVE node -6. **VERIFY**: PVE form shows PVE data, not PBS data - -## Current Status -- [x] Fix implemented in source code -- [x] Frontend rebuilt with fix -- [x] Backend restarted -- [ ] Manual testing completed -- [ ] User confirmed it works \ No newline at end of file