diff --git a/package-lock.json b/package-lock.json index 28735d4a..263e8166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@headlessui/react": "^2.2.9", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.2.2", + "@ltonetwork/http-message-signatures": "^0.1.12", "@mdxeditor/editor": "^3.52.4", "@ory/elements-react": "^1.1.0", "@ory/nextjs": "^1.0.0-rc.1", @@ -179,6 +180,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -951,6 +953,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -1039,6 +1042,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", "license": "MIT", + "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -1048,6 +1052,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz", "integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "style-mod": "^4.1.0", @@ -2518,7 +2523,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -2854,6 +2858,7 @@ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", "license": "MIT", + "peer": true, "dependencies": { "@lezer/common": "^1.0.0" } @@ -2987,6 +2992,12 @@ "@lezer/lr": "^1.4.0" } }, + "node_modules/@ltonetwork/http-message-signatures": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@ltonetwork/http-message-signatures/-/http-message-signatures-0.1.12.tgz", + "integrity": "sha512-bk7BemlROclcdXu5ISTKjPeRstJoqWefh6Bvr1q6XaX+KyKgLuKeCSYOKRyBBJ8P/E+tq9toBUIlMRLvW5J9KQ==", + "license": "ISC" + }, "node_modules/@marijn/find-cluster-break": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", @@ -3118,9 +3129,9 @@ } }, "node_modules/@next/env": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.12.tgz", - "integrity": "sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.14.tgz", + "integrity": "sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -3133,9 +3144,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.12.tgz", - "integrity": "sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.14.tgz", + "integrity": "sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg==", "cpu": [ "arm64" ], @@ -3149,9 +3160,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.12.tgz", - "integrity": "sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.14.tgz", + "integrity": "sha512-aNnkSMjSFRTOmkd7qoNI2/rETQm/vKD6c/Ac9BZGa9CtoOzy3c2njgz7LvebQJ8iPxdeTuGnAjagyis8a9ifBw==", "cpu": [ "x64" ], @@ -3165,9 +3176,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.12.tgz", - "integrity": "sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.14.tgz", + "integrity": "sha512-tjlpia+yStPRS//6sdmlVwuO1Rioern4u2onafa5n+h2hCS9MAvMXqpVbSrjgiEOoCs0nJy7oPOmWgtRRNSM5Q==", "cpu": [ "arm64" ], @@ -3181,9 +3192,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.12.tgz", - "integrity": "sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.14.tgz", + "integrity": "sha512-8B8cngBaLadl5lbDRdxGCP1Lef8ipD6KlxS3v0ElDAGil6lafrAM3B258p1KJOglInCVFUjk751IXMr2ixeQOQ==", "cpu": [ "arm64" ], @@ -3197,9 +3208,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.12.tgz", - "integrity": "sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.14.tgz", + "integrity": "sha512-bAS6tIAg8u4Gn3Nz7fCPpSoKAexEt2d5vn1mzokcqdqyov6ZJ6gu6GdF9l8ORFrBuRHgv3go/RfzYz5BkZ6YSQ==", "cpu": [ "x64" ], @@ -3213,9 +3224,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.12.tgz", - "integrity": "sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.14.tgz", + "integrity": "sha512-mMxv/FcrT7Gfaq4tsR22l17oKWXZmH/lVqcvjX0kfp5I0lKodHYLICKPoX1KRnnE+ci6oIUdriUhuA3rBCDiSw==", "cpu": [ "x64" ], @@ -3229,9 +3240,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.12.tgz", - "integrity": "sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.14.tgz", + "integrity": "sha512-OTmiBlYThppnvnsqx0rBqjDRemlmIeZ8/o4zI7veaXoeO1PVHoyj2lfTfXTiiGjCyRDhA10y4h6ZvZvBiynr2g==", "cpu": [ "arm64" ], @@ -3245,9 +3256,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.12.tgz", - "integrity": "sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.14.tgz", + "integrity": "sha512-+W7eFf3RS7m4G6tppVTOSyP9Y6FsJXfOuKzav1qKniiFm3KFByQfPEcouHdjlZmysl4zJGuGLQ/M9XyVeyeNEg==", "cpu": [ "x64" ], @@ -3328,6 +3339,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -3378,6 +3390,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", @@ -4049,6 +4062,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -4854,6 +4868,7 @@ "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.56.1" }, @@ -6669,6 +6684,7 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.1.2.tgz", "integrity": "sha512-k8FR9yVHV9kIF3iuOD0ds5hVymXYXfgdKklqziBVod9ZEJ8uk05Zjw29J/omU3IKeUfLNAIHfxneN3TUYM4I2w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/react-reconciler": "^0.28.9", @@ -6821,6 +6837,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8287,6 +8304,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -8589,7 +8607,6 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -8600,7 +8617,6 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -8785,6 +8801,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -8836,6 +8853,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -8845,6 +8863,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -8901,6 +8920,7 @@ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.176.0.tgz", "integrity": "sha512-FwfPXxCqOtP7EdYMagCFePNKoG1AGBDUEVKtluv2BTVRpSt7b+X27xNsirPCTCqY1pGYsPUzaM3jgWP7dXSxlw==", "license": "MIT", + "peer": true, "dependencies": { "@dimforge/rapier3d-compat": "^0.12.0", "@tweenjs/tween.js": "~23.1.3", @@ -8996,6 +9016,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -9462,7 +9483,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -9472,29 +9492,25 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -9505,15 +9521,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -9526,7 +9540,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "license": "MIT", - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -9536,7 +9549,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -9545,15 +9557,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -9570,7 +9580,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -9584,7 +9593,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -9597,7 +9605,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -9612,7 +9619,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -9628,15 +9634,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@xyflow/react": { "version": "12.6.4", @@ -9716,6 +9720,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -9748,7 +9753,6 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -9811,7 +9815,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -9829,7 +9832,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -9845,8 +9847,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/anser": { "version": "2.3.2", @@ -10436,6 +10437,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -10681,7 +10683,6 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.0" } @@ -10873,8 +10874,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/commondir": { "version": "1.0.1", @@ -11132,6 +11132,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -11582,7 +11583,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-auto-height": { "version": "8.6.0", @@ -11793,8 +11795,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -11946,6 +11947,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -12033,6 +12035,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -12128,6 +12131,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -12480,7 +12484,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.x" } @@ -12626,8 +12629,7 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.20.1", @@ -12743,9 +12745,9 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "license": "ISC" }, "node_modules/focus-visible": { @@ -13103,8 +13105,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/globals": { "version": "16.5.0", @@ -14181,7 +14182,6 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "license": "MIT", - "peer": true, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -15484,7 +15484,6 @@ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", "license": "MIT", - "peer": true, "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -15783,7 +15782,6 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.11.5" }, @@ -17318,16 +17316,16 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/next": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.12.tgz", - "integrity": "sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz", + "integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==", "license": "MIT", + "peer": true, "dependencies": { - "@next/env": "15.5.12", + "@next/env": "15.5.14", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -17340,14 +17338,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.12", - "@next/swc-darwin-x64": "15.5.12", - "@next/swc-linux-arm64-gnu": "15.5.12", - "@next/swc-linux-arm64-musl": "15.5.12", - "@next/swc-linux-x64-gnu": "15.5.12", - "@next/swc-linux-x64-musl": "15.5.12", - "@next/swc-win32-arm64-msvc": "15.5.12", - "@next/swc-win32-x64-msvc": "15.5.12", + "@next/swc-darwin-arm64": "15.5.14", + "@next/swc-darwin-x64": "15.5.14", + "@next/swc-linux-arm64-gnu": "15.5.14", + "@next/swc-linux-arm64-musl": "15.5.14", + "@next/swc-linux-x64-gnu": "15.5.14", + "@next/swc-linux-x64-musl": "15.5.14", + "@next/swc-win32-arm64-msvc": "15.5.14", + "@next/swc-win32-x64-msvc": "15.5.14", "sharp": "^0.34.3" }, "peerDependencies": { @@ -18151,6 +18149,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -18210,6 +18209,7 @@ "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.37.3.tgz", "integrity": "sha512-2FzfZg41Ml/ddvSalLdCz7H53Wmr/ZvK238fauTG1CI97+65BDgo1dNH6AKhAWc5q9ngpyG5EYiXVbNCl2t1iA==", "license": "Zlib", + "peer": true, "peerDependencies": { "three": ">= 0.157.0 < 0.177.0" } @@ -18235,6 +18235,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18431,6 +18432,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -18449,6 +18451,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -18494,6 +18497,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -19052,6 +19056,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -19190,6 +19195,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -19229,7 +19235,6 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -19266,7 +19271,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -19278,8 +19282,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/semver": { "version": "7.7.3", @@ -20025,7 +20028,8 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -20054,7 +20058,6 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -20073,7 +20076,6 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -20107,7 +20109,6 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -20122,7 +20123,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -20138,7 +20138,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -20163,7 +20162,8 @@ "version": "0.176.0", "resolved": "https://registry.npmjs.org/three/-/three-0.176.0.tgz", "integrity": "sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/three-mesh-bvh": { "version": "0.8.3", @@ -20247,6 +20247,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -20380,6 +20381,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20614,6 +20616,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21171,7 +21174,6 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "license": "MIT", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -21206,7 +21208,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.3.tgz", "integrity": "sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -21270,7 +21271,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -21284,7 +21284,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } diff --git a/package.json b/package.json index ecb76596..ebcb46a1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@headlessui/react": "^2.2.9", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.2.2", + "@ltonetwork/http-message-signatures": "^0.1.12", "@mdxeditor/editor": "^3.52.4", "@ory/elements-react": "^1.1.0", "@ory/nextjs": "^1.0.0-rc.1", diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx new file mode 100644 index 00000000..f238aab8 --- /dev/null +++ b/src/app/admin/layout.tsx @@ -0,0 +1,19 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +import React from "react"; +import RootHeader from "@/components/common/RootHeader"; +import { InstanceAdminProvider } from "@/context/InstanceAdminContext"; + +export default function AdminLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + +
{children}
+
+ ); +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx new file mode 100644 index 00000000..e9932891 --- /dev/null +++ b/src/app/admin/page.tsx @@ -0,0 +1,204 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useInstanceAdmin } from "@/context/InstanceAdminContext"; +import { adminBrowserApiClient } from "@/services/adminApi"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Field, + FieldDescription, + FieldGroup, + FieldLabel, +} from "@/components/ui/field"; +import { Input } from "@/components/ui/input"; +import InstanceDashboard, { + type InstanceDashboardHandle, +} from "@/components/admin/InstanceDashboard"; +import InstanceTechnicalInfo, { + type InstanceTechnicalInfoHandle, +} from "@/components/admin/InstanceTechnicalInfo"; +import AdminTools from "@/components/admin/AdminTools"; +import Section from "@/components/common/Section"; +import { ArrowPathIcon } from "@heroicons/react/20/solid"; + +export default function InstanceAdminPage() { + const { isAuthenticated, authenticate, logout, getPrivateKey } = + useInstanceAdmin(); + const [keyInput, setKeyInput] = useState(""); + const [verifying, setVerifying] = useState(false); + const [mounted, setMounted] = useState(false); + const techInfoRef = useRef(null); + const dashboardRef = useRef(null); + + useEffect(() => { + setMounted(true); + }, []); + + const handleAuthenticate = useCallback(async () => { + const trimmedKey = keyInput.trim(); + if (!trimmedKey) { + toast.error("Please enter your private key."); + return; + } + + if (!/^[0-9a-fA-F]+$/.test(trimmedKey)) { + toast.error("Invalid key format. The private key must be hex-encoded."); + return; + } + + setVerifying(true); + try { + const resp = await adminBrowserApiClient("/admin", trimmedKey); + + if (resp.ok) { + authenticate(trimmedKey); + setKeyInput(""); + toast.success("Authenticated as instance admin."); + } else if (resp.status === 401 || resp.status === 403) { + toast.error( + "Authentication failed. The private key does not match the instance's admin public key.", + ); + } else { + toast.error(`Unexpected response from server: ${resp.status}`); + } + } catch (err) { + toast.error("Failed to verify key. Is the API reachable?"); + } finally { + setVerifying(false); + } + }, [keyInput, authenticate]); + + const handleLogout = useCallback(() => { + logout(); + toast.success("Admin session ended. Private key removed from session."); + }, [logout]); + + if (!mounted) return null; + + if (!isAuthenticated) { + return ( +
+
+ + + Instance Administration + + Authenticate with your ECDSA P-256 private key to access + instance admin features. + + + +
{ + e.preventDefault(); + handleAuthenticate(); + }} + > + + + + Private Key (hex) + + setKeyInput(e.target.value)} + autoComplete="off" + required + /> + + The key is stored in session storage and cleared when you + close the tab. It is never sent to the server — it signs + requests locally. + + + + + + +
+
+
+
+
+ ); + } + + return ( +
+
+
+

Instance Administration

+

+ Manage this DevGuard instance. +

+
+ +
+
+ +
+ +
techInfoRef.current?.refresh()} + > + + Refresh + + } + > + +
+ +
dashboardRef.current?.refresh()} + > + + Refresh + + } + > + +
+
+ ); +} diff --git a/src/components/admin/AdminTools.tsx b/src/components/admin/AdminTools.tsx new file mode 100644 index 00000000..2896ce7a --- /dev/null +++ b/src/components/admin/AdminTools.tsx @@ -0,0 +1,20 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import ExternalOrgAdminCard from "@/components/admin/ExternalOrgAdminCard"; +import InstanceSettingsCard from "@/components/admin/InstanceSettingsCard"; +import TriggerDaemonsCard from "@/components/admin/TriggerDaemonsCard"; + +export default function AdminTools() { + return ( +
+ +
+ + +
+
+ ); +} diff --git a/src/components/admin/ExternalOrgAdminCard.tsx b/src/components/admin/ExternalOrgAdminCard.tsx new file mode 100644 index 00000000..32c051ba --- /dev/null +++ b/src/components/admin/ExternalOrgAdminCard.tsx @@ -0,0 +1,315 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useCallback, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { + BuildingOffice2Icon, + ChevronRightIcon, + ShieldCheckIcon, + TrashIcon, + UserPlusIcon, +} from "@heroicons/react/20/solid"; +import { UserCircle2Icon } from "lucide-react"; + +// ── Types & mock data ────────────────────────────────────────── + +interface OrgAdmin { + id: string; + email: string; +} + +interface ExternalOrg { + id: string; + /** The reserved @-prefixed slug, e.g. "@gitlab" or "@opencode" */ + slug: string; + /** Display name for the GitLab instance */ + instanceName: string; + /** Base URL of the GitLab instance */ + instanceUrl: string; + admins: OrgAdmin[]; +} + +const mockExternalOrgs: ExternalOrg[] = [ + { + id: "ext-1", + slug: "@gitlab", + instanceName: "GitLab.com", + instanceUrl: "https://gitlab.com", + admins: [ + { id: "u1", email: "alice@acme.corp" }, + { id: "u2", email: "bob@acme.corp" }, + ], + }, + { + id: "ext-2", + slug: "@opencode", + instanceName: "OpenCode", + instanceUrl: "https://gitlab.opencode.de", + admins: [{ id: "u3", email: "carol@gov.de" }], + }, +]; + +// ── Component ────────────────────────────────────────────────── + +export default function ExternalOrgAdminCard() { + const [orgs, setOrgs] = useState(mockExternalOrgs); + const [emailInputs, setEmailInputs] = useState>({}); + const [addingFor, setAddingFor] = useState(null); + const [confirmAssign, setConfirmAssign] = useState<{ + orgId: string; + email: string; + } | null>(null); + + const requestAssignAdmin = useCallback( + (orgId: string) => { + const email = (emailInputs[orgId] ?? "").trim(); + if (!email) return; + + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + toast.error("Please enter a valid email address."); + return; + } + + const org = orgs.find((o) => o.id === orgId); + if (org?.admins.some((a) => a.email === email)) { + toast.error(`${email} is already an admin of ${org.slug}.`); + return; + } + + setConfirmAssign({ orgId, email }); + }, + [emailInputs, orgs], + ); + + const handleAssignAdmin = useCallback( + (orgId: string, email: string) => { + const org = orgs.find((o) => o.id === orgId); + + // TODO: POST to admin API to verify the user exists & assign admin role + const newAdmin: OrgAdmin = { + id: `generated-${Date.now()}`, + email, + }; + + setOrgs((prev) => + prev.map((o) => + o.id === orgId ? { ...o, admins: [...o.admins, newAdmin] } : o, + ), + ); + setEmailInputs((prev) => ({ ...prev, [orgId]: "" })); + setAddingFor(null); + setConfirmAssign(null); + toast.success(`Added ${email} as admin for ${org?.slug}.`); + }, + [orgs], + ); + + const handleRevokeAdmin = useCallback( + (orgId: string, adminId: string) => { + const org = orgs.find((o) => o.id === orgId); + const admin = org?.admins.find((a) => a.id === adminId); + setOrgs((prev) => + prev.map((o) => + o.id === orgId + ? { ...o, admins: o.admins.filter((a) => a.id !== adminId) } + : o, + ), + ); + toast.success(`Revoked admin privileges for ${admin?.email ?? adminId}.`); + }, + [orgs], + ); + + return ( + + + + + External Organisation Admin Management + + + Manage admin privileges for organisations with deep project sync. + These are reserved @-prefixed + organisations backed by GitLab integrations. + + + +
+ {orgs.map((org) => ( + + + + + {org.slug} + + + {org.admins.length}{" "} + {org.admins.length === 1 ? "admin" : "admins"} + + + + +
+

+ Connected to{" "} + + {org.instanceName} + +

+ + {/* Admin list */} +
+ {org.admins.length === 0 && ( +

+ No admins assigned. +

+ )} + {org.admins.map((admin) => ( +
+ + + {admin.email} + + +
+ ))} +
+ + {/* Add admin */} + {addingFor === org.id ? ( +
+ + setEmailInputs((prev) => ({ + ...prev, + [org.id]: e.target.value, + })) + } + onKeyDown={(e) => { + if (e.key === "Enter") requestAssignAdmin(org.id); + if (e.key === "Escape") setAddingFor(null); + }} + /> + + +
+ ) : ( + + )} +
+
+
+ ))} +
+ + {/* Confirmation dialog */} + !open && setConfirmAssign(null)} + > + + + Confirm Admin Assignment + + You are about to grant admin privileges to{" "} + + {confirmAssign?.email} + {" "} + for the organisation{" "} + + {orgs.find((o) => o.id === confirmAssign?.orgId)?.slug} + + . This user will be able to manage all projects and settings + within this organisation. + + + + Cancel + { + if (confirmAssign) { + handleAssignAdmin(confirmAssign.orgId, confirmAssign.email); + } + }} + > + Confirm & Add Admin + + + + +
+
+ ); +} diff --git a/src/components/admin/InstanceDashboard.tsx b/src/components/admin/InstanceDashboard.tsx new file mode 100644 index 00000000..39529066 --- /dev/null +++ b/src/components/admin/InstanceDashboard.tsx @@ -0,0 +1,591 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useState, +} from "react"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + getSeverityClassNames, + severityToColor, +} from "@/components/common/Severity"; +import { classNames } from "@/utils/common"; +import { + Bar, + BarChart, + CartesianGrid, + ResponsiveContainer, + XAxis, + YAxis, +} from "recharts"; +import { + ExclamationTriangleIcon, + ShieldExclamationIcon, +} from "@heroicons/react/24/outline"; +import { + ArrowsRightLeftIcon, + BuildingOffice2Icon, + FolderOpenIcon, + LinkIcon, + TagIcon, + UsersIcon, +} from "@heroicons/react/20/solid"; +import type { InstanceAdminStatsDTO } from "@/types/api/api"; + +export interface InstanceDashboardHandle { + refresh: () => void; +} + +const mockStats: InstanceAdminStatsDTO = { + totalUsers: 1024, + totalOrganisations: 38, + nonEmptyProjects: 267, + totalProjectRefs: 1892, + ticketSyncProjects: 43, + enabledIntegrations: 17, + avgVulnsPerOrg: [ + { org: "Acme Corp", critical: 12, high: 34, medium: 67, low: 120 }, + { org: "WidgetCo", critical: 5, high: 18, medium: 42, low: 85 }, + { org: "Foxtail", critical: 8, high: 27, medium: 53, low: 91 }, + { org: "NovaTech", critical: 3, high: 11, medium: 29, low: 60 }, + { org: "Helios", critical: 15, high: 40, medium: 72, low: 130 }, + ], + topVulnerableAssets: [ + { asset: "api-gateway", org: "Acme Corp", open: 87, critical: 14 }, + { asset: "auth-service", org: "WidgetCo", open: 64, critical: 9 }, + { asset: "frontend-app", org: "Foxtail", open: 52, critical: 6 }, + { asset: "payment-svc", org: "NovaTech", open: 48, critical: 11 }, + { asset: "data-pipeline", org: "Helios", open: 43, critical: 5 }, + { asset: "notification-svc", org: "Acme Corp", open: 39, critical: 3 }, + { asset: "user-mgmt", org: "WidgetCo", open: 31, critical: 2 }, + ], + topCVEs: [ + { cve: "CVE-2025-31498", affected: 42, severity: "CRITICAL" }, + { cve: "CVE-2025-29927", affected: 38, severity: "CRITICAL" }, + { cve: "CVE-2025-27789", affected: 35, severity: "HIGH" }, + { cve: "CVE-2024-50340", affected: 29, severity: "HIGH" }, + { cve: "CVE-2025-21502", affected: 24, severity: "MEDIUM" }, + { cve: "CVE-2024-47176", affected: 22, severity: "HIGH" }, + { cve: "CVE-2024-38816", affected: 19, severity: "MEDIUM" }, + ], + topDependencies: [ + { pkg: "lodash", count: 312, ecosystem: "npm" }, + { pkg: "express", count: 287, ecosystem: "npm" }, + { pkg: "golang.org/x/net", count: 245, ecosystem: "go" }, + { pkg: "com.google.guava:guava", count: 198, ecosystem: "maven" }, + { pkg: "axios", count: 176, ecosystem: "npm" }, + { pkg: "org.slf4j:slf4j-api", count: 164, ecosystem: "maven" }, + { pkg: "requests", count: 149, ecosystem: "pypi" }, + { pkg: "react", count: 142, ecosystem: "npm" }, + ], + avgCodeRisksPerOrg: [ + { org: "Acme Corp", risks: 23 }, + { org: "WidgetCo", risks: 17 }, + { org: "Foxtail", risks: 31 }, + { org: "NovaTech", risks: 9 }, + { org: "Helios", risks: 27 }, + ], + maliciousPackages: [ + { + pkg: "event-stream@3.3.6", + ecosystem: "npm", + affected: 4, + type: "Dependency Injection", + }, + { + pkg: "ua-parser-js@0.7.29", + ecosystem: "npm", + affected: 3, + type: "Cryptominer", + }, + { + pkg: "colors@1.4.1", + ecosystem: "npm", + affected: 2, + type: "Protestware", + }, + ], +}; + +export default forwardRef( + function InstanceDashboard(_, ref) { + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + + const loadStats = useCallback(async () => { + setLoading(true); + // TODO: Replace with real API call when endpoint is available + await new Promise((r) => setTimeout(r, 600)); + setStats(mockStats); + setLoading(false); + }, []); + + useImperativeHandle(ref, () => ({ refresh: loadStats }), [loadStats]); + + useEffect(() => { + loadStats(); + }, [loadStats]); + + if (loading || !stats) { + return ( +
+ {/* Summary skeleton */} +
+ {Array.from({ length: 6 }).map((_, i) => ( + + + + + + + + + + ))} +
+ {/* Chart skeleton rows */} + {Array.from({ length: 3 }).map((_, row) => ( +
+ {Array.from({ length: 2 }).map((_, col) => ( + + + + + + + + + + ))} +
+ ))} +
+ ); + } + + return ( +
+ {/* Summary cards */} +
+ + + + + Users + + + {stats.totalUsers.toLocaleString()} + + + + Registered users on this instance + + + + + + + + Organisations + + + {stats.totalOrganisations.toLocaleString()} + + + + Total created organisations + + + + + + + + Non-Empty Projects + + + {stats.nonEmptyProjects.toLocaleString()} + + + + Projects with at least one asset + + + + + + + + Project Refs + + + {stats.totalProjectRefs.toLocaleString()} + + + + Total asset versions across projects + + + + + + + + Ticket Sync + + + {stats.ticketSyncProjects.toLocaleString()} + + + + Projects with ticket sync enabled + + + + + + + + Integrations + + + {stats.enabledIntegrations.toLocaleString()} + + + + Active PATs for GitLab integration + + +
+ + {/* Row 1: Avg Open Vulns per Org + Top Vulnerable Assets */} +
+ + + Avg. Open Vulnerabilities per Organisation + + Breakdown by CVSS severity across all organisations. + + + + + + + + + + { + const { content: _, ...rest } = props; + return ( + + ); + }} + /> + } /> + + + + + + + + + + + + + Top Vulnerable Projects + + Assets with the most open vulnerabilities across the instance. + + + +
+ {stats.topVulnerableAssets.map((a) => ( +
+
+
+ + {a.asset} + + + {a.org} + +
+
+
+
+
+
+ + {a.open} + + {a.critical > 0 && ( + + {a.critical} critical + + )} +
+
+ ))} +
+ + +
+ + {/* Row 2: Top CVEs Across Instance + Top Dependencies */} +
+ + + Top CVEs Across Instance + + Most widespread CVEs by number of affected assets. + + + +
+ {stats.topCVEs.map((c) => ( +
+
+ {c.cve} +
+ + {c.severity} + + + {c.affected} assets + +
+ ))} +
+
+
+ + + + Top Used Dependency Packages + + Most common dependencies across the entire instance. + + + +
+ {stats.topDependencies.map((d) => ( +
+
+
+ + {d.pkg} + + + {d.ecosystem} + +
+
+ + {d.count} + + + {((d.count / stats.totalProjectRefs) * 100).toFixed(1)}% + +
+ ))} +
+
+
+
+ + {/* Row 3: Avg Code Risks per Org + Malicious Packages */} +
+ + + Avg. Open Code Risks per Organisation + + Average number of first-party (code) risks across all projects + per organisation. + + + + + + + + + + + } + /> + + + + + + + + + + + + Malicious Packages + + + Known malicious packages detected across the instance. + + + +
+ +
+

+ {stats.maliciousPackages.reduce( + (s, p) => s + p.affected, + 0, + )} +

+

+ affected assets across {stats.maliciousPackages.length}{" "} + malicious packages +

+
+
+
+ {stats.maliciousPackages.map((p) => ( +
+
+
+ + {p.pkg} + + + {p.ecosystem} + +
+ + {p.type} + +
+ + {p.affected} asset{p.affected !== 1 ? "s" : ""} + +
+ ))} +
+
+
+
+
+ ); + }, +); diff --git a/src/components/admin/InstanceSettingsCard.tsx b/src/components/admin/InstanceSettingsCard.tsx new file mode 100644 index 00000000..55464740 --- /dev/null +++ b/src/components/admin/InstanceSettingsCard.tsx @@ -0,0 +1,64 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useState } from "react"; +import { toast } from "sonner"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Switch } from "@/components/ui/switch"; +import { NoSymbolIcon } from "@heroicons/react/20/solid"; + +export default function InstanceSettingsCard() { + const [orgCreationDisabled, setOrgCreationDisabled] = useState(false); + + return ( + + + + + Instance Settings + + + Global configuration toggles for this DevGuard instance. + + + +
+
+
+

+ Disable Organisation Creation +

+

+ When enabled, regular users cannot create new organisations. +

+
+
+ {orgCreationDisabled && Disabled} + { + setOrgCreationDisabled(checked); + toast.success( + checked + ? "Organisation creation disabled for regular users." + : "Organisation creation re-enabled.", + ); + }} + /> +
+
+
+
+
+ ); +} diff --git a/src/components/admin/InstanceTechnicalInfo.tsx b/src/components/admin/InstanceTechnicalInfo.tsx new file mode 100644 index 00000000..d6ef6add --- /dev/null +++ b/src/components/admin/InstanceTechnicalInfo.tsx @@ -0,0 +1,307 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useState, +} from "react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + ArrowTopRightOnSquareIcon, + ServerIcon, + CpuChipIcon, + CircleStackIcon, + CodeBracketIcon, +} from "@heroicons/react/20/solid"; +import type { InstanceInfoDTO } from "@/types/api/api"; +import { adminBrowserApiClient } from "@/services/adminApi"; +import { useInstanceAdmin } from "@/context/InstanceAdminContext"; +import { + checkForUpdate, + type VersionCheckResult, +} from "@/services/versionCheck"; +import { + formatBytes, + formatDuration, + formatUnixTimestamp, +} from "@/utils/format"; + +export interface InstanceTechnicalInfoHandle { + refresh: () => void; +} + +function Row({ + label, + value, + mono, + href, +}: { + label: string; + value: React.ReactNode; + mono?: boolean; + href?: string; +}) { + return ( +
+ {label} + {href ? ( + + {value} + + + ) : ( + + {value} + + )} +
+ ); +} + +export default forwardRef( + function InstanceTechnicalInfo(_, ref) { + const { getPrivateKey } = useInstanceAdmin(); + const [info, setInfo] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [versionCheck, setVersionCheck] = useState( + null, + ); + + const loadInfo = useCallback(async () => { + const key = getPrivateKey(); + if (!key) return; + + try { + setLoading(true); + const resp = await adminBrowserApiClient("/info", key); + if (!resp.ok) { + setError(`Server returned ${resp.status}`); + return; + } + const data = (await resp.json()) as InstanceInfoDTO; + setInfo(data); + setError(null); + + // Fire-and-forget version check + checkForUpdate(data.build.version) + .then((result) => setVersionCheck(result)) + .catch(() => { + /* non-critical */ + }); + } catch { + setError("Failed to fetch instance info."); + } finally { + setLoading(false); + } + }, [getPrivateKey]); + + useImperativeHandle(ref, () => ({ refresh: loadInfo }), [loadInfo]); + + useEffect(() => { + loadInfo(); + }, [loadInfo]); + + if (loading) { + return ( +
+ {[4, 3, 5, 4].map((rows, i) => ( + + + + + + + {Array.from({ length: rows }).map((_, j) => ( +
+ + +
+ ))} +
+
+ ))} +
+ ); + } + + if (error || !info) { + return ( + + +

+ {error ?? "No instance info available."} +

+
+
+ ); + } + + const commitShort = info.build.commit.slice(0, 8); + const commitUrl = `https://github.com/l3montree-dev/devguard/commit/${info.build.commit}`; + const vulndbUrl = + "https://github.com/l3montree-dev/devguard/pkgs/container/devguard%2Fvulndb%2Fv1"; + + console.log("Instance info loaded:", info, versionCheck); + + return ( +
+ {/* Build */} + + + + + Build + + + Version and source control information. + + + + + {info.build.version} + {versionCheck?.updateAvailable && ( + + + {versionCheck.latestVersion} available + + + )} + {versionCheck && !versionCheck.updateAvailable && ( + up to date + )} + + } + mono + /> + + + + + + + {/* Process */} + + + + + Process + + + Server process and uptime details. + + + + + + + + + + {/* Runtime */} + + + + + Runtime + + Go runtime and memory statistics. + + + + + + + + + + + {/* Database */} + + + + + Database + + + Connection pool and migration status. + + + + + {info.database.status} + + } + /> + + + v{info.database.migrationVersion} + {info.database.migrationDirty && ( + dirty + )} + + } + /> + + + +
+ ); + }, +); diff --git a/src/components/admin/TriggerDaemonsCard.tsx b/src/components/admin/TriggerDaemonsCard.tsx new file mode 100644 index 00000000..36dafc31 --- /dev/null +++ b/src/components/admin/TriggerDaemonsCard.tsx @@ -0,0 +1,239 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useCallback, useRef, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { ArrowPathIcon, PlayIcon } from "@heroicons/react/20/solid"; +import { CommandLineIcon } from "@heroicons/react/24/outline"; +import { useInstanceAdmin } from "@/context/InstanceAdminContext"; +import { + adminSSETrigger, + AdminAPIError, + type DaemonSSEEvent, +} from "@/services/adminApi"; + +interface Daemon { + id: string; + label: string; + description: string; + /** Admin API endpoint path (relative to /api/v1) */ + endpoint: string; + /** Whether a body payload with assetId is needed */ + requiresAssetId?: boolean; +} + +const daemons: Daemon[] = [ + { + id: "openSourceInsights", + label: "Open Source Insights", + description: "Sync open-source project metadata from deps.dev.", + endpoint: "/admin/daemons/open-source-insights/trigger", + }, + { + id: "vulndb", + label: "VulnDB Import", + description: "Run an incremental VulnDB import from upstream diffs.", + endpoint: "/admin/daemons/vulndb/trigger", + }, + { + id: "vulndbCleanup", + label: "VulnDB Cleanup", + description: "Remove orphaned database tables from failed imports.", + endpoint: "/admin/daemons/vulndb-cleanup/trigger", + }, + { + id: "fixedVersions", + label: "Fixed Versions", + description: "Update known fixed versions for tracked vulnerabilities.", + endpoint: "/admin/daemons/fixed-versions/trigger", + }, + { + id: "assetPipelineAll", + label: "Asset Pipeline (All)", + description: "Run the asset pipeline for every asset on this instance.", + endpoint: "/admin/daemons/asset-pipeline-all/trigger", + }, + { + id: "assetPipelineSingle", + label: "Asset Pipeline (Single)", + description: "Run the asset pipeline for a single asset by ID.", + endpoint: "/admin/daemons/asset-pipeline-single/trigger", + requiresAssetId: true, + }, +]; + +export default function TriggerDaemonsCard() { + const { getPrivateKey } = useInstanceAdmin(); + const [running, setRunning] = useState>({}); + const [logs, setLogs] = useState>({}); + const [assetId, setAssetId] = useState(""); + const logContainerRefs = useRef>({}); + + const appendLog = useCallback((daemonId: string, message: string) => { + setLogs((prev) => ({ + ...prev, + [daemonId]: [...(prev[daemonId] ?? []), message], + })); + // Auto-scroll the log container to the bottom without moving the page + setTimeout(() => { + const el = logContainerRefs.current[daemonId]; + if (el) { + el.scrollTop = el.scrollHeight; + } + }, 50); + }, []); + + const handleTriggerDaemon = useCallback( + async (daemon: Daemon) => { + if (daemon.requiresAssetId && !assetId.trim()) { + toast.error("Please enter an asset ID."); + return; + } + + const privateKey = getPrivateKey(); + if (!privateKey) { + toast.error("Admin session expired. Please re-authenticate."); + return; + } + + // Clear previous logs and set running + setLogs((prev) => ({ ...prev, [daemon.id]: [] })); + setRunning((prev) => ({ ...prev, [daemon.id]: true })); + + try { + const body = daemon.requiresAssetId + ? JSON.stringify({ assetId: assetId.trim() }) + : undefined; + + await adminSSETrigger( + daemon.endpoint, + privateKey, + (evt: DaemonSSEEvent) => { + switch (evt.event) { + case "log": + appendLog(daemon.id, evt.data); + break; + case "done": + appendLog(daemon.id, "Completed successfully."); + toast.success(`${daemon.label} completed.`); + break; + case "error": + try { + const parsed = JSON.parse(evt.data); + appendLog(daemon.id, `Error: ${parsed.message}`); + toast.error(`${daemon.label} failed: ${parsed.message}`); + } catch { + appendLog(daemon.id, `Error: ${evt.data}`); + toast.error(`${daemon.label} failed.`); + } + break; + } + }, + body, + ); + } catch (err) { + const message = err instanceof Error ? err.message : "unknown error"; + appendLog(daemon.id, `Error: ${message}`); + if (err instanceof AdminAPIError && err.status === 429) { + toast.warning(`${daemon.label}: ${message}`); + } else { + toast.error(`${daemon.label}: ${message}`); + } + } finally { + setRunning((prev) => ({ ...prev, [daemon.id]: false })); + } + }, + [assetId, getPrivateKey, appendLog], + ); + + return ( + + + + + Trigger Daemons + + + Manually trigger individual background daemons. Each daemon has a + 5-minute cooldown between triggers (shared across all API instances). + + + +
+ {daemons.map((d) => ( +
+
+
+
+ {d.label} +
+

+ {d.description} +

+ {d.requiresAssetId && ( + setAssetId(e.target.value)} + /> + )} +
+ +
+ {/* SSE log output */} + {logs[d.id] && logs[d.id].length > 0 && ( +
{ + logContainerRefs.current[d.id] = el; + }} + className="max-h-32 overflow-y-auto rounded bg-muted/50 px-2 py-1.5 font-mono text-xs text-muted-foreground" + > + {logs[d.id].map((line, i) => ( +
{line}
+ ))} +
+ )} +
+ ))} +
+
+
+ ); +} diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx index a6bc9e16..f8974ba3 100644 --- a/src/components/ui/alert-dialog.tsx +++ b/src/components/ui/alert-dialog.tsx @@ -34,7 +34,7 @@ const AlertDialogContent = React.forwardRef< +>(({ className, ...props }, ref) => ( +
+)); +FieldGroup.displayName = "FieldGroup"; + +const Field = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Field.displayName = "Field"; + +const FieldLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +