From f5f92fadd5d6c6feb2de627837f862beb7c2186d Mon Sep 17 00:00:00 2001 From: Grant Date: Sat, 10 May 2025 21:52:49 -0600 Subject: [PATCH 01/10] [wip] initial --- package-lock.json | 1441 +++++++---------- packages/client/package.json | 3 + .../client/src/components/Info/Altcha.tsx | 66 + .../src/components/Info/InfoSidebar.tsx | 16 +- packages/client/src/index.tsx | 1 + packages/client/src/lib/captcha.ts | 44 + packages/server/package.json | 1 + packages/server/src/api/client.ts | 13 + .../src/controllers/CaptchaController.ts | 48 + packages/server/src/index.ts | 2 + 10 files changed, 759 insertions(+), 876 deletions(-) create mode 100644 packages/client/src/components/Info/Altcha.tsx create mode 100644 packages/client/src/lib/captcha.ts create mode 100644 packages/server/src/controllers/CaptchaController.ts diff --git a/package-lock.json b/package-lock.json index 2d349bb..6946bc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,6 +74,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@altcha/crypto": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@altcha/crypto/-/crypto-0.0.1.tgz", + "integrity": "sha512-qZMdnoD3lAyvfSUMNtC2adRi666Pxdcw9zqfMU5qBOaJWqpN9K+eqQGWqeiKDMqL0SF+EytNG4kR/Pr/99GJ6g==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -88,14 +93,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -493,18 +498,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "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==", "dev": true, "engines": { "node": ">=6.9.0" @@ -534,25 +539,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", "dev": true, "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -2141,25 +2146,22 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2207,13 +2209,13 @@ "dev": true }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2257,9 +2259,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -2273,9 +2275,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -2289,9 +2291,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -2305,9 +2307,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -2321,9 +2323,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -2337,9 +2339,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -2353,9 +2355,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -2369,9 +2371,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -2385,9 +2387,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -2401,9 +2403,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -2417,9 +2419,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -2433,9 +2435,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -2449,9 +2451,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -2465,9 +2467,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -2481,9 +2483,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -2497,9 +2499,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -2513,9 +2515,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -2529,9 +2531,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", "cpu": [ "arm64" ], @@ -2545,9 +2547,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -2561,9 +2563,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -2577,9 +2579,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -2593,9 +2595,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -2609,9 +2611,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -2625,9 +2627,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -2641,9 +2643,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -5085,9 +5087,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.56.0.tgz", - "integrity": "sha512-Wr39+94UNNG3Ei9nv3pHd4AJ63gq5nSemMRpCd8fPwDL9rN3vK26lzxfH27mw16XzOSO+TpyQwBAMaLxaPWG0g==", + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", "dependencies": { "@opentelemetry/api": "^1.3.0" }, @@ -5096,9 +5098,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.0.tgz", - "integrity": "sha512-roCetrG/cz0r/gugQm/jFo75UxblVvHaNSRoR0kSSRSzXFAiIBqFCZuH458BHBNRtRe+0yJdIJ21L9t94bw7+g==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "engines": { "node": ">=14" }, @@ -5107,9 +5109,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.0.tgz", - "integrity": "sha512-Q/3u/K73KUjTCnFUP97ZY+pBjQ1kPEgjOfXj/bJl8zW7GbXdkw6cwuyZk6ZTXkVgCBsYRYUzx4fvYK1jxdb9MA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, @@ -5121,11 +5123,11 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.56.0.tgz", - "integrity": "sha512-2KkGBKE+FPXU1F0zKww+stnlUxUTlBvLCiWdP63Z9sqXYeNI/ziNzsxAp4LAdUcTQmXjw1IWgvm5CAb/BHy99w==", + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", + "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", "dependencies": { - "@opentelemetry/api-logs": "0.56.0", + "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", @@ -5140,12 +5142,12 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.45.0.tgz", - "integrity": "sha512-SlKLsOS65NGMIBG1Lh/hLrMDU9WzTUF25apnV6ZmWZB1bBmUwan7qrwwrTu1cL5LzJWCXOdZPuTaxP7pC9qxnQ==", + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz", + "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.1", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5156,12 +5158,12 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.42.0.tgz", - "integrity": "sha512-bOoYHBmbnq/jFaLHmXJ55VQ6jrH5fHDMAPjFM0d3JvR0dvIqW7anEoNC33QqYGFYUfVJ50S0d/eoyF61ALqQuA==", + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.0.tgz", + "integrity": "sha512-Q57JGpH6T4dkYHo9tKXONgLtxzsh1ZEW5M9A/OwKrZFyEpLqWgjhcZ3hIuVvDlhb426iDF1f9FPToV/mi5rpeA==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.36" }, @@ -5181,11 +5183,11 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.15.0.tgz", - "integrity": "sha512-5fP35A2jUPk4SerVcduEkpbRAIoqa2PaP5rWumn01T1uSbavXNccAr3Xvx1N6xFtZxXpLJq4FYqGFnMgDWgVng==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.0.tgz", + "integrity": "sha512-88+qCHZC02up8PwKHk0UQKLLqGGURzS3hFQBZC7PnGwReuoKjHXS1o29H58S+QkXJpkTr2GACbx8j6mUoGjNPA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0" + "@opentelemetry/instrumentation": "^0.57.0" }, "engines": { "node": ">=14" @@ -5195,12 +5197,12 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.46.0.tgz", - "integrity": "sha512-BCEClDj/HPq/1xYRAlOr6z+OUnbp2eFp18DSrgyQz4IT9pkdYk8eWHnMi9oZSqlC6J5mQzkFmaW5RrKb1GLQhg==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.0.tgz", + "integrity": "sha512-XFWVx6k0XlU8lu6cBlCa29ONtVt6ADEjmxtyAyeF2+rifk8uBJbk1La0yIVfI0DoKURGbaEDTNelaXG9l/lNNQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5211,12 +5213,12 @@ } }, "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.43.0.tgz", - "integrity": "sha512-Lmdsg7tYiV+K3/NKVAQfnnLNGmakUOFdB0PhoTh2aXuSyCmyNnnDvhn2MsArAPTZ68wnD5Llh5HtmiuTkf+DyQ==", + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.44.1.tgz", + "integrity": "sha512-RoVeMGKcNttNfXMSl6W4fsYoCAYP1vi6ZAWIGhBY+o7R9Y0afA7f9JJL0j8LHbyb0P0QhSYk+6O56OwI2k4iRQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5227,12 +5229,12 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.18.0.tgz", - "integrity": "sha512-kC40y6CEMONm8/MWwoF5GHWIC7gOdF+g3sgsjfwJaUkgD6bdWV+FgG0XApqSbTQndICKzw3RonVk8i7s6mHqhA==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.0.tgz", + "integrity": "sha512-JGwmHhBkRT2G/BYNV1aGI+bBjJu4fJUD/5/Jat0EWZa2ftrLV3YE8z84Fiij/wK32oMZ88eS8DI4ecLGZhpqsQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0" + "@opentelemetry/instrumentation": "^0.57.0" }, "engines": { "node": ">=14" @@ -5242,11 +5244,11 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.42.0.tgz", - "integrity": "sha512-J4QxqiQ1imtB9ogzsOnHra0g3dmmLAx4JCeoK3o0rFes1OirljNHnO8Hsj4s1jAir8WmWvnEEQO1y8yk6j2tog==", + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.0.tgz", + "integrity": "sha512-at8GceTtNxD1NfFKGAuwtqM41ot/TpcLh+YsGe4dhf7gvv1HW/ZWdq6nfRtS6UjIvZJOokViqLPJ3GVtZItAnQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0" + "@opentelemetry/instrumentation": "^0.57.0" }, "engines": { "node": ">=14" @@ -5256,11 +5258,11 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.46.0.tgz", - "integrity": "sha512-tplk0YWINSECcK89PGM7IVtOYenXyoOuhOQlN0X0YrcDUfMS4tZMKkVc0vyhNWYYrexnUHwNry2YNBNugSpjlQ==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.0.tgz", + "integrity": "sha512-Cc8SMf+nLqp0fi8oAnooNEfwZWFnzMiBHCGmDFYqmgjPylyLmi83b+NiTns/rKGwlErpW0AGPt0sMpkbNlzn8w==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0" + "@opentelemetry/instrumentation": "^0.57.0" }, "engines": { "node": ">=14" @@ -5270,12 +5272,12 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.44.0.tgz", - "integrity": "sha512-4HdNIMNXWK1O6nsaQOrACo83QWEVoyNODTdVDbUqtqXiv2peDfD0RAPhSQlSGWLPw3S4d9UoOmrV7s2HYj6T2A==", + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.1.tgz", + "integrity": "sha512-VH6mU3YqAKTePPfUPwfq4/xr049774qWtfTuJqVHoVspCLiT3bW+fCQ1toZxt6cxRPYASoYaBsMA3CWo8B8rcw==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5286,12 +5288,12 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.56.0.tgz", - "integrity": "sha512-/bWHBUAq8VoATnH9iLk5w8CE9+gj+RgYSUphe7hry472n6fYl7+4PvuScoQMdmSUTprKq/gyr2kOWL6zrC7FkQ==", + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.1.tgz", + "integrity": "sha512-ThLmzAQDs7b/tdKI3BV2+yawuF09jF111OFsovqT1Qj3D8vjwKBwhi/rDE5xethwn4tSXtZcJ9hBsVAlWFQZ7g==", "dependencies": { - "@opentelemetry/core": "1.29.0", - "@opentelemetry/instrumentation": "0.56.0", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "0.57.1", "@opentelemetry/semantic-conventions": "1.28.0", "forwarded-parse": "2.1.2", "semver": "^7.5.2" @@ -5303,26 +5305,42 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.29.0.tgz", - "integrity": "sha512-gmT7vAreXl0DTHD2rVZcw3+l2g84+5XiHIqdBUxXbExymPCvSsGOpiwMmn8nkiJur28STV31wnhIDrzWDPzjfA==", + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/api-logs": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.1.tgz", + "integrity": "sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg==", "dependencies": { - "@opentelemetry/semantic-conventions": "1.28.0" + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/instrumentation": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.1.tgz", + "integrity": "sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.1", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" }, "engines": { "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": "^1.3.0" } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.46.0.tgz", - "integrity": "sha512-sOdsq8oGi29V58p1AkefHvuB3l2ymP1IbxRIX3y4lZesQWKL8fLhBmy8xYjINSQ5gHzWul2yoz7pe7boxhZcqQ==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.0.tgz", + "integrity": "sha512-4HqP9IBC8e7pW9p90P3q4ox0XlbLGme65YTrA3UTLvqvo4Z6b0puqZQP203YFu8m9rE/luLfaG7/xrwwqMUpJw==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/redis-common": "^0.36.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -5334,11 +5352,11 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.6.0.tgz", - "integrity": "sha512-MGQrzqEUAl0tacKJUFpuNHJesyTi51oUzSVizn7FdvJplkRIdS11FukyZBZJEscofSEdk7Ycmg+kNMLi5QHUFg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.0.tgz", + "integrity": "sha512-LB+3xiNzc034zHfCtgs4ITWhq6Xvdo8bsq7amR058jZlf2aXXDrN9SV4si4z2ya9QX4tz6r4eZJwDkXOp14/AQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5349,11 +5367,11 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.43.0.tgz", - "integrity": "sha512-mOp0TRQNFFSBj5am0WF67fRO7UZMUmsF3/7HSDja9g3H4pnj+4YNvWWyZn4+q0rGrPtywminAXe0rxtgaGYIqg==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.0.tgz", + "integrity": "sha512-SlT0+bLA0Lg3VthGje+bSZatlGHw/vwgQywx0R/5u9QC59FddTQSPJeWNw29M6f8ScORMeUOOTwihlQAn4GkJQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5364,12 +5382,12 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.46.0.tgz", - "integrity": "sha512-RcWXMQdJQANnPUaXbHY5G0Fg6gmleZ/ZtZeSsekWPaZmQq12FGk0L1UwodIgs31OlYfviAZ4yTeytoSUkgo5vQ==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.0.tgz", + "integrity": "sha512-HFdvqf2+w8sWOuwtEXayGzdZ2vWpCKEQv5F7+2DSA74Te/Cv4rvb2E5So5/lh+ok4/RAIPuvCbCb/SHQFzMmbw==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5380,11 +5398,11 @@ } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.43.0.tgz", - "integrity": "sha512-fZc+1eJUV+tFxaB3zkbupiA8SL3vhDUq89HbDNg1asweYrEb9OlHIB+Ot14ZiHUc1qCmmWmZHbPTwa56mVVwzg==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.0.tgz", + "integrity": "sha512-Tn7emHAlvYDFik3vGU0mdwvWJDwtITtkJ+5eT2cUquct6nIs+H8M47sqMJkCpyPe5QIBJoTOHxmc6mj9lz6zDw==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0" + "@opentelemetry/instrumentation": "^0.57.0" }, "engines": { "node": ">=14" @@ -5394,11 +5412,11 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.50.0.tgz", - "integrity": "sha512-DtwJMjYFXFT5auAvv8aGrBj1h3ciA/dXQom11rxL7B1+Oy3FopSpanvwYxJ+z0qmBrQ1/iMuWELitYqU4LnlkQ==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.51.0.tgz", + "integrity": "sha512-cMKASxCX4aFxesoj3WK8uoQ0YUrRvnfxaO72QWI2xLu5ZtgX/QvdGBlU3Ehdond5eb74c2s1cqRQUIptBnKz1g==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5409,12 +5427,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.45.0.tgz", - "integrity": "sha512-zHgNh+A01C5baI2mb5dAGyMC7DWmUpOfwpV8axtC0Hd5Uzqv+oqKgKbVDIVhOaDkPxjgVJwYF9YQZl2pw2qxIA==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.0.tgz", + "integrity": "sha512-mtVv6UeaaSaWTeZtLo4cx4P5/ING2obSqfWGItIFSunQBrYROfhuVe7wdIrFUs2RH1tn2YYpAJyMaRe/bnTTIQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5425,11 +5443,11 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.44.0.tgz", - "integrity": "sha512-al7jbXvT/uT1KV8gdNDzaWd5/WXf+mrjrsF0/NtbnqLa0UUFGgQnoK3cyborgny7I+KxWhL8h7YPTf6Zq4nKsg==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.0.tgz", + "integrity": "sha512-tWWyymgwYcTwZ4t8/rLDfPYbOTF3oYB8SxnYMtIQ1zEf5uDm90Ku3i6U/vhaMyfHNlIHvDhvJh+qx5Nc4Z3Acg==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mysql": "2.15.26" }, @@ -5441,11 +5459,11 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.44.0.tgz", - "integrity": "sha512-e9QY4AGsjGFwmfHd6kBa4yPaQZjAq2FuxMb0BbKlXCAjG+jwqw+sr9xWdJGR60jMsTq52hx3mAlE3dUJ9BipxQ==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.0.tgz", + "integrity": "sha512-qLslv/EPuLj0IXFvcE3b0EqhWI8LKmrgRPIa4gUd8DllbBpqJAvLNJSv3cC6vWwovpbSI3bagNO/3Q2SuXv2xA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.40.1" }, @@ -5457,11 +5475,11 @@ } }, "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.43.0.tgz", - "integrity": "sha512-NEo4RU7HTjiaXk3curqXUvCb9alRiFWxQY//+hvDXwWLlADX2vB6QEmVCeEZrKO+6I/tBrI4vNdAnbCY9ldZVg==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.44.0.tgz", + "integrity": "sha512-t16pQ7A4WYu1yyQJZhRKIfUNvl5PAaF2pEteLvgJb/BWdd1oNuU1rOYt4S825kMy+0q4ngiX281Ss9qiwHfxFQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -5472,12 +5490,12 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.49.0.tgz", - "integrity": "sha512-3alvNNjPXVdAPdY1G7nGRVINbDxRK02+KAugDiEpzw0jFQfU8IzFkSWA4jyU4/GbMxKvHD+XIOEfSjpieSodKw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.50.0.tgz", + "integrity": "sha512-TtLxDdYZmBhFswm8UIsrDjh/HFBeDXd4BLmE8h2MxirNHewLJ0VS9UUddKKEverb5Sm2qFVjqRjcU+8Iw4FJ3w==", "dependencies": { "@opentelemetry/core": "^1.26.0", - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "1.27.0", "@opentelemetry/sql-common": "^0.40.1", "@types/pg": "8.6.1", @@ -5499,11 +5517,11 @@ } }, "node_modules/@opentelemetry/instrumentation-redis-4": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.45.0.tgz", - "integrity": "sha512-Sjgym1xn3mdxPRH5CNZtoz+bFd3E3NlGIu7FoYr4YrQouCc9PbnmoBcmSkEdDy5LYgzNildPgsjx9l0EKNjKTQ==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.0.tgz", + "integrity": "sha512-aTUWbzbFMFeRODn3720TZO0tsh/49T8H3h8vVnVKJ+yE36AeW38Uj/8zykQ/9nO8Vrtjr5yKuX3uMiG/W8FKNw==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/redis-common": "^0.36.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -5515,11 +5533,11 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.17.0.tgz", - "integrity": "sha512-yRBz2409an03uVd1Q2jWMt3SqwZqRFyKoWYYX3hBAtPDazJ4w5L+1VOij71TKwgZxZZNdDBXImTQjii+VeuzLg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.0.tgz", + "integrity": "sha512-9zhjDpUDOtD+coeADnYEJQ0IeLVCj7w/hqzIutdp5NqS1VqTAanaEfsEcSypyvYv5DX3YOsTUoF+nr2wDXPETA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/tedious": "^4.0.14" }, @@ -5531,12 +5549,12 @@ } }, "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.9.0.tgz", - "integrity": "sha512-lxc3cpUZ28CqbrWcUHxGW/ObDpMOYbuxF/ZOzeFZq54P9uJ2Cpa8gcrC9F716mtuiMaekwk8D6n34vg/JtkkxQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.0.tgz", + "integrity": "sha512-vm+V255NGw9gaSsPD6CP0oGo8L55BffBc8KnxqsMuc6XiAD1L8SFNzsW0RHhxJFqy9CJaJh+YiJ5EHXuZ5rZBw==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.56.0" + "@opentelemetry/instrumentation": "^0.57.0" }, "engines": { "node": ">=14" @@ -5554,11 +5572,11 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.0.tgz", - "integrity": "sha512-5mGMjL0Uld/99t7/pcd7CuVtJbkARckLVuiOX84nO8RtLtIz0/J6EOHM2TGvPZ6F4K+XjUq13gMx14w80SVCQg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", "dependencies": { - "@opentelemetry/core": "1.30.0", + "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "engines": { @@ -5569,12 +5587,12 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.0.tgz", - "integrity": "sha512-RKQDaDIkV7PwizmHw+rE/FgfB2a6MBx+AEVVlAHXRG1YYxLiBpPX2KhmoB99R5vA4b72iJrjle68NDWnbrE9Dg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", "dependencies": { - "@opentelemetry/core": "1.30.0", - "@opentelemetry/resources": "1.30.0", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "engines": { @@ -7311,9 +7329,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", - "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", + "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", "cpu": [ "arm" ], @@ -7324,9 +7342,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", - "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", + "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", "cpu": [ "arm64" ], @@ -7337,9 +7355,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", - "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", + "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", "cpu": [ "arm64" ], @@ -7350,9 +7368,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", - "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", + "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", "cpu": [ "x64" ], @@ -7363,9 +7381,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", - "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", + "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", "cpu": [ "arm64" ], @@ -7376,9 +7394,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", - "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", + "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", "cpu": [ "x64" ], @@ -7389,9 +7407,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", - "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", + "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", "cpu": [ "arm" ], @@ -7402,9 +7420,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", - "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", + "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", "cpu": [ "arm" ], @@ -7415,9 +7433,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", - "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", + "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", "cpu": [ "arm64" ], @@ -7428,9 +7446,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", - "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", + "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", "cpu": [ "arm64" ], @@ -7441,9 +7459,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", - "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", + "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", "cpu": [ "loong64" ], @@ -7454,9 +7472,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", - "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", + "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", "cpu": [ "ppc64" ], @@ -7467,9 +7485,22 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", - "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", + "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", + "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", "cpu": [ "riscv64" ], @@ -7480,9 +7511,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", - "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", + "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", "cpu": [ "s390x" ], @@ -7493,9 +7524,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", - "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", + "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", "cpu": [ "x64" ], @@ -7506,9 +7537,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", - "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", + "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", "cpu": [ "x64" ], @@ -7519,9 +7550,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", - "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", + "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", "cpu": [ "arm64" ], @@ -7532,9 +7563,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", - "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", + "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", "cpu": [ "ia32" ], @@ -7545,9 +7576,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", - "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", + "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", "cpu": [ "x64" ], @@ -8054,52 +8085,52 @@ } }, "node_modules/@sentry/core": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.47.0.tgz", - "integrity": "sha512-iSEJZMe3DOcqBFZQAqgA3NB2lCWBc4Gv5x/SCri/TVg96wAlss4VrUunSI2Mp0J4jJ5nJcJ2ChqHSBAU48k3FA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.55.0.tgz", + "integrity": "sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==", "engines": { "node": ">=14.18" } }, "node_modules/@sentry/node": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-8.47.0.tgz", - "integrity": "sha512-tMzeU3KkmDi2OVvSu+Ah5pwoi7srsSyc1DovBbRQU96RFf/lOFzGe9JERa1MyDUqqLH95NqnPTNsa4Amb8/Vxg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-8.55.0.tgz", + "integrity": "sha512-h10LJLDTRAzYgay60Oy7moMookqqSZSviCWkkmHZyaDn+4WURnPp5SKhhfrzPRQcXKrweiOwDSHBgn1tweDssg==", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.29.0", - "@opentelemetry/core": "^1.29.0", - "@opentelemetry/instrumentation": "^0.56.0", - "@opentelemetry/instrumentation-amqplib": "^0.45.0", - "@opentelemetry/instrumentation-connect": "0.42.0", - "@opentelemetry/instrumentation-dataloader": "0.15.0", - "@opentelemetry/instrumentation-express": "0.46.0", - "@opentelemetry/instrumentation-fastify": "0.43.0", - "@opentelemetry/instrumentation-fs": "0.18.0", - "@opentelemetry/instrumentation-generic-pool": "0.42.0", - "@opentelemetry/instrumentation-graphql": "0.46.0", - "@opentelemetry/instrumentation-hapi": "0.44.0", - "@opentelemetry/instrumentation-http": "0.56.0", - "@opentelemetry/instrumentation-ioredis": "0.46.0", - "@opentelemetry/instrumentation-kafkajs": "0.6.0", - "@opentelemetry/instrumentation-knex": "0.43.0", - "@opentelemetry/instrumentation-koa": "0.46.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.43.0", - "@opentelemetry/instrumentation-mongodb": "0.50.0", - "@opentelemetry/instrumentation-mongoose": "0.45.0", - "@opentelemetry/instrumentation-mysql": "0.44.0", - "@opentelemetry/instrumentation-mysql2": "0.44.0", - "@opentelemetry/instrumentation-nestjs-core": "0.43.0", - "@opentelemetry/instrumentation-pg": "0.49.0", - "@opentelemetry/instrumentation-redis-4": "0.45.0", - "@opentelemetry/instrumentation-tedious": "0.17.0", - "@opentelemetry/instrumentation-undici": "0.9.0", - "@opentelemetry/resources": "^1.29.0", - "@opentelemetry/sdk-trace-base": "^1.29.0", + "@opentelemetry/context-async-hooks": "^1.30.1", + "@opentelemetry/core": "^1.30.1", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/instrumentation-amqplib": "^0.46.0", + "@opentelemetry/instrumentation-connect": "0.43.0", + "@opentelemetry/instrumentation-dataloader": "0.16.0", + "@opentelemetry/instrumentation-express": "0.47.0", + "@opentelemetry/instrumentation-fastify": "0.44.1", + "@opentelemetry/instrumentation-fs": "0.19.0", + "@opentelemetry/instrumentation-generic-pool": "0.43.0", + "@opentelemetry/instrumentation-graphql": "0.47.0", + "@opentelemetry/instrumentation-hapi": "0.45.1", + "@opentelemetry/instrumentation-http": "0.57.1", + "@opentelemetry/instrumentation-ioredis": "0.47.0", + "@opentelemetry/instrumentation-kafkajs": "0.7.0", + "@opentelemetry/instrumentation-knex": "0.44.0", + "@opentelemetry/instrumentation-koa": "0.47.0", + "@opentelemetry/instrumentation-lru-memoizer": "0.44.0", + "@opentelemetry/instrumentation-mongodb": "0.51.0", + "@opentelemetry/instrumentation-mongoose": "0.46.0", + "@opentelemetry/instrumentation-mysql": "0.45.0", + "@opentelemetry/instrumentation-mysql2": "0.45.0", + "@opentelemetry/instrumentation-nestjs-core": "0.44.0", + "@opentelemetry/instrumentation-pg": "0.50.0", + "@opentelemetry/instrumentation-redis-4": "0.46.0", + "@opentelemetry/instrumentation-tedious": "0.18.0", + "@opentelemetry/instrumentation-undici": "0.10.0", + "@opentelemetry/resources": "^1.30.1", + "@opentelemetry/sdk-trace-base": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.28.0", "@prisma/instrumentation": "5.22.0", - "@sentry/core": "8.47.0", - "@sentry/opentelemetry": "8.47.0", + "@sentry/core": "8.55.0", + "@sentry/opentelemetry": "8.55.0", "import-in-the-middle": "^1.11.2" }, "engines": { @@ -8107,20 +8138,21 @@ } }, "node_modules/@sentry/opentelemetry": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-8.47.0.tgz", - "integrity": "sha512-wunyBIUPeY6Kx3SFhOQqOPs+hyRADO5bztpo8aZ3N3xfzhefSTOdrgUroKvHx1DvoQO6MAlykcuUFps3yfaqmg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-8.55.0.tgz", + "integrity": "sha512-UvatdmSr3Xf+4PLBzJNLZ2JjG1yAPWGe/VrJlJAqyTJ2gKeTzgXJJw8rp4pbvNZO8NaTGEYhhO+scLUj0UtLAQ==", "dependencies": { - "@sentry/core": "8.47.0" + "@sentry/core": "8.55.0" }, "engines": { "node": ">=14.18" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.29.0", - "@opentelemetry/instrumentation": "^0.56.0", - "@opentelemetry/sdk-trace-base": "^1.29.0", + "@opentelemetry/context-async-hooks": "^1.30.1", + "@opentelemetry/core": "^1.30.1", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/sdk-trace-base": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.28.0" } }, @@ -8602,9 +8634,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, "node_modules/@types/express": { @@ -9530,6 +9562,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/altcha": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/altcha/-/altcha-1.4.4.tgz", + "integrity": "sha512-4ZoRt4xOz4+NnOcnMvW9gEDjGUC6d9k+4xf6RFty6FWPbyNlYE/2BPJaOhP6WS/PliYkXnPjer1Yk/U47VMiqQ==", + "hasInstallScript": true, + "dependencies": { + "@altcha/crypto": "^0.0.1" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "4.18.0" + } + }, + "node_modules/altcha-lib": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/altcha-lib/-/altcha-lib-1.2.0.tgz", + "integrity": "sha512-S5WF8QLNRaM1hvK24XPhOLfu9is2EBCvH7+nv50sM5CaIdUCqQCd0WV/qm/ZZFGTdSoKLuDp+IapZxBLvC+SNg==" + }, + "node_modules/altcha/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -11469,9 +11530,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "dev": true, "hasInstallScript": true, "bin": { @@ -11481,31 +11542,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/escalade": { @@ -12881,16 +12942,18 @@ } }, "node_modules/formidable": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", - "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, - "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^2.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -13308,16 +13371,6 @@ "node": ">= 0.4" } }, - "node_modules/hexoid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", - "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -13394,6 +13447,14 @@ "node": ">=10.17.0" } }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -15939,9 +16000,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", - "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.9.5.tgz", + "integrity": "sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg==" }, "node_modules/pg-types": { "version": "2.2.0", @@ -16069,9 +16130,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -16087,7 +16148,7 @@ } ], "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -16527,14 +16588,12 @@ } }, "node_modules/react-router": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", - "integrity": "sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", + "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", "dependencies": { - "@types/cookie": "^0.6.0", "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0", - "turbo-stream": "2.4.0" + "set-cookie-parser": "^2.6.0" }, "engines": { "node": ">=20.0.0" @@ -16550,11 +16609,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.1.tgz", - "integrity": "sha512-vSrQHWlJ5DCfyrhgo0k6zViOe9ToK8uT5XGSmnuC2R3/g261IdIMpZVqfjD6vWSXdnf5Czs4VA/V60oVR6/jnA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz", + "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", "dependencies": { - "react-router": "7.1.1" + "react-router": "7.6.0" }, "engines": { "node": ">=20.0.0" @@ -16564,11 +16623,6 @@ "react-dom": ">=18" } }, - "node_modules/react-router/node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" - }, "node_modules/react-router/node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -16743,11 +16797,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", @@ -16954,12 +17003,12 @@ } }, "node_modules/rollup": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", - "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", + "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", "dev": true, "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -16969,25 +17018,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.29.1", - "@rollup/rollup-android-arm64": "4.29.1", - "@rollup/rollup-darwin-arm64": "4.29.1", - "@rollup/rollup-darwin-x64": "4.29.1", - "@rollup/rollup-freebsd-arm64": "4.29.1", - "@rollup/rollup-freebsd-x64": "4.29.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", - "@rollup/rollup-linux-arm-musleabihf": "4.29.1", - "@rollup/rollup-linux-arm64-gnu": "4.29.1", - "@rollup/rollup-linux-arm64-musl": "4.29.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", - "@rollup/rollup-linux-riscv64-gnu": "4.29.1", - "@rollup/rollup-linux-s390x-gnu": "4.29.1", - "@rollup/rollup-linux-x64-gnu": "4.29.1", - "@rollup/rollup-linux-x64-musl": "4.29.1", - "@rollup/rollup-win32-arm64-msvc": "4.29.1", - "@rollup/rollup-win32-ia32-msvc": "4.29.1", - "@rollup/rollup-win32-x64-msvc": "4.29.1", + "@rollup/rollup-android-arm-eabi": "4.40.2", + "@rollup/rollup-android-arm64": "4.40.2", + "@rollup/rollup-darwin-arm64": "4.40.2", + "@rollup/rollup-darwin-x64": "4.40.2", + "@rollup/rollup-freebsd-arm64": "4.40.2", + "@rollup/rollup-freebsd-x64": "4.40.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", + "@rollup/rollup-linux-arm-musleabihf": "4.40.2", + "@rollup/rollup-linux-arm64-gnu": "4.40.2", + "@rollup/rollup-linux-arm64-musl": "4.40.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-musl": "4.40.2", + "@rollup/rollup-linux-s390x-gnu": "4.40.2", + "@rollup/rollup-linux-x64-gnu": "4.40.2", + "@rollup/rollup-linux-x64-musl": "4.40.2", + "@rollup/rollup-win32-arm64-msvc": "4.40.2", + "@rollup/rollup-win32-ia32-msvc": "4.40.2", + "@rollup/rollup-win32-x64-msvc": "4.40.2", "fsevents": "~2.3.2" } }, @@ -18208,6 +18258,48 @@ "node": ">=0.8" } }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -18442,12 +18534,12 @@ "dev": true }, "node_modules/tsx": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", - "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "version": "4.19.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", + "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", "dev": true, "dependencies": { - "esbuild": "~0.23.0", + "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -18460,434 +18552,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" - } - }, - "node_modules/turbo-stream": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", - "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -19267,14 +18931,17 @@ } }, "node_modules/vite": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", - "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "dependencies": { - "esbuild": "^0.24.2", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -19352,6 +19019,32 @@ "vite": "^2.3.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -19833,8 +19526,11 @@ "@nextui-org/react": "^2.6.11", "@sc07-canvas/lib": "^1.0.0", "@theme-toggles/react": "^4.1.0", + "altcha": "^1.4.4", + "altcha-lib": "^1.2.0", "eventemitter3": "^5.0.1", "framer-motion": "^11.3.2", + "i": "^0.3.7", "lodash.throttle": "^4.1.1", "prop-types": "^15.8.1", "react-zoom-pan-pinch": "^3.4.1", @@ -19903,6 +19599,7 @@ "@prisma/client": "^6.2.1", "@sc07-canvas/lib": "^1.0.0", "@sentry/node": "^8.47.0", + "altcha-lib": "^1.2.0", "body-parser": "^1.20.2", "bullmq": "^5.40.2", "connect-redis": "^8.0.1", diff --git a/packages/client/package.json b/packages/client/package.json index c034012..7847a23 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -19,8 +19,11 @@ "@nextui-org/react": "^2.6.11", "@sc07-canvas/lib": "^1.0.0", "@theme-toggles/react": "^4.1.0", + "altcha": "^1.4.4", + "altcha-lib": "^1.2.0", "eventemitter3": "^5.0.1", "framer-motion": "^11.3.2", + "i": "^0.3.7", "lodash.throttle": "^4.1.1", "prop-types": "^15.8.1", "react-zoom-pan-pinch": "^3.4.1", diff --git a/packages/client/src/components/Info/Altcha.tsx b/packages/client/src/components/Info/Altcha.tsx new file mode 100644 index 0000000..3ed2b5f --- /dev/null +++ b/packages/client/src/components/Info/Altcha.tsx @@ -0,0 +1,66 @@ +import { + useEffect, + useRef, + useState, + forwardRef, + useImperativeHandle, +} from "react"; + +// Importing altcha package will introduce a new element +import "altcha"; + +interface AltchaProps { + onStateChange?: (ev: Event | CustomEvent) => void; +} + +/** + * Altcha widget + * @see https://github.com/altcha-org/altcha-starter-react-ts/blob/46fcfc1146c649a03fb3b4c8bae5bbfc4db668a9/src/Altcha.tsx + */ +const Altcha = forwardRef<{ value: string | null }, AltchaProps>( + ({ onStateChange }, ref) => { + const widgetRef = useRef( + null + ); + const [value, setValue] = useState(null); + + useImperativeHandle(ref, () => { + return { + get value() { + return value; + }, + }; + }, [value]); + + useEffect(() => { + const handleStateChange = (ev: Event | CustomEvent) => { + if ("detail" in ev) { + setValue(ev.detail.payload || null); + onStateChange?.(ev); + } + }; + + const { current } = widgetRef; + + if (current) { + current.addEventListener("statechange", handleStateChange); + return () => + current.removeEventListener("statechange", handleStateChange); + } + }, [onStateChange]); + + /* Configure your `challengeurl` and remove the `test` attribute, see docs: https://altcha.org/docs/website-integration/#using-altcha-widget */ + return ( + + ); + } +); + +export default Altcha; diff --git a/packages/client/src/components/Info/InfoSidebar.tsx b/packages/client/src/components/Info/InfoSidebar.tsx index 6abb8b3..ea70726 100644 --- a/packages/client/src/components/Info/InfoSidebar.tsx +++ b/packages/client/src/components/Info/InfoSidebar.tsx @@ -3,6 +3,7 @@ import { InfoText } from "./InfoText"; import { InfoButtons } from "./InfoButtons"; import { SidebarBase } from "../SidebarBase"; import { faInfoCircle } from "@fortawesome/free-solid-svg-icons"; +import Altcha from "./Altcha"; /** * Information sidebar @@ -15,18 +16,25 @@ export const InfoSidebar = () => { const { infoSidebar, setInfoSidebar } = useAppContext(); return ( - +
+ console.log(e)} />

Build {__COMMIT_HASH__}

-
- ) -}; \ No newline at end of file + ); +}; diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx index fd62470..f4f70a8 100644 --- a/packages/client/src/index.tsx +++ b/packages/client/src/index.tsx @@ -5,6 +5,7 @@ import { NextUIProvider } from "@nextui-org/react"; import { ThemeProvider } from "next-themes"; import App from "./components/App"; +import "./lib/captcha"; import * as Sentry from "@sentry/react"; let ErrorBoundary = ({ children }: React.PropsWithChildren) => <>{children}; diff --git a/packages/client/src/lib/captcha.ts b/packages/client/src/lib/captcha.ts new file mode 100644 index 0000000..d94a31c --- /dev/null +++ b/packages/client/src/lib/captcha.ts @@ -0,0 +1,44 @@ +import { solveChallenge } from "altcha-lib"; +import { Challenge, Payload, Solution } from "altcha-lib/types"; + +export class CaptchaService { + private static instance: CaptchaService; + + static get() { + if (this.instance) return this.instance; + + return (this.instance = new CaptchaService()); + } + + private createPayload(data: Challenge, solution: Solution) { + return btoa( + JSON.stringify({ + algorithm: data.algorithm, + challenge: data.challenge, + salt: data.salt, + signature: data.signature, + number: solution.number, + took: solution.took, + } satisfies Payload & { took: number }) + ); + } + + solve(challenge: Challenge) { + const response = solveChallenge( + challenge.challenge, + challenge.salt, + challenge.algorithm, + challenge.maxnumber + ); + + return { + promise: response.promise.then(async (data) => { + if (!data) return data; + + return this.createPayload(challenge, data); + }), + }; + } +} + +window.CaptchaService = CaptchaService; diff --git a/packages/server/package.json b/packages/server/package.json index fe2ec60..ee424bc 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -45,6 +45,7 @@ "@prisma/client": "^6.2.1", "@sc07-canvas/lib": "^1.0.0", "@sentry/node": "^8.47.0", + "altcha-lib": "^1.2.0", "body-parser": "^1.20.2", "bullmq": "^5.40.2", "connect-redis": "^8.0.1", diff --git a/packages/server/src/api/client.ts b/packages/server/src/api/client.ts index ea10e6e..937922a 100644 --- a/packages/server/src/api/client.ts +++ b/packages/server/src/api/client.ts @@ -3,6 +3,7 @@ import { Router } from "express"; import { ResponseBodyError } from "openid-client"; import { CanvasController } from "../controllers/CanvasController"; +import { CaptchaController } from "../controllers/CaptchaController"; import { OpenID, OpenIDController } from "../controllers/OpenIDController"; import { getLogger } from "../lib/Logger"; import { prisma } from "../lib/prisma"; @@ -34,6 +35,18 @@ const app = Router(); // if the sentry environment variables aren't set, the loaded router will be empty app.use(SentryRouter); +app.get("/test", async (req, res) => { + const challenge = await CaptchaController.get().create(); + + res.json(challenge); +}); + +app.post("/verify", async (req, res) => { + const valid = await CaptchaController.get().verify(req.body.payload); + + res.json({ valid }); +}); + /** * Redirect to actual authorization page */ diff --git a/packages/server/src/controllers/CaptchaController.ts b/packages/server/src/controllers/CaptchaController.ts new file mode 100644 index 0000000..e285b0d --- /dev/null +++ b/packages/server/src/controllers/CaptchaController.ts @@ -0,0 +1,48 @@ +import { createChallenge, verifySolution } from "altcha-lib"; +import { Payload } from "altcha-lib/types"; + +const hmacKey = "secretsecret"; + +export class CaptchaController { + private static instance: CaptchaController | undefined; + + private constructor() {} + + static initialize() { + if (typeof CaptchaController.instance !== "undefined") { + throw new Error("CaptchaController#initialize when initialized"); + } + + // initialize class + const instance = (CaptchaController.instance = new CaptchaController()); + + return instance; + } + + static get(): CaptchaController { + if (typeof CaptchaController.instance === "undefined") { + throw new Error("CaptchaController#get when not initialized"); + } + + return CaptchaController.instance!; + } + + create({ + params, + expires, + }: { + params?: Record; + expires?: Date; + } = {}) { + return createChallenge({ + hmacKey, + algorithm: "SHA-512", + params, + expires, + }); + } + + verify(payload: string | Payload) { + return verifySolution(payload, hmacKey, true); + } +} diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index dc27589..b23cc82 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -5,6 +5,7 @@ import "./workers/worker"; import "./jobs/bullmq"; import { CanvasController } from "./controllers/CanvasController"; +import { CaptchaController } from "./controllers/CaptchaController"; import { ExpressController } from "./controllers/ExpressController"; import { OpenIDController } from "./controllers/OpenIDController"; import { Redis } from "./controllers/RedisController"; @@ -107,6 +108,7 @@ Promise.all([ await CanvasController.initialize(); ExpressController.initialize(); SocketController.initialize(); + CaptchaController.initialize(); }), ]).then(() => { Logger.info("Startup tasks have completed, starting server"); -- GitLab From c3245d37ae8cd357d6f78ebe0690c302b3e8a5eb Mon Sep 17 00:00:00 2001 From: Grant Date: Sat, 17 May 2025 21:06:29 -0600 Subject: [PATCH 02/10] initial CaptchaService & CaptchaController --- package-lock.json | 3 + packages/client/src/lib/captcha.ts | 5 +- packages/client/src/lib/network.ts | 18 +++--- packages/lib/package.json | 3 + packages/lib/src/debug.ts | 5 ++ packages/lib/src/net.ts | 10 +++- .../src/controllers/CaptchaController.ts | 43 ++++++++++++++- .../src/controllers/SocketController.ts | 55 ++++++++++++++++--- packages/server/src/utils/tryParseJSON.ts | 7 +++ 9 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 packages/server/src/utils/tryParseJSON.ts diff --git a/package-lock.json b/package-lock.json index 6946bc7..b154453 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19589,6 +19589,9 @@ "version": "1.0.0", "dependencies": { "eventemitter3": "^5.0.1" + }, + "devDependencies": { + "altcha-lib": "^1.2.0" } }, "packages/server": { diff --git a/packages/client/src/lib/captcha.ts b/packages/client/src/lib/captcha.ts index d94a31c..927d6a7 100644 --- a/packages/client/src/lib/captcha.ts +++ b/packages/client/src/lib/captcha.ts @@ -24,6 +24,8 @@ export class CaptchaService { } solve(challenge: Challenge) { + console.log("[CaptchaService] Start captcha solve..."); + console.time("[CaptchaService] solve"); const response = solveChallenge( challenge.challenge, challenge.salt, @@ -33,6 +35,7 @@ export class CaptchaService { return { promise: response.promise.then(async (data) => { + console.timeEnd("[CaptchaService] solve"); if (!data) return data; return this.createPayload(challenge, data); @@ -40,5 +43,3 @@ export class CaptchaService { }; } } - -window.CaptchaService = CaptchaService; diff --git a/packages/client/src/lib/network.ts b/packages/client/src/lib/network.ts index aef93e8..fd41a4d 100644 --- a/packages/client/src/lib/network.ts +++ b/packages/client/src/lib/network.ts @@ -11,7 +11,8 @@ import { } from "@sc07-canvas/lib/src/net"; import { toast } from "react-toastify"; import { handleAlert, handleDismiss } from "./alerts"; -import { Recaptcha } from "./recaptcha"; +import { CaptchaService } from "./captcha"; +import { Debug } from "@sc07-canvas/lib/src/debug"; export interface INetworkEvents { connected: () => void; @@ -65,6 +66,8 @@ class Network extends EventEmitter { constructor() { super(); + Debug._network = this; + this.socket.on("connect", () => { console.log("Connected to server"); toast.success("Connected to server"); @@ -99,12 +102,13 @@ class Network extends EventEmitter { console.log("Reconnect failed"); }); - this.socket.on("recaptcha", (site_key) => { - Recaptcha.load(site_key); - }); - - this.socket.on("recaptcha_challenge", (ack) => { - Recaptcha.executeChallenge(ack); + this.socket.on("captcha_challenge", (challenge, ack) => { + console.log("captcha_challenge"); + CaptchaService.get() + .solve(challenge) + .promise.then((token) => { + ack(token); + }); }); this.socket.on("user", (user) => { diff --git a/packages/lib/package.json b/packages/lib/package.json index 1b031a4..b091277 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -7,5 +7,8 @@ }, "dependencies": { "eventemitter3": "^5.0.1" + }, + "devDependencies": { + "altcha-lib": "^1.2.0" } } diff --git a/packages/lib/src/debug.ts b/packages/lib/src/debug.ts index 7702449..224aeab 100644 --- a/packages/lib/src/debug.ts +++ b/packages/lib/src/debug.ts @@ -101,6 +101,7 @@ class FlagManager extends EventEmitter { class Debugcl extends EventEmitter { readonly flags = new FlagManager(); _getRenderer: any; + _network: any; constructor() { super(); @@ -185,6 +186,10 @@ class Debugcl extends EventEmitter { getRenderer() { return this._getRenderer(); } + + getNetwork() { + return this._network; + } } const Debug = new Debugcl(); diff --git a/packages/lib/src/net.ts b/packages/lib/src/net.ts index ba3719d..497cf96 100644 --- a/packages/lib/src/net.ts +++ b/packages/lib/src/net.ts @@ -1,3 +1,5 @@ +import type { Challenge } from "altcha-lib/types"; + // socket.io export type Subscription = "heatmap"; @@ -28,8 +30,10 @@ export interface ServerToClientEvents { alert: (alert: IAlert) => void; alert_dismiss: (id: string) => void; - recaptcha: (site_key: string) => void; - recaptcha_challenge: (ack: (token: string) => void) => void; + captcha_challenge: ( + challenge: Challenge, + ack: (token: string | null) => void + ) => void; /* --- subscribe events --- */ @@ -70,6 +74,8 @@ export interface ClientToServerEvents { subscribe: (topic: Subscription) => void; unsubscribe: (topic: Subscription) => void; + + debug: (key: string, ...rest: any[]) => void; } export interface IPosition { diff --git a/packages/server/src/controllers/CaptchaController.ts b/packages/server/src/controllers/CaptchaController.ts index e285b0d..fee0caf 100644 --- a/packages/server/src/controllers/CaptchaController.ts +++ b/packages/server/src/controllers/CaptchaController.ts @@ -1,6 +1,9 @@ -import { createChallenge, verifySolution } from "altcha-lib"; +import { createChallenge, extractParams, verifySolution } from "altcha-lib"; import { Payload } from "altcha-lib/types"; +import { tryParseJSON } from "../utils/tryParseJSON"; +import { Socket, SocketController } from "./SocketController"; + const hmacKey = "secretsecret"; export class CaptchaController { @@ -16,6 +19,10 @@ export class CaptchaController { // initialize class const instance = (CaptchaController.instance = new CaptchaController()); + SocketController.addDebug("test_captcha", (socket) => { + instance.challenge(socket); + }); + return instance; } @@ -45,4 +52,38 @@ export class CaptchaController { verify(payload: string | Payload) { return verifySolution(payload, hmacKey, true); } + + async challenge( + socket: Socket, + ...args: Parameters + ) { + const challenge = await this.create(...args); + + socket.emit("captcha_challenge", challenge, async (token) => { + console.time("captcha res"); + if (token) { + const valid = await this.verify(token); + const params = extractParams(token); + const clientParams = tryParseJSON<{ took: number }>( + Buffer.from(token, "base64").toString() + ); + + if ( + clientParams && + "took" in clientParams && + typeof clientParams.took === "number" + ) { + console.log("client reported taking " + clientParams.took + "ms"); + } + + console.log("captcha payload:", token); + console.log("captcha valid:", valid); + console.log("params", params); + console.log("client params", clientParams); + } else { + console.log("captcha token is null"); + } + console.timeEnd("captcha res"); + }); + } } diff --git a/packages/server/src/controllers/SocketController.ts b/packages/server/src/controllers/SocketController.ts index 482253b..209186b 100644 --- a/packages/server/src/controllers/SocketController.ts +++ b/packages/server/src/controllers/SocketController.ts @@ -64,10 +64,20 @@ export const getClientConfig = (): ClientConfig => { }; }; -type Socket = RawSocket; +export type Socket = RawSocket; + +interface Hooks { + socketConnection: (socket: Socket) => void; +} + +type HooksMap = { [k in keyof Hooks]: Hooks[k][] }; export class SocketController { private static instance: SocketController | undefined; + private static hooks: HooksMap = { + socketConnection: [], + }; + private static debugHooks: { [k: string]: (...rest: any[]) => void } = {}; io: Server; private constructor(server: http.Server, session: ISessionProvider) { @@ -97,6 +107,27 @@ export class SocketController { ); } + static addDebug(key: string, func: (socket: Socket, ...rest: any[]) => void) { + if (process.env.NODE_ENV !== "development") return; + + Logger.debug(`Added debug hook for ${key}`); + this.debugHooks[key] = func; + } + + static hook(hook: Hook, func: Hooks[Hook]) { + this.hooks[hook].push(func); + } + + private static execHook( + hook: Hook, + ...args: Parameters + ) { + for (const func of this.hooks[hook]) { + // @ts-expect-error TS2556 is thrown when not applicable + func(...args); + } + } + static get(): SocketController { if (typeof SocketController.instance === "undefined") { throw new Error("SocketController#get called when not initialized"); @@ -114,10 +145,15 @@ export class SocketController { this.io.emit("config", getClientConfig()); } - async handleConnection(socket: Socket) { - const user = + async getUserFromSocket(socket: Socket) { + return ( socket.request.session.user && - (await User.fromAuthSession(socket.request.session.user)); + (await User.fromAuthSession(socket.request.session.user)) + ); + } + + async handleConnection(socket: Socket) { + const user = await this.getUserFromSocket(socket); Logger.debug( `Socket ${socket.id} connection ` + (user ? "@" + user.sub : "No Auth") ); @@ -163,9 +199,6 @@ export class SocketController { ); } - if (process.env.RECAPTCHA_SITE_KEY) - socket.emit("recaptcha", process.env.RECAPTCHA_SITE_KEY); - socket.emit("config", getClientConfig()); { CanvasController.get() @@ -366,6 +399,14 @@ export class SocketController { socket.on("unsubscribe", (topic) => { socket.leave("sub:" + topic); }); + + if (process.env.NODE_ENV === "development") { + socket.on("debug", (key, ...rest) => { + SocketController.debugHooks[key]?.(socket, ...rest); + }); + } + + SocketController.execHook("socketConnection", socket); } /** diff --git a/packages/server/src/utils/tryParseJSON.ts b/packages/server/src/utils/tryParseJSON.ts new file mode 100644 index 0000000..6a2c0af --- /dev/null +++ b/packages/server/src/utils/tryParseJSON.ts @@ -0,0 +1,7 @@ +export const tryParseJSON = (input: string): T | null => { + try { + return JSON.parse(input); + } catch (_e) { + return null; + } +}; -- GitLab From dec4a8291b290c86c5121999f12580b4b7150ae1 Mon Sep 17 00:00:00 2001 From: Grant Date: Sat, 17 May 2025 23:43:04 -0600 Subject: [PATCH 03/10] fix merge --- packages/server/src/api/client.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/server/src/api/client.ts b/packages/server/src/api/client.ts index 97c71d7..15858ca 100644 --- a/packages/server/src/api/client.ts +++ b/packages/server/src/api/client.ts @@ -25,18 +25,6 @@ app.post("/verify", async (req, res) => { res.json({ valid }); }); -app.get("/test", async (req, res) => { - const challenge = await CaptchaController.get().create(); - - res.json(challenge); -}); - -app.post("/verify", async (req, res) => { - const valid = await CaptchaController.get().verify(req.body.payload); - - res.json({ valid }); -}); - // register auth endpoints app.use(AuthEndpoints); -- GitLab From 3f12d9c0d95c02b7894e5a688703694aad767372 Mon Sep 17 00:00:00 2001 From: Grant Date: Sun, 18 May 2025 12:56:08 -0600 Subject: [PATCH 04/10] captcha working nudge --- .../components/Captcha/CaptchaStatusBar.tsx | 78 +++++++++++++++++++ .../src/components/Toolbar/ToolbarWrapper.tsx | 2 + packages/client/src/lib/captcha.ts | 21 ++++- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 packages/client/src/components/Captcha/CaptchaStatusBar.tsx diff --git a/packages/client/src/components/Captcha/CaptchaStatusBar.tsx b/packages/client/src/components/Captcha/CaptchaStatusBar.tsx new file mode 100644 index 0000000..b620f4a --- /dev/null +++ b/packages/client/src/components/Captcha/CaptchaStatusBar.tsx @@ -0,0 +1,78 @@ +import { motion } from "framer-motion"; +import { Card, CardBody } from "@nextui-org/react"; +import { useEffect, useRef, useState } from "react"; +import { CaptchaService, CaptchaServiceStatus } from "../../lib/captcha"; + +const Timer = () => { + const ref = useRef(null); + + useEffect(() => { + let _timer = 0; + + const timer = setInterval(() => { + _timer++; + let text = (_timer + "").split(""); + text.splice(-1, 0, "."); + if (ref.current) ref.current.innerText = text.join("") + "s"; + }, 100); + + return () => { + clearInterval(timer); + }; + }, []); + + return ; +}; + +export const CaptchaStatusBar = () => { + const [status, setStatus] = useState("IDLE"); + + useEffect(() => { + const service = CaptchaService.get(); + + setStatus(service.status); + + function handleStatus(status: CaptchaServiceStatus) { + setStatus(status); + } + service.on("status", handleStatus); + + return () => { + service.off("status", handleStatus); + }; + }, []); + + useEffect(() => { + if (status === "IDLE") { + } + }, [status]); + + return ( +
+ + + +

Captcha running...

+

+ Using{" "} + + Altcha + {" "} + • {status === "WORK" && } +

+
+
+
+
+ ); +}; diff --git a/packages/client/src/components/Toolbar/ToolbarWrapper.tsx b/packages/client/src/components/Toolbar/ToolbarWrapper.tsx index 3fa0a51..8fc7159 100644 --- a/packages/client/src/components/Toolbar/ToolbarWrapper.tsx +++ b/packages/client/src/components/Toolbar/ToolbarWrapper.tsx @@ -1,5 +1,6 @@ import { useAppContext } from "../../contexts/AppContext"; import { useTemplateContext } from "../../contexts/TemplateContext"; +import { CaptchaStatusBar } from "../Captcha/CaptchaStatusBar"; import { MobileTemplateButtons } from "../Templating/MobileTemplateButtons"; import { CanvasMeta } from "./CanvasMeta"; import { Palette } from "./Palette"; @@ -18,6 +19,7 @@ export const ToolbarWrapper = () => {
+ {showMobileTools && } diff --git a/packages/client/src/lib/captcha.ts b/packages/client/src/lib/captcha.ts index 927d6a7..7f47954 100644 --- a/packages/client/src/lib/captcha.ts +++ b/packages/client/src/lib/captcha.ts @@ -1,8 +1,16 @@ import { solveChallenge } from "altcha-lib"; import { Challenge, Payload, Solution } from "altcha-lib/types"; +import EventEmitter from "eventemitter3"; -export class CaptchaService { +export type CaptchaServiceStatus = "WORK" | "IDLE"; + +interface Events { + status: (status: CaptchaServiceStatus) => void; +} + +export class CaptchaService extends EventEmitter { private static instance: CaptchaService; + private _status: CaptchaServiceStatus = "IDLE"; static get() { if (this.instance) return this.instance; @@ -10,6 +18,15 @@ export class CaptchaService { return (this.instance = new CaptchaService()); } + get status() { + return this._status; + } + + private setStatus(status: CaptchaServiceStatus) { + this._status = status; + this.emit("status", status); + } + private createPayload(data: Challenge, solution: Solution) { return btoa( JSON.stringify({ @@ -24,6 +41,7 @@ export class CaptchaService { } solve(challenge: Challenge) { + this.setStatus("WORK"); console.log("[CaptchaService] Start captcha solve..."); console.time("[CaptchaService] solve"); const response = solveChallenge( @@ -35,6 +53,7 @@ export class CaptchaService { return { promise: response.promise.then(async (data) => { + this.setStatus("IDLE"); console.timeEnd("[CaptchaService] solve"); if (!data) return data; -- GitLab From 8a4045734d267fcf48ad94c3d848566ce2899e69 Mon Sep 17 00:00:00 2001 From: Grant Date: Thu, 19 Jun 2025 17:26:43 -0600 Subject: [PATCH 05/10] fix merge --- .../src/controllers/SocketController.ts | 47 ------------------- packages/server/src/index.main.ts | 2 + 2 files changed, 2 insertions(+), 47 deletions(-) diff --git a/packages/server/src/controllers/SocketController.ts b/packages/server/src/controllers/SocketController.ts index 0cae217..962570e 100644 --- a/packages/server/src/controllers/SocketController.ts +++ b/packages/server/src/controllers/SocketController.ts @@ -1,7 +1,6 @@ import http from "node:http"; import { - ClientConfig, ClientToServerEvents, Pixel, ServerToClientEvents, @@ -10,7 +9,6 @@ import { createAdapter } from "@socket.io/redis-adapter"; import { Redis as IORedis } from "ioredis"; import { Server, Socket as RawSocket } from "socket.io"; -import { SHORT_HASH } from "../const"; import { getLogger } from "../lib/Logger"; import { prisma } from "../lib/prisma"; import { Recaptcha } from "../lib/Recaptcha"; @@ -19,54 +17,9 @@ import { ClientConfigService } from "../services/ClientConfigService"; import { CanvasController } from "./CanvasController"; import { ExpressController, ISessionProvider } from "./ExpressController"; import { LockExists, Redis } from "./RedisController"; -import { Palette } from "../utils/Palette"; const Logger = getLogger("SOCKET"); -/** - * get socket.io server config, generated from environment vars - */ -const getSocketConfig = () => { - // origins that should be permitted - // origins need to be specifically defined if we want to allow CORS credential usage (cookies) - const origins: string[] = []; - - if (process.env.CLIENT_ORIGIN) { - origins.push(process.env.CLIENT_ORIGIN); - } - - if (origins.length === 0) { - return undefined; - } - - return { - cors: { - origin: origins, - credentials: true, - }, - }; -}; - -// this is terrible, another way to get the client config needs to be found -const PIXEL_TIMEOUT_MS = 1000; - -export const getClientConfig = (): ClientConfig => { - return { - version: SHORT_HASH, - pallete: { - colors: Palette.get(), - pixel_cooldown: PIXEL_TIMEOUT_MS, - }, - canvas: CanvasController.get().getCanvasConfig(), - chat: { - enabled: true, - matrix_homeserver: process.env.MATRIX_HOMESERVER, - element_host: process.env.ELEMENT_HOST, - general_alias: process.env.MATRIX_GENERAL_ALIAS, - }, - }; -}; - export type Socket = RawSocket; interface Hooks { diff --git a/packages/server/src/index.main.ts b/packages/server/src/index.main.ts index 332aa37..ae036b8 100644 --- a/packages/server/src/index.main.ts +++ b/packages/server/src/index.main.ts @@ -1,4 +1,5 @@ import { CanvasController } from "./controllers/CanvasController"; +import { CaptchaController } from "./controllers/CaptchaController"; import { ExpressController } from "./controllers/ExpressController"; import { OpenIDController } from "./controllers/OpenIDController"; import { Redis } from "./controllers/RedisController"; @@ -25,6 +26,7 @@ export default () => { await CanvasController.initialize(); ExpressController.initialize(); SocketController.initialize(); + CaptchaController.initialize(); }) ), ]).then(() => { -- GitLab From 987cc6e906bb3f68b2b63b642956efa8ee0bf886 Mon Sep 17 00:00:00 2001 From: Grant Date: Fri, 20 Jun 2025 14:53:58 -0600 Subject: [PATCH 06/10] [wip] mod endpoint to send captcha to user --- package-lock.json | 30 ------ packages/client/package.json | 1 - .../client/src/Moderator/UserModSidebar.tsx | 7 ++ .../client/src/components/Info/Altcha.tsx | 66 ------------- .../src/components/Info/InfoPrivacy.tsx | 10 +- .../src/components/Info/InfoSidebar.tsx | 2 - packages/client/src/lib/network.ts | 1 - packages/server/prisma/schema.prisma | 34 +++++-- packages/server/src/api/mod/users.ts | 31 ++++++ .../src/controllers/CaptchaController.ts | 97 ++++++++++++++----- .../src/controllers/SocketController.ts | 5 + 11 files changed, 153 insertions(+), 131 deletions(-) delete mode 100644 packages/client/src/components/Info/Altcha.tsx diff --git a/package-lock.json b/package-lock.json index 91fb601..92a8d20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,11 +78,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@altcha/crypto": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@altcha/crypto/-/crypto-0.0.1.tgz", - "integrity": "sha512-qZMdnoD3lAyvfSUMNtC2adRi666Pxdcw9zqfMU5qBOaJWqpN9K+eqQGWqeiKDMqL0SF+EytNG4kR/Pr/99GJ6g==" - }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -9718,35 +9713,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/altcha": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/altcha/-/altcha-1.4.4.tgz", - "integrity": "sha512-4ZoRt4xOz4+NnOcnMvW9gEDjGUC6d9k+4xf6RFty6FWPbyNlYE/2BPJaOhP6WS/PliYkXnPjer1Yk/U47VMiqQ==", - "hasInstallScript": true, - "dependencies": { - "@altcha/crypto": "^0.0.1" - }, - "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "4.18.0" - } - }, "node_modules/altcha-lib": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/altcha-lib/-/altcha-lib-1.2.0.tgz", "integrity": "sha512-S5WF8QLNRaM1hvK24XPhOLfu9is2EBCvH7+nv50sM5CaIdUCqQCd0WV/qm/ZZFGTdSoKLuDp+IapZxBLvC+SNg==" }, - "node_modules/altcha/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -19919,7 +19890,6 @@ "@nextui-org/react": "^2.6.11", "@sc07-canvas/lib": "^1.0.0", "@theme-toggles/react": "^4.1.0", - "altcha": "^1.4.4", "altcha-lib": "^1.2.0", "eventemitter3": "^5.0.1", "framer-motion": "^11.3.2", diff --git a/packages/client/package.json b/packages/client/package.json index 027c38b..e9a32a1 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -17,7 +17,6 @@ "@nextui-org/react": "^2.6.11", "@sc07-canvas/lib": "^1.0.0", "@theme-toggles/react": "^4.1.0", - "altcha": "^1.4.4", "altcha-lib": "^1.2.0", "eventemitter3": "^5.0.1", "framer-motion": "^11.3.2", diff --git a/packages/client/src/Moderator/UserModSidebar.tsx b/packages/client/src/Moderator/UserModSidebar.tsx index 6d2035c..aabc97c 100644 --- a/packages/client/src/Moderator/UserModSidebar.tsx +++ b/packages/client/src/Moderator/UserModSidebar.tsx @@ -81,11 +81,18 @@ const Inner = ({ sub }: { sub: string }) => { + + + ); }; +const Captcha = ({ sub }: { sub: string }) => { + return <>; +}; + const Notice = ({ sub }: { sub: string }) => { const [loading, setLoading] = useState(false); const [title, setTitle] = useState(""); diff --git a/packages/client/src/components/Info/Altcha.tsx b/packages/client/src/components/Info/Altcha.tsx deleted file mode 100644 index 3ed2b5f..0000000 --- a/packages/client/src/components/Info/Altcha.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { - useEffect, - useRef, - useState, - forwardRef, - useImperativeHandle, -} from "react"; - -// Importing altcha package will introduce a new element -import "altcha"; - -interface AltchaProps { - onStateChange?: (ev: Event | CustomEvent) => void; -} - -/** - * Altcha widget - * @see https://github.com/altcha-org/altcha-starter-react-ts/blob/46fcfc1146c649a03fb3b4c8bae5bbfc4db668a9/src/Altcha.tsx - */ -const Altcha = forwardRef<{ value: string | null }, AltchaProps>( - ({ onStateChange }, ref) => { - const widgetRef = useRef( - null - ); - const [value, setValue] = useState(null); - - useImperativeHandle(ref, () => { - return { - get value() { - return value; - }, - }; - }, [value]); - - useEffect(() => { - const handleStateChange = (ev: Event | CustomEvent) => { - if ("detail" in ev) { - setValue(ev.detail.payload || null); - onStateChange?.(ev); - } - }; - - const { current } = widgetRef; - - if (current) { - current.addEventListener("statechange", handleStateChange); - return () => - current.removeEventListener("statechange", handleStateChange); - } - }, [onStateChange]); - - /* Configure your `challengeurl` and remove the `test` attribute, see docs: https://altcha.org/docs/website-integration/#using-altcha-widget */ - return ( - - ); - } -); - -export default Altcha; diff --git a/packages/client/src/components/Info/InfoPrivacy.tsx b/packages/client/src/components/Info/InfoPrivacy.tsx index e5e59da..84f7c7c 100644 --- a/packages/client/src/components/Info/InfoPrivacy.tsx +++ b/packages/client/src/components/Info/InfoPrivacy.tsx @@ -1,5 +1,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faShieldHalved } from "@fortawesome/free-solid-svg-icons"; +import { Link } from "@nextui-org/react"; export const InfoPrivacy = () => { return ( @@ -11,8 +12,13 @@ export const InfoPrivacy = () => {
  • - Google Invisible Recaptcha is used to help prevent bots. Google's - privacy policy and terms are available below. + + altcha-lib + {" "} + is used for proof-of-work captcha verification
  • Usernames should not be assumed to be private
diff --git a/packages/client/src/components/Info/InfoSidebar.tsx b/packages/client/src/components/Info/InfoSidebar.tsx index e7f4ca2..35c2120 100644 --- a/packages/client/src/components/Info/InfoSidebar.tsx +++ b/packages/client/src/components/Info/InfoSidebar.tsx @@ -3,7 +3,6 @@ import { InfoText } from "./InfoText"; import { InfoButtons } from "./InfoButtons"; import { SidebarBase } from "../SidebarBase"; import { faInfoCircle } from "@fortawesome/free-solid-svg-icons"; -import Altcha from "./Altcha"; /** * Information sidebar @@ -28,7 +27,6 @@ export const InfoSidebar = () => {
- console.log(e)} />

Build {__COMMIT_HASH__}

diff --git a/packages/client/src/lib/network.ts b/packages/client/src/lib/network.ts index adeb11b..c4f0f47 100644 --- a/packages/client/src/lib/network.ts +++ b/packages/client/src/lib/network.ts @@ -103,7 +103,6 @@ class Network extends EventEmitter { }); this.socket.on("captcha_challenge", (challenge, ack) => { - console.log("captcha_challenge"); CaptchaService.get() .solve(challenge) .promise.then((token) => { diff --git a/packages/server/prisma/schema.prisma b/packages/server/prisma/schema.prisma index fe172f1..16f95e3 100644 --- a/packages/server/prisma/schema.prisma +++ b/packages/server/prisma/schema.prisma @@ -29,12 +29,34 @@ model User { isAdmin Boolean @default(false) isModerator Boolean @default(false) - pixels Pixel[] - FactionMember FactionMember[] - Ban Ban? - AuditLog AuditLog[] - IPAddress IPAddress[] - PixelReport PixelReport[] + pixels Pixel[] + FactionMember FactionMember[] + Ban Ban? + AuditLog AuditLog[] + IPAddress IPAddress[] + PixelReport PixelReport[] + CaptchaChallenge CaptchaChallenge[] +} + +enum CaptchaState { + WAITING + INVALID + PASSED +} + +model CaptchaChallenge { + id Int @id @default(autoincrement()) + userSub String? + socketId String + challenge String // stringified json + responseToken String? + serverMetrics Json + state CaptchaState + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + user User? @relation(fields: [userSub], references: [sub], onDelete: SetNull) } model Instance { diff --git a/packages/server/src/api/mod/users.ts b/packages/server/src/api/mod/users.ts index 7cfca3a..9c7f598 100644 --- a/packages/server/src/api/mod/users.ts +++ b/packages/server/src/api/mod/users.ts @@ -1,5 +1,6 @@ import { Prisma } from "@prisma/client"; +import { CaptchaController } from "../../controllers/CaptchaController"; import { SocketController } from "../../controllers/SocketController"; import { getLogger } from "../../lib/Logger"; import { prisma } from "../../lib/prisma"; @@ -101,6 +102,36 @@ export class UsersEndpoints extends Router { res.json({ success: true }); } + @Router.handler("post", "/:sub/captcha") + async sendCaptcha(req: Router.Request, res: Router.Response) { + let user: User; + + try { + user = await User.fromSub(req.params.sub); + } catch (e) { + if (e instanceof UserNotFound) { + res.status(404).json({ success: false, error: "User not found" }); + } else { + Logger.error( + `/user/${req.params.sub}/ips Error ` + (e as any)?.message + ); + res.status(500).json({ success: false, error: "Internal error" }); + } + return; + } + + const sockets = await SocketController.get().getUserSockets(user.sub); + if (!sockets.length) { + return res.status(400).json({ error: "User is not connected" }); + } + + const Captcha = CaptchaController.get(); + + for (const socket of sockets) { + Captcha.challenge(socket); + } + } + /** * Get all of a user's IP addresses * diff --git a/packages/server/src/controllers/CaptchaController.ts b/packages/server/src/controllers/CaptchaController.ts index fee0caf..e7210fb 100644 --- a/packages/server/src/controllers/CaptchaController.ts +++ b/packages/server/src/controllers/CaptchaController.ts @@ -1,6 +1,7 @@ import { createChallenge, extractParams, verifySolution } from "altcha-lib"; import { Payload } from "altcha-lib/types"; +import { prisma } from "../lib/prisma"; import { tryParseJSON } from "../utils/tryParseJSON"; import { Socket, SocketController } from "./SocketController"; @@ -59,31 +60,81 @@ export class CaptchaController { ) { const challenge = await this.create(...args); + const user = await SocketController.get().getUserFromSocket(socket); + const captchaChallenge = await prisma.captchaChallenge.create({ + data: { + userSub: user?.sub, + socketId: socket.id, + challenge: JSON.stringify(challenge), + state: "WAITING", + serverMetrics: {}, + }, + }); + socket.emit("captcha_challenge", challenge, async (token) => { - console.time("captcha res"); - if (token) { - const valid = await this.verify(token); - const params = extractParams(token); - const clientParams = tryParseJSON<{ took: number }>( - Buffer.from(token, "base64").toString() - ); - - if ( - clientParams && - "took" in clientParams && - typeof clientParams.took === "number" - ) { - console.log("client reported taking " + clientParams.took + "ms"); - } - - console.log("captcha payload:", token); - console.log("captcha valid:", valid); - console.log("params", params); - console.log("client params", clientParams); - } else { - console.log("captcha token is null"); + const start = Date.now(); + + if (!token) { + // token is null + await prisma.captchaChallenge.update({ + where: { + id: captchaChallenge.id, + }, + data: { + state: "INVALID", + serverMetrics: { + verifyTimeMs: Date.now() - start, + }, + }, + }); + return; + } + + const valid = await this.verify(token); + if (!valid) { + // token is invalid + await prisma.captchaChallenge.update({ + where: { + id: captchaChallenge.id, + }, + data: { + state: "INVALID", + serverMetrics: { + verifyTimeMs: Date.now() - start, + }, + }, + }); + return; } - console.timeEnd("captcha res"); + + await prisma.captchaChallenge.update({ + where: { + id: captchaChallenge.id, + }, + data: { + state: "PASSED", + responseToken: token, + serverMetrics: { + verifyTimeMs: Date.now() - start, + }, + }, + }); }); + + return captchaChallenge; + } + + extractData>( + token: string + ): { server: Record; client: T | null } { + const serverParams = extractParams(token); + const clientParams = tryParseJSON( + Buffer.from(token, "base64").toString() + ); + + return { + server: serverParams, + client: clientParams, + }; } } diff --git a/packages/server/src/controllers/SocketController.ts b/packages/server/src/controllers/SocketController.ts index 962570e..8d02345 100644 --- a/packages/server/src/controllers/SocketController.ts +++ b/packages/server/src/controllers/SocketController.ts @@ -392,4 +392,9 @@ export class SocketController { this.io.to("sub:heatmap").emit("heatmap", message); }); } + + async getUserSockets(userSub: string) { + const sockets = await this.io.in(`user:${userSub}`).fetchSockets(); + return sockets; + } } -- GitLab From 589a884f2ad29f87043c5688eae340e10ea2971a Mon Sep 17 00:00:00 2001 From: Grant Date: Sat, 21 Jun 2025 12:34:55 -0600 Subject: [PATCH 07/10] add manual sending of captchas to sockets --- package-lock.json | 17 +- packages/client/src/Moderator/Moderator.tsx | 9 ++ .../client/src/Moderator/ModeratorModule.ts | 47 ++++++ .../client/src/Moderator/UserModSidebar.tsx | 151 +++++++++++++++++- packages/lib/src/debug.ts | 4 + packages/lib/src/net.ts | 25 +++ packages/server/package.json | 1 + .../migration.sql | 20 +++ packages/server/src/api/mod/users.ts | 51 +++++- packages/server/src/api/openapi.yml | 52 ++++++ .../src/controllers/CaptchaController.ts | 43 ++++- .../src/controllers/SocketController.ts | 75 ++++++++- 12 files changed, 468 insertions(+), 27 deletions(-) create mode 100644 packages/client/src/Moderator/ModeratorModule.ts create mode 100644 packages/server/prisma/migrations/20250621000533_add_captcha_challenge/migration.sql diff --git a/package-lock.json b/package-lock.json index 92a8d20..b60e267 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8941,11 +8941,12 @@ } }, "node_modules/@types/node": { - "version": "20.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.7.tgz", - "integrity": "sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==", + "version": "24.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz", + "integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.8.0" } }, "node_modules/@types/parse-json": { @@ -19049,9 +19050,10 @@ "dev": true }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", @@ -19991,6 +19993,7 @@ "@types/express": "^5.0.0", "@types/express-session": "^1.18.1", "@types/jest": "^29.5.14", + "@types/node": "^24.0.3", "@types/redis-mock": "^0.17.3", "@types/supertest": "^6.0.2", "@types/uuid": "^10.0.0", diff --git a/packages/client/src/Moderator/Moderator.tsx b/packages/client/src/Moderator/Moderator.tsx index 31f5275..c0495d7 100644 --- a/packages/client/src/Moderator/Moderator.tsx +++ b/packages/client/src/Moderator/Moderator.tsx @@ -3,6 +3,9 @@ import { UserModSidebar } from "./UserModSidebar"; import { KeybindManager } from "../lib/keybinds"; import { ModSidebar } from "./ModSidebar"; import { useHasRole } from "../hooks/useHasRole"; +import { ModeratorModule } from "./ModeratorModule"; + +ModeratorModule.get(); // initialize const context = createContext<{ state: IModeratorContext; @@ -103,6 +106,12 @@ export const ModeratorContext = ({ children }: React.PropsWithChildren) => { ); useEffect(() => { + if (isMod) { + ModeratorModule.get().connect(); + } else { + ModeratorModule.get().socket.disconnect(); + } + const handleKeybind = () => { if (!isMod) { console.warn("TOGGLE_MOD_MENU canceled, user is not a moderator"); diff --git a/packages/client/src/Moderator/ModeratorModule.ts b/packages/client/src/Moderator/ModeratorModule.ts new file mode 100644 index 0000000..b99032b --- /dev/null +++ b/packages/client/src/Moderator/ModeratorModule.ts @@ -0,0 +1,47 @@ +import { Debug } from "@sc07-canvas/lib/src/debug"; +import { + ModClientToServerEvents, + ModServerToClientEvents, +} from "@sc07-canvas/lib/src/net"; +import { io, Socket } from "socket.io-client"; + +export class ModeratorModule { + private static instance: ModeratorModule; + socket: Socket = io( + "/mod", + { autoConnect: false } + ); + + private constructor() { + Debug.controllers.set("Moderator", this); + + this.socket.on("connect", () => { + console.log("connected to mod socket"); + }); + + this.socket.on("connect_error", (err) => { + if (this.socket.active) { + console.log("disconnected temporarily"); + } else { + console.log("failed to connect", err); + } + }); + + this.socket.on("disconnect", () => { + console.log("disconnected"); + }); + + this.socket.on("captcha", (...data) => { + console.log(...data); + }); + } + + static get() { + if (!this.instance) this.instance = new ModeratorModule(); + return this.instance; + } + + connect() { + this.socket.connect(); + } +} diff --git a/packages/client/src/Moderator/UserModSidebar.tsx b/packages/client/src/Moderator/UserModSidebar.tsx index aabc97c..f272eb7 100644 --- a/packages/client/src/Moderator/UserModSidebar.tsx +++ b/packages/client/src/Moderator/UserModSidebar.tsx @@ -16,6 +16,7 @@ import { import { CalendarDateTime, parseDateTime } from "@internationalized/date"; import { toast } from "react-toastify"; import { EError } from "@sc07-canvas/lib"; +import { ModeratorModule } from "./ModeratorModule"; export const UserModSidebar = () => { const { state, dispatch } = useModerator(); @@ -81,7 +82,7 @@ const Inner = ({ sub }: { sub: string }) => { - + @@ -89,8 +90,154 @@ const Inner = ({ sub }: { sub: string }) => { ); }; +type CaptchaStatus = "WAITING" | "INVALID" | "PASSED"; +const CaptchaStatusColors: { + [k in CaptchaStatus]: + | "default" + | "success" + | "warning" + | "primary" + | "secondary" + | "danger"; +} = { + WAITING: "default", + INVALID: "danger", + PASSED: "success", +}; + const Captcha = ({ sub }: { sub: string }) => { - return <>; + const [status, setStatus] = useState<{ + [k: string]: CaptchaStatus; + }>({}); + const [socketStatus, setSocketStatus] = useState<{ + [k: string]: "NEW" | "GONE"; + }>({}); + + const sockets = useQuery("/mod/user/{sub}/sockets", { + params: { + path: { + sub, + }, + }, + }); + + const sendCaptcha = useCallback( + (socketId?: string) => { + oapi.POST( + socketId + ? "/mod/user/{sub}/captcha/{socket}" + : "/mod/user/{sub}/captcha", + { + params: { + path: { + sub, + socket: socketId, + }, + }, + } + ); + }, + [sub] + ); + + useEffect(() => { + console.log("captcha & sockets loaded"); + + setStatus({}); + const handleStatus = ( + id: number, + userId: string, + socketId: string, + status: CaptchaStatus + ) => { + setStatus((v) => ({ + ...v, + [socketId]: status, + })); + }; + + const handleConnect = (id: string) => { + setSocketStatus((v) => ({ + ...v, + [id]: "NEW", + })); + }; + + const handleDisconnect = (id: string) => { + setSocketStatus((v) => ({ + ...v, + [id]: "GONE", + })); + }; + + const mod = ModeratorModule.get(); + + mod.socket.emit("join", "captcha"); + mod.socket.emit("join", "user:" + sub); + mod.socket.on("captcha", handleStatus); + mod.socket.on("socketConnect", handleConnect); + mod.socket.on("socketDisconnect", handleDisconnect); + + return () => { + console.log("captcha & sockets unloaded"); + mod.socket.emit("leave", "captcha"); + mod.socket.emit("leave", "user:" + sub); + mod.socket.off("captcha", handleStatus); + mod.socket.off("socketConnect", handleConnect); + mod.socket.off("socketDisconnect", handleDisconnect); + }; + }, [sub]); + + return ( + <> + + + {sockets.data?.sockets.map((socket) => ( + + {socket.id} + + + } + > + + + )) || null} + + + ); +}; + +const SocketChip = ({ status }: { status?: "NEW" | "GONE" }) => { + switch (status) { + case "NEW": + return ( + + New + + ); + case "GONE": + return ( + + Gone + + ); + default: + <>; + } +}; + +const CaptchaChip = ({ status }: { status?: CaptchaStatus }) => { + if (!status) return <>; + return ( + + {status} + + ); }; const Notice = ({ sub }: { sub: string }) => { diff --git a/packages/lib/src/debug.ts b/packages/lib/src/debug.ts index 224aeab..1f8967e 100644 --- a/packages/lib/src/debug.ts +++ b/packages/lib/src/debug.ts @@ -102,6 +102,10 @@ class Debugcl extends EventEmitter { readonly flags = new FlagManager(); _getRenderer: any; _network: any; + /** + * name -> instance + */ + readonly controllers: Map = new Map(); constructor() { super(); diff --git a/packages/lib/src/net.ts b/packages/lib/src/net.ts index e94c217..e0ee55c 100644 --- a/packages/lib/src/net.ts +++ b/packages/lib/src/net.ts @@ -78,6 +78,31 @@ export interface ClientToServerEvents { debug: (key: string, ...rest: any[]) => void; } +export interface ModServerToClientEvents { + /** + * @room captcha + */ + captcha: ( + id: number, + userId: string, + socketId: string, + result: "WAITING" | "INVALID" | "PASSED" + ) => void; + + /** + * @room user:{sub} + */ + socketConnect: (socketId: string) => void; + socketDisconnect: (socketId: string) => void; + pixelPlace: (socketId: string, x: number, y: number) => void; + pixelUndo: (socketId: string, x: number, y: number) => void; +} + +export interface ModClientToServerEvents { + join: (room: string) => void; + leave: (room: string) => void; +} + export interface IPosition { x: number; y: number; diff --git a/packages/server/package.json b/packages/server/package.json index b31fc87..de2ab82 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -27,6 +27,7 @@ "@types/express": "^5.0.0", "@types/express-session": "^1.18.1", "@types/jest": "^29.5.14", + "@types/node": "^24.0.3", "@types/redis-mock": "^0.17.3", "@types/supertest": "^6.0.2", "@types/uuid": "^10.0.0", diff --git a/packages/server/prisma/migrations/20250621000533_add_captcha_challenge/migration.sql b/packages/server/prisma/migrations/20250621000533_add_captcha_challenge/migration.sql new file mode 100644 index 0000000..17d4106 --- /dev/null +++ b/packages/server/prisma/migrations/20250621000533_add_captcha_challenge/migration.sql @@ -0,0 +1,20 @@ +-- CreateEnum +CREATE TYPE "CaptchaState" AS ENUM ('WAITING', 'INVALID', 'PASSED'); + +-- CreateTable +CREATE TABLE "CaptchaChallenge" ( + "id" SERIAL NOT NULL, + "userSub" TEXT, + "socketId" TEXT NOT NULL, + "challenge" TEXT NOT NULL, + "responseToken" TEXT, + "serverMetrics" JSONB NOT NULL, + "state" "CaptchaState" NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CaptchaChallenge_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "CaptchaChallenge" ADD CONSTRAINT "CaptchaChallenge_userSub_fkey" FOREIGN KEY ("userSub") REFERENCES "User"("sub") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/server/src/api/mod/users.ts b/packages/server/src/api/mod/users.ts index 9c7f598..c4e96bc 100644 --- a/packages/server/src/api/mod/users.ts +++ b/packages/server/src/api/mod/users.ts @@ -102,7 +102,32 @@ export class UsersEndpoints extends Router { res.json({ success: true }); } - @Router.handler("post", "/:sub/captcha") + @Router.handler("get", "/:sub/sockets") + async getSockets(req: Router.Request, res: Router.Response) { + let user: User; + + try { + user = await User.fromSub(req.params.sub); + } catch (e) { + if (e instanceof UserNotFound) { + res.status(404).json({ success: false, error: "User not found" }); + } else { + Logger.error( + `/user/${req.params.sub}/sockets Error ` + (e as any)?.message + ); + res.status(500).json({ success: false, error: "Internal error" }); + } + return; + } + + const sockets = await SocketController.get().getUserSockets(user.sub); + + res.json({ + sockets: sockets.map((s) => ({ id: s.id })), + }); + } + + @Router.handler("post", "/:sub/captcha/:socket?") async sendCaptcha(req: Router.Request, res: Router.Response) { let user: User; @@ -113,23 +138,37 @@ export class UsersEndpoints extends Router { res.status(404).json({ success: false, error: "User not found" }); } else { Logger.error( - `/user/${req.params.sub}/ips Error ` + (e as any)?.message + `/user/${req.params.sub}/captcha Error ` + (e as any)?.message ); res.status(500).json({ success: false, error: "Internal error" }); } return; } + const Captcha = CaptchaController.get(); + + if (typeof req.params.socket === "string") { + const sockets = await SocketController.get() + .io.in(req.params.socket) + .fetchSockets(); + res.json({ + challenges: await Promise.allSettled( + sockets.map((socket) => Captcha.challenge(socket)) + ), + }); + return; + } + const sockets = await SocketController.get().getUserSockets(user.sub); if (!sockets.length) { return res.status(400).json({ error: "User is not connected" }); } - const Captcha = CaptchaController.get(); + const challenges = await Promise.allSettled( + sockets.map((socket) => Captcha.challenge(socket)) + ); - for (const socket of sockets) { - Captcha.challenge(socket); - } + res.json({ challenges }); } /** diff --git a/packages/server/src/api/openapi.yml b/packages/server/src/api/openapi.yml index 95dd776..2bdd138 100644 --- a/packages/server/src/api/openapi.yml +++ b/packages/server/src/api/openapi.yml @@ -165,6 +165,58 @@ paths: description: Query all users /mod/user/@all/notice: description: Send notice to all users + /mod/user/{sub}/sockets: + parameters: + - $ref: "#/components/parameters/UserSub" + get: + summary: Get all sockets owned by a user + responses: + 200: + description: Successful + content: + application/json: + schema: + type: object + required: + - sockets + properties: + sockets: + type: array + items: + type: object + required: + - id + properties: + id: + type: string + /mod/user/{sub}/captcha: + parameters: + - $ref: "#/components/parameters/UserSub" + post: + summary: Send captcha to user + responses: + 200: + description: Success + content: + application/json: + schema: + type: object + /mod/user/{sub}/captcha/{socket}: + parameters: + - $ref: "#/components/parameters/UserSub" + - in: path + name: socket + schema: + type: string + post: + summary: Send captcha to user + responses: + 200: + description: Success + content: + application/json: + schema: + type: object /mod/user/{sub}/ips: description: Get all of user's IP addresses parameters: diff --git a/packages/server/src/controllers/CaptchaController.ts b/packages/server/src/controllers/CaptchaController.ts index e7210fb..98f8332 100644 --- a/packages/server/src/controllers/CaptchaController.ts +++ b/packages/server/src/controllers/CaptchaController.ts @@ -1,16 +1,35 @@ +import { $Enums } from "@prisma/client"; import { createChallenge, extractParams, verifySolution } from "altcha-lib"; import { Payload } from "altcha-lib/types"; +import EventEmitter from "events"; import { prisma } from "../lib/prisma"; import { tryParseJSON } from "../utils/tryParseJSON"; -import { Socket, SocketController } from "./SocketController"; +import { RemoteSocket, Socket, SocketController } from "./SocketController"; const hmacKey = "secretsecret"; -export class CaptchaController { +interface Events { + solve: [ + id: number, + userId: string, + socketId: string, + state: $Enums.CaptchaState, + ]; +} + +export class CaptchaController extends EventEmitter { private static instance: CaptchaController | undefined; - private constructor() {} + private constructor() { + super(); + + this.on("solve", (...data) => { + SocketController.get() + .MOD_NS.to("captcha") + .emit("captcha", ...data); + }); + } static initialize() { if (typeof CaptchaController.instance !== "undefined") { @@ -55,7 +74,7 @@ export class CaptchaController { } async challenge( - socket: Socket, + socket: Socket | RemoteSocket, ...args: Parameters ) { const challenge = await this.create(...args); @@ -70,6 +89,7 @@ export class CaptchaController { serverMetrics: {}, }, }); + this.emit("solve", captchaChallenge.id, user!.sub, socket.id, "WAITING"); socket.emit("captcha_challenge", challenge, async (token) => { const start = Date.now(); @@ -87,6 +107,13 @@ export class CaptchaController { }, }, }); + this.emit( + "solve", + captchaChallenge.id, + user!.sub, + socket.id, + "INVALID" + ); return; } @@ -104,6 +131,13 @@ export class CaptchaController { }, }, }); + this.emit( + "solve", + captchaChallenge.id, + user!.sub, + socket.id, + "INVALID" + ); return; } @@ -119,6 +153,7 @@ export class CaptchaController { }, }, }); + this.emit("solve", captchaChallenge.id, user!.sub, socket.id, "PASSED"); }); return captchaChallenge; diff --git a/packages/server/src/controllers/SocketController.ts b/packages/server/src/controllers/SocketController.ts index 8d02345..ebb4287 100644 --- a/packages/server/src/controllers/SocketController.ts +++ b/packages/server/src/controllers/SocketController.ts @@ -2,12 +2,19 @@ import http from "node:http"; import { ClientToServerEvents, + ModClientToServerEvents, + ModServerToClientEvents, Pixel, ServerToClientEvents, } from "@sc07-canvas/lib/src/net"; import { createAdapter } from "@socket.io/redis-adapter"; import { Redis as IORedis } from "ioredis"; -import { Server, Socket as RawSocket } from "socket.io"; +import { + Namespace, + RemoteSocket as RawRemoteSocket, + Server, + Socket as RawSocket, +} from "socket.io"; import { getLogger } from "../lib/Logger"; import { prisma } from "../lib/prisma"; @@ -21,6 +28,7 @@ import { LockExists, Redis } from "./RedisController"; const Logger = getLogger("SOCKET"); export type Socket = RawSocket; +export type RemoteSocket = RawRemoteSocket; interface Hooks { socketConnection: (socket: Socket) => void; @@ -35,6 +43,7 @@ export class SocketController { }; private static debugHooks: { [k: string]: (...rest: any[]) => void } = {}; io: Server; + MOD_NS: Namespace; private constructor(server?: http.Server, session?: ISessionProvider) { const pubClient = new IORedis(process.env.REDIS_HOST); @@ -49,8 +58,29 @@ export class SocketController { this.io.engine.use(session); this.io.on("connection", this.handleConnection.bind(this)); + + this.MOD_NS = this.io.of("/mod"); + this.MOD_NS.use(async (socket, next) => { + const user = await this.getUserFromSocket(socket, true); + if (!user || !user.isModerator) { + next(new Error("Not Permitted")); + return; + } + + next(); + }); + this.MOD_NS.on("connection", (socket) => { + socket.on("join", (room) => { + socket.join(room); + }); + + socket.on("leave", (room) => { + socket.leave(room); + }); + }); } else { this.io = new Server({ adapter: createAdapter(pubClient, subClient) }); + this.MOD_NS = this.io.of("/mod"); } } @@ -116,15 +146,27 @@ export class SocketController { this.io.emit("config", ClientConfigService.getConfig()); } - async getUserFromSocket(socket: Socket) { - return ( - socket.request.session.user && - (await User.fromAuthSession(socket.request.session.user)) - ); + async getUserFromSocket(socket: Socket, raw: true): Promise; + async getUserFromSocket( + socket: Socket | RemoteSocket, + raw?: false + ): Promise; + async getUserFromSocket(socket: Socket | RemoteSocket, raw?: boolean) { + if (raw && "request" in socket) { + return ( + socket.request.session.user && + (await User.fromAuthSession(socket.request.session.user)) + ); + } + + const redis = await Redis.getClient(); + const uid = await redis.get(Redis.key("socketToSub", socket.id)); + if (!uid) return undefined; + return await User.fromSub(uid); } async handleConnection(socket: Socket) { - const user = await this.getUserFromSocket(socket); + const user = await this.getUserFromSocket(socket, true); Logger.debug( `Socket ${socket.id} connection ` + (user ? "@" + user.sub : "No Auth") ); @@ -153,6 +195,7 @@ export class SocketController { } if (user) { + this.MOD_NS.to(`user:${user.sub}`).emit("socketConnect", socket.id); socket.emit("availablePixels", user.pixelStack); socket.emit("pixelLastPlaced", user.lastTimeGainStarted.getTime()); @@ -187,6 +230,10 @@ export class SocketController { socket.on("disconnect", () => { Logger.debug(`Socket ${socket.id} disconnected`); + if (user) { + this.MOD_NS.to(`user:${user.sub}`).emit("socketDisconnect", socket.id); + } + Redis.getClient().then((redis) => { if (user) redis.del(Redis.key("socketToSub", socket.id)); }); @@ -287,6 +334,12 @@ export class SocketController { data: newPixel, }); socket.broadcast.emit("pixel", newPixel); + this.MOD_NS.to(`user:${user.sub}`).emit( + "pixelPlace", + socket.id, + newPixel.x, + newPixel.y + ); }); } catch (e) { if (e instanceof LockExists) { @@ -356,6 +409,12 @@ export class SocketController { await CanvasController.get().refreshPixel(pixel.x, pixel.y); ack({ success: true, data: {} }); + this.MOD_NS.to(`user:${user.sub}`).emit( + "pixelUndo", + socket.id, + pixel.x, + pixel.y + ); }); socket.on("subscribe", (topic) => { @@ -393,7 +452,7 @@ export class SocketController { }); } - async getUserSockets(userSub: string) { + async getUserSockets(userSub: string): Promise { const sockets = await this.io.in(`user:${userSub}`).fetchSockets(); return sockets; } -- GitLab From 3d9429023fde4d5f92125f4bede4cc4b6538b957 Mon Sep 17 00:00:00 2001 From: Grant Date: Sat, 21 Jun 2025 13:16:36 -0600 Subject: [PATCH 08/10] remove grecaptcha --- package-lock.json | 16 --- packages/client/package.json | 2 - .../src/components/Info/InfoSidebar.tsx | 1 - packages/client/src/lib/recaptcha.ts | 32 ----- .../src/controllers/CaptchaController.ts | 7 ++ .../src/controllers/SocketController.ts | 4 +- packages/server/src/lib/Recaptcha.ts | 109 ------------------ packages/server/src/types.ts | 6 - 8 files changed, 9 insertions(+), 168 deletions(-) delete mode 100644 packages/client/src/lib/recaptcha.ts delete mode 100644 packages/server/src/lib/Recaptcha.ts diff --git a/package-lock.json b/package-lock.json index b60e267..cef9c39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8835,12 +8835,6 @@ "@types/node": "*" } }, - "node_modules/@types/grecaptcha": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/grecaptcha/-/grecaptcha-3.0.9.tgz", - "integrity": "sha512-fFxMtjAvXXMYTzDFK5NpcVB7WHnrHVLl00QzEGpuFxSAC789io6M+vjcn+g5FTEamIJtJr/IHkCDsqvJxeWDyw==", - "dev": true - }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -13604,14 +13598,6 @@ "node": ">=10.17.0" } }, - "node_modules/i": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", - "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", - "engines": { - "node": ">=0.4" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -19895,7 +19881,6 @@ "altcha-lib": "^1.2.0", "eventemitter3": "^5.0.1", "framer-motion": "^11.3.2", - "i": "^0.3.7", "lodash.throttle": "^4.1.1", "prop-types": "^15.8.1", "react-zoom-pan-pinch": "^3.4.1", @@ -19904,7 +19889,6 @@ "vite-plugin-banner": "^0.8.1" }, "devDependencies": { - "@types/grecaptcha": "^3.0.9", "@types/lodash.throttle": "^4.1.9", "@types/socket.io-client": "^3.0.0", "eslint-plugin-react": "^7.33.2", diff --git a/packages/client/package.json b/packages/client/package.json index e9a32a1..401d096 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -20,7 +20,6 @@ "altcha-lib": "^1.2.0", "eventemitter3": "^5.0.1", "framer-motion": "^11.3.2", - "i": "^0.3.7", "lodash.throttle": "^4.1.1", "prop-types": "^15.8.1", "react-zoom-pan-pinch": "^3.4.1", @@ -29,7 +28,6 @@ "vite-plugin-banner": "^0.8.1" }, "devDependencies": { - "@types/grecaptcha": "^3.0.9", "@types/lodash.throttle": "^4.1.9", "@types/socket.io-client": "^3.0.0", "eslint-plugin-react": "^7.33.2", diff --git a/packages/client/src/components/Info/InfoSidebar.tsx b/packages/client/src/components/Info/InfoSidebar.tsx index 35c2120..1989b29 100644 --- a/packages/client/src/components/Info/InfoSidebar.tsx +++ b/packages/client/src/components/Info/InfoSidebar.tsx @@ -30,7 +30,6 @@ export const InfoSidebar = () => {

Build {__COMMIT_HASH__}

-

© 2023 - {__BUILD_YEAR__}{" "} { - grecaptcha.ready(() => { - grecaptcha.render("grecaptcha-badge", { - sitekey: site_key, - badge: "inline", - size: "invisible", - }); - - console.log("Google Recaptcha Loaded!"); - }); - }; - } - - executeChallenge(ack: (token: string) => void) { - console.log("[Recaptcha] Received challenge request..."); - grecaptcha.execute().then((token) => { - console.log("[Recaptcha] Sending challenge token back"); - ack(token as any); - }); - } -} - -export const Recaptcha = new Recaptcha_(); diff --git a/packages/server/src/controllers/CaptchaController.ts b/packages/server/src/controllers/CaptchaController.ts index 98f8332..a170c82 100644 --- a/packages/server/src/controllers/CaptchaController.ts +++ b/packages/server/src/controllers/CaptchaController.ts @@ -172,4 +172,11 @@ export class CaptchaController extends EventEmitter { client: clientParams, }; } + + /** + * Roll a dice to send a captcha to a socket + * + * @stub + */ + maybeChallenge(_socket: Socket) {} } diff --git a/packages/server/src/controllers/SocketController.ts b/packages/server/src/controllers/SocketController.ts index ebb4287..dc2846e 100644 --- a/packages/server/src/controllers/SocketController.ts +++ b/packages/server/src/controllers/SocketController.ts @@ -18,10 +18,10 @@ import { import { getLogger } from "../lib/Logger"; import { prisma } from "../lib/prisma"; -import { Recaptcha } from "../lib/Recaptcha"; import { User } from "../models/User"; import { ClientConfigService } from "../services/ClientConfigService"; import { CanvasController } from "./CanvasController"; +import { CaptchaController } from "./CaptchaController"; import { ExpressController, ISessionProvider } from "./ExpressController"; import { LockExists, Redis } from "./RedisController"; @@ -309,7 +309,7 @@ export class SocketController { return; } - Recaptcha.maybeChallenge(socket); + CaptchaController.get().maybeChallenge(socket); await user.modifyStack(-1); await CanvasController.get().setPixel( diff --git a/packages/server/src/lib/Recaptcha.ts b/packages/server/src/lib/Recaptcha.ts deleted file mode 100644 index 1b29302..0000000 --- a/packages/server/src/lib/Recaptcha.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { - ClientToServerEvents, - ServerToClientEvents, -} from "@sc07-canvas/lib/src/net"; -import { Socket } from "socket.io"; - -import { User } from "../models/User"; -import { getLogger } from "./Logger"; - -const Logger = getLogger("RECAPTCHA"); - -class Recaptcha_ { - disabled = false; - chance: number | null = null; - - constructor() { - this.disabled = - !process.env.RECAPTCHA_SITE_KEY || - !process.env.RECAPTCHA_SECRET_KEY || - !process.env.RECAPTCHA_PIXEL_CHANCE; - - if (!process.env.RECAPTCHA_PIXEL_CHANCE) { - Logger.warn("No RECAPTCHA_PIXEL_CHANCE set, captchas will not be sent!"); - } else { - this.chance = parseFloat(process.env.RECAPTCHA_PIXEL_CHANCE); - - if (this.chance > 1 || this.chance < 0) { - this.chance = null; - this.disabled = true; - Logger.warn("RECAPTCHA_PIXEL_CHANCE is not within (0 - ): boolean { - if (this.disabled || !this.chance) return false; - - if (Math.random() > this.chance) { - socket.emitWithAck("recaptcha_challenge").then((token) => { - this.verifyToken(token).then(async (data) => { - if (!data.success) { - this.notifyStaffOfError(data).then(() => {}); - } else { - // if (data.score < 0.5 || true) { - // try { - // const user = (await User.fromAuthSession( - // socket.request.session.user! - // ))!; - // this.notifyStaff(user, data.score).then(() => {}); - // } catch (e) {} - // } - } - }); - }); - return true; - } - - return false; - } - - async verifyToken( - token: string - ): Promise< - | { success: true; challenge_ts: string; hostname: string; score: number } - | { success: false; "error-codes": string[] } - > { - return await fetch( - `https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET_KEY!}&response=${token}`, - { - method: "POST", - } - ).then((a) => a.json()); - } - - async notifyStaff(user: User, score: number) { - if (!process.env.DISCORD_WEBHOOK) return; - - return await fetch(process.env.DISCORD_WEBHOOK, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - content: `User ${user.sub} got a low score ${score}`, - }), - }); - } - - async notifyStaffOfError(obj: any) { - if (!process.env.DISCORD_WEBHOOK) return; - - return await fetch(process.env.DISCORD_WEBHOOK, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - content: - "Error while verifying captcha\n```\n" + - JSON.stringify(obj, null, 2) + - "\n```", - }), - }); - } -} - -export const Recaptcha = new Recaptcha_(); diff --git a/packages/server/src/types.ts b/packages/server/src/types.ts index e79e894..6237ad1 100644 --- a/packages/server/src/types.ts +++ b/packages/server/src/types.ts @@ -74,12 +74,6 @@ declare global { PIXEL_LOG_PATH?: string; - RECAPTCHA_SITE_KEY?: string; - RECAPTCHA_SECRET_KEY?: string; - RECAPTCHA_PIXEL_CHANCE?: string; - - DISCORD_WEBHOOK?: string; - CACHE_WORKERS?: string; SENTRY_DSN?: string; -- GitLab From 22b23b87223a41e3e1d3f226b36de349172f9ba5 Mon Sep 17 00:00:00 2001 From: Grant Date: Sat, 21 Jun 2025 17:40:23 -0600 Subject: [PATCH 09/10] implement captcha settings --- .../src/controllers/CanvasController.ts | 6 +- .../src/controllers/CaptchaController.ts | 30 +++- packages/server/src/index.main.ts | 18 ++- packages/server/src/index.worker.ts | 10 +- packages/server/src/lib/Logger.ts | 1 + packages/server/src/lib/Secrets.ts | 53 +++++++ packages/server/src/lib/Settings.ts | 146 ------------------ packages/server/src/settings/Settings.ts | 85 ++++++++++ packages/server/src/settings/canvas/frozen.ts | 9 ++ packages/server/src/settings/canvas/size.ts | 9 ++ .../server/src/settings/captcha/algorithm.ts | 9 ++ .../server/src/settings/captcha/enable.ts | 9 ++ .../server/src/settings/captcha/maxnumber.ts | 9 ++ packages/server/src/settings/setting.class.ts | 79 ++++++++++ packages/server/src/tools/init_settings.ts | 45 ------ packages/server/src/types.ts | 3 + .../server/src/utils/validate_environment.ts | 3 + 17 files changed, 312 insertions(+), 212 deletions(-) create mode 100644 packages/server/src/lib/Secrets.ts delete mode 100644 packages/server/src/lib/Settings.ts create mode 100644 packages/server/src/settings/Settings.ts create mode 100644 packages/server/src/settings/canvas/frozen.ts create mode 100644 packages/server/src/settings/canvas/size.ts create mode 100644 packages/server/src/settings/captcha/algorithm.ts create mode 100644 packages/server/src/settings/captcha/enable.ts create mode 100644 packages/server/src/settings/captcha/maxnumber.ts create mode 100644 packages/server/src/settings/setting.class.ts delete mode 100644 packages/server/src/tools/init_settings.ts diff --git a/packages/server/src/controllers/CanvasController.ts b/packages/server/src/controllers/CanvasController.ts index 6df2e26..b8b7030 100644 --- a/packages/server/src/controllers/CanvasController.ts +++ b/packages/server/src/controllers/CanvasController.ts @@ -8,8 +8,8 @@ import { Socket } from "socket.io"; import { getLogger } from "../lib/Logger"; import { prisma } from "../lib/prisma"; -import { Settings } from "../lib/Settings"; import { ClientConfigService } from "../services/ClientConfigService"; +import { Settings } from "../settings/Settings"; import { callCacheWorker, getCacheWorkerIdForCoords } from "../workers/worker"; import { Redis } from "./RedisController"; import { SocketController } from "./SocketController"; @@ -44,10 +44,10 @@ export class CanvasController { const instance = (CanvasController.instance = new CanvasController()); // set values - const canvasSize = Settings.quickGet("canvas.size", true, false); + const canvasSize = Settings.get("canvas.size"); instance.canvasSize = [canvasSize.width, canvasSize.height]; - const canvasFrozen = Settings.quickGet("canvas.frozen", true, false); + const canvasFrozen = Settings.get("canvas.frozen"); instance.isFrozen = canvasFrozen; // run sideeffects diff --git a/packages/server/src/controllers/CaptchaController.ts b/packages/server/src/controllers/CaptchaController.ts index a170c82..b3da1a0 100644 --- a/packages/server/src/controllers/CaptchaController.ts +++ b/packages/server/src/controllers/CaptchaController.ts @@ -4,11 +4,11 @@ import { Payload } from "altcha-lib/types"; import EventEmitter from "events"; import { prisma } from "../lib/prisma"; +import { Secrets } from "../lib/Secrets"; +import { Settings } from "../settings/Settings"; import { tryParseJSON } from "../utils/tryParseJSON"; import { RemoteSocket, Socket, SocketController } from "./SocketController"; -const hmacKey = "secretsecret"; - interface Events { solve: [ id: number, @@ -18,8 +18,11 @@ interface Events { ]; } +// TODO: implement random checks (see #maybeChallenge) + export class CaptchaController extends EventEmitter { private static instance: CaptchaController | undefined; + private static readonly Secrets = new Secrets(["CAPTCHA_HMAC_SECRET"]); private constructor() { super(); @@ -31,6 +34,10 @@ export class CaptchaController extends EventEmitter { }); } + static validateEnvironment() { + this.Secrets.load(); + } + static initialize() { if (typeof CaptchaController.instance !== "undefined") { throw new Error("CaptchaController#initialize when initialized"); @@ -54,6 +61,10 @@ export class CaptchaController extends EventEmitter { return CaptchaController.instance!; } + private get Secrets() { + return CaptchaController.Secrets; + } + create({ params, expires, @@ -62,15 +73,20 @@ export class CaptchaController extends EventEmitter { expires?: Date; } = {}) { return createChallenge({ - hmacKey, - algorithm: "SHA-512", + hmacKey: this.Secrets.get("CAPTCHA_HMAC_SECRET"), + algorithm: Settings.get("captcha.algorithm"), + maxNumber: Settings.get("captcha.maxnumber"), params, expires, }); } verify(payload: string | Payload) { - return verifySolution(payload, hmacKey, true); + return verifySolution( + payload, + this.Secrets.get("CAPTCHA_HMAC_SECRET"), + true + ); } async challenge( @@ -178,5 +194,7 @@ export class CaptchaController extends EventEmitter { * * @stub */ - maybeChallenge(_socket: Socket) {} + maybeChallenge(_socket: Socket) { + // TODO: use Settings captcha.enable to determine if random challenges should be issued + } } diff --git a/packages/server/src/index.main.ts b/packages/server/src/index.main.ts index ae036b8..5f1dbf8 100644 --- a/packages/server/src/index.main.ts +++ b/packages/server/src/index.main.ts @@ -5,8 +5,8 @@ import { OpenIDController } from "./controllers/OpenIDController"; import { Redis } from "./controllers/RedisController"; import { SocketController } from "./controllers/SocketController"; import { getLogger } from "./lib/Logger"; -import { Settings } from "./lib/Settings"; import { ClientConfigService } from "./services/ClientConfigService"; +import { Settings } from "./settings/Settings"; import { Palette } from "./utils/Palette"; import { spawnCacheWorkers } from "./workers/worker"; @@ -21,13 +21,15 @@ export default () => { }), spawnCacheWorkers(), Palette.load().then(() => - Settings.loadAllSettings().then(async () => { - await ClientConfigService.initialize(); - await CanvasController.initialize(); - ExpressController.initialize(); - SocketController.initialize(); - CaptchaController.initialize(); - }) + Settings.get() + .initialize() + .then(async () => { + await ClientConfigService.initialize(); + await CanvasController.initialize(); + ExpressController.initialize(); + SocketController.initialize(); + CaptchaController.initialize(); + }) ), ]).then(() => { Logger.info("Startup tasks have completed, starting server"); diff --git a/packages/server/src/index.worker.ts b/packages/server/src/index.worker.ts index 3f1be73..6d24d57 100644 --- a/packages/server/src/index.worker.ts +++ b/packages/server/src/index.worker.ts @@ -1,8 +1,8 @@ import { SocketController } from "./controllers/SocketController"; import { BullMQ_JobManager } from "./jobs/bullmq"; import { getLogger } from "./lib/Logger"; -import { Settings } from "./lib/Settings"; import { ClientConfigService } from "./services/ClientConfigService"; +import { Settings } from "./settings/Settings"; import { Palette } from "./utils/Palette"; const Logger = getLogger("MAIN"); @@ -10,9 +10,11 @@ const Logger = getLogger("MAIN"); export default () => { Promise.all([ Palette.load(), - Settings.loadAllSettings().then(() => { - SocketController.initialize(true); - }), + Settings.get() + .initialize() + .then(() => { + SocketController.initialize(true); + }), ClientConfigService.initialize(), ]).then(() => { Logger.info("Startup tasks completed, starting workers..."); diff --git a/packages/server/src/lib/Logger.ts b/packages/server/src/lib/Logger.ts index 344ba88..6a39848 100644 --- a/packages/server/src/lib/Logger.ts +++ b/packages/server/src/lib/Logger.ts @@ -69,6 +69,7 @@ export const LoggerType = createEnum([ "CANVAS_WORK", "WORKER_ROOT", "RECAPTCHA", + "SECRETS", ]); export const getLogger = ( diff --git a/packages/server/src/lib/Secrets.ts b/packages/server/src/lib/Secrets.ts new file mode 100644 index 0000000..f600358 --- /dev/null +++ b/packages/server/src/lib/Secrets.ts @@ -0,0 +1,53 @@ +import fs from "node:fs"; + +import { getLogger } from "./Logger"; + +const Logger = getLogger("SECRETS"); + +export class Secrets { + private secrets: Record; + + constructor(secrets: T[]) { + const builder: any = {}; + for (const secret of secrets) { + builder[secret] = ""; + } + this.secrets = builder; + } + + load() { + for (const secret of Object.keys(this.secrets)) { + this._load(secret as any); + } + } + + private _load(secret: T) { + const RAW = process.env[secret]; + const FILE = process.env[`${secret}_FILE`]; + + if (RAW || FILE) { + let content: string; + if (FILE) { + content = fs.readFileSync(FILE, "utf8"); + } else { + content = RAW as any; + } + + if (!content) + throw new Error( + `${secret} failed to load from ${FILE ? "file" : "environment"}` + ); + + Logger.info(`Loaded ${secret} from ${FILE ? "file" : "environment"}`); + this.secrets[secret] = content; + } else { + throw new Error( + `${secret} or ${secret}_FILE is missing from environment variables` + ); + } + } + + get(secret: T) { + return this.secrets[secret]; + } +} diff --git a/packages/server/src/lib/Settings.ts b/packages/server/src/lib/Settings.ts deleted file mode 100644 index dd9be51..0000000 --- a/packages/server/src/lib/Settings.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { getLogger } from "./Logger"; -import { prisma } from "./prisma"; - -const Logger = getLogger("SETTINGS"); - -interface ISettings { - "canvas.frozen"(): boolean; - "canvas.size"(): { width: number; height: number }; -} - -type ISettingValue = ReturnType; -type ISettingKey = keyof ISettings; - -export class SettingNotSetError extends Error { - constructor(settingId: string) { - super("SettingNotSetError: Setting `" + settingId + "` is not set"); - } -} - -export class SettingStaleError extends Error { - constructor(settingId: string) { - super("SettingStaleError: Setting `" + settingId + "` is stale"); - } -} - -type ICachedSetting = { - key: Key; - value?: ISettingValue; - lastFetched: Date; -}; - -const isSettingStale = (cachedSetting: ICachedSetting): boolean => { - // TODO: use a better time - if (Date.now() - cachedSetting.lastFetched.getTime() > 60 * 1000) { - return true; - } - - return false; -}; - -export const Settings = new (class Settings { - #local: ICachedSetting[] = []; - - private upsertLocal( - key: T, - value: ISettingValue | undefined - ) { - const val = this.#local.find((s) => s.key === key); - if (val) { - val.value = JSON.stringify(value); - val.lastFetched = new Date(); - } else { - this.#local.push({ - key, - value: JSON.stringify(value), - lastFetched: new Date(), - }); - } - } - - /** - * Clears all #local & loads fresh settings - */ - async loadAllSettings() { - const settings = await prisma.setting.findMany(); - - this.#local = []; - - for (const setting of settings) { - this.upsertLocal(setting.key as any, JSON.parse(setting.value)); - } - - Logger.debug(`Settings#loadAllSettings loaded ${settings.length} settings`); - } - - get( - settingId: SettingID, - throwUnset: false - ): Promise | undefined>; - get( - settingId: SettingID, - throwUnset: true - ): Promise>; - async get(settingId: string, throwUnset: boolean): Promise { - const setting = await prisma.setting.findFirst({ - where: { key: settingId }, - }); - - this.upsertLocal( - settingId as any, - setting ? JSON.parse(setting.value) : undefined - ); - - if (throwUnset && !setting) throw new SettingNotSetError(settingId); - if (!setting) return undefined; - return JSON.parse(setting.value); - } - - quickGet( - settingId: SettingID, - throwUnset: false, - throwStale: boolean - ): ISettingValue | undefined; - quickGet( - settingId: SettingID, - throwUnset: true, - throwStale: boolean - ): ISettingValue; - quickGet( - settingId: string, - throwUnset: boolean, - throwStale: boolean = false - ): unknown { - const setting = this.#local.find((s) => s.key === settingId); - - if (!setting) { - if (throwUnset) throw new SettingNotSetError(settingId); - return undefined; - } - - if (isSettingStale(setting) && throwStale) { - throw new SettingStaleError(settingId); - } - - return JSON.parse(setting.value); - } - - set( - settingId: SettingID, - value: ISettingValue - ) { - this.upsertLocal(settingId, value); - return prisma.setting.upsert({ - where: { - key: settingId, - }, - create: { - key: settingId, - value: JSON.stringify(value), - }, - update: { - value: JSON.stringify(value), - }, - }); - } -})(); diff --git a/packages/server/src/settings/Settings.ts b/packages/server/src/settings/Settings.ts new file mode 100644 index 0000000..476bee4 --- /dev/null +++ b/packages/server/src/settings/Settings.ts @@ -0,0 +1,85 @@ +import { getLogger } from "../lib/Logger"; +import { CanvasFrozenSetting } from "./canvas/frozen"; +import { CanvasSizeSetting } from "./canvas/size"; +import { CaptchaAlgorithmSetting } from "./captcha/algorithm"; +import { CaptchaEnableSetting } from "./captcha/enable"; +import { CaptchaMaxNumberSetting } from "./captcha/maxnumber"; +import { ISettingKey, ISettingValue, Setting } from "./setting.class"; + +const Logger = getLogger("SETTINGS"); + +type AllSettings = Record>; + +export class Settings { + private static instance: Settings; + settings: AllSettings = {} as any; + + private constructor() { + this.register("canvas.frozen", new CanvasFrozenSetting()); + this.register("canvas.size", new CanvasSizeSetting()); + this.register("captcha.enable", new CaptchaEnableSetting()); + this.register("captcha.algorithm", new CaptchaAlgorithmSetting()); + this.register("captcha.maxnumber", new CaptchaMaxNumberSetting()); + } + + static get(): Settings; + static get(key?: T): ISettingValue; + static get(key?: T): ISettingValue | Settings { + if (typeof key === "string") { + return Settings.get().get(key); + } + + if (!this.instance) this.instance = new Settings(); + + return this.instance; + } + + static set(key: T, value: ISettingValue) { + return Settings.get().get(key, true).set(value); + } + + async initialize() { + await Promise.allSettled( + Object.values(this.settings).map((s) => + s.load(true).then(() => { + Logger.info("Loaded " + s.KEY); + }) + ) + ); + } + + register(key: T, instance: Setting) { + this.settings[key] = instance; + } + + get(key: T): ISettingValue; + get(key: T, full: true): Setting; + get( + key: T, + full?: boolean + ): ISettingValue | Setting { + if (!(key in this.settings)) { + throw new Error(`Failed to fetch setting ${key} as it's not registered`); + } + + if (full) { + // @ts-expect-error Generic type + return this.settings[key]; + } + + // @ts-expect-error generic type + return this.settings[key].get(); + } +} + +export class SettingNotSetError extends Error { + constructor(settingId: string) { + super("SettingNotSetError: Setting `" + settingId + "` is not set"); + } +} + +export class SettingStaleError extends Error { + constructor(settingId: string) { + super("SettingStaleError: Setting `" + settingId + "` is stale"); + } +} diff --git a/packages/server/src/settings/canvas/frozen.ts b/packages/server/src/settings/canvas/frozen.ts new file mode 100644 index 0000000..21c1731 --- /dev/null +++ b/packages/server/src/settings/canvas/frozen.ts @@ -0,0 +1,9 @@ +import { Setting } from "../setting.class"; + +export class CanvasFrozenSetting extends Setting<"canvas.frozen"> { + readonly KEY = "canvas.frozen"; + + override default() { + return false; + } +} diff --git a/packages/server/src/settings/canvas/size.ts b/packages/server/src/settings/canvas/size.ts new file mode 100644 index 0000000..68feb10 --- /dev/null +++ b/packages/server/src/settings/canvas/size.ts @@ -0,0 +1,9 @@ +import { Setting } from "../setting.class"; + +export class CanvasSizeSetting extends Setting<"canvas.size"> { + readonly KEY = "canvas.size"; + + override default() { + return { width: 100, height: 100 }; + } +} diff --git a/packages/server/src/settings/captcha/algorithm.ts b/packages/server/src/settings/captcha/algorithm.ts new file mode 100644 index 0000000..4911300 --- /dev/null +++ b/packages/server/src/settings/captcha/algorithm.ts @@ -0,0 +1,9 @@ +import { ISettingValue, Setting } from "../setting.class"; + +export class CaptchaAlgorithmSetting extends Setting<"captcha.algorithm"> { + readonly KEY = "captcha.algorithm"; + + override default() { + return "SHA-256" as ISettingValue<"captcha.algorithm">; + } +} diff --git a/packages/server/src/settings/captcha/enable.ts b/packages/server/src/settings/captcha/enable.ts new file mode 100644 index 0000000..92fe2e8 --- /dev/null +++ b/packages/server/src/settings/captcha/enable.ts @@ -0,0 +1,9 @@ +import { Setting } from "../setting.class"; + +export class CaptchaEnableSetting extends Setting<"captcha.enable"> { + readonly KEY = "captcha.enable"; + + override default() { + return false; + } +} diff --git a/packages/server/src/settings/captcha/maxnumber.ts b/packages/server/src/settings/captcha/maxnumber.ts new file mode 100644 index 0000000..117c8df --- /dev/null +++ b/packages/server/src/settings/captcha/maxnumber.ts @@ -0,0 +1,9 @@ +import { Setting } from "../setting.class"; + +export class CaptchaMaxNumberSetting extends Setting<"captcha.maxnumber"> { + readonly KEY = "captcha.maxnumber"; + + override default() { + return 1_000_000; + } +} diff --git a/packages/server/src/settings/setting.class.ts b/packages/server/src/settings/setting.class.ts new file mode 100644 index 0000000..7679a63 --- /dev/null +++ b/packages/server/src/settings/setting.class.ts @@ -0,0 +1,79 @@ +import { prisma } from "../lib/prisma"; + +interface ISettings { + "canvas.frozen"(): boolean; + "canvas.size"(): { width: number; height: number }; + "captcha.enable"(): boolean; + "captcha.algorithm"(): "SHA-1" | "SHA-256" | "SHA-512"; + "captcha.maxnumber"(): number; +} + +export type ISettingValue = ReturnType< + ISettings[Key] +>; +export type ISettingKey = keyof ISettings; + +export abstract class Setting> { + abstract readonly KEY: K; + abstract default(): V; + + protected _staleFlag = true; + protected _value: V = this.default(); + protected _lastUpdated: Date | null = null; + + constructor() {} + + isStale() { + if (this._staleFlag) return true; + if (!this._lastUpdated) return true; + return Date.now() - this._lastUpdated.getTime() > 60 * 1000; + } + + async load(force?: boolean) { + if (!force && !this.isStale()) return this; + + const setting = await prisma.setting.findFirst({ + where: { + key: this.KEY, + }, + }); + + if (setting) { + this._value = JSON.parse(setting.value); + } + + this._lastUpdated = new Date(); + + return this; + } + + poke() { + this._staleFlag = true; + return this; + } + + get() { + if (this.isStale()) { + // if stale, update in the background + void this.load(); + } + + return this._value; + } + + async set(value: V) { + this._value = value; + await prisma.setting.upsert({ + where: { + key: this.KEY, + }, + create: { + key: this.KEY, + value: JSON.stringify(value), + }, + update: { + value: JSON.stringify(value), + }, + }); + } +} diff --git a/packages/server/src/tools/init_settings.ts b/packages/server/src/tools/init_settings.ts deleted file mode 100644 index b6afbb3..0000000 --- a/packages/server/src/tools/init_settings.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -const prisma = new PrismaClient(); - -// eslint-disable-next-line no-console -const log = (...msg: any[]) => console.log(...msg); - -async function main() { - const SETTINGS: { key: string; defaultValue: any }[] = [ - { - key: "canvas.size", - defaultValue: { - width: 100, - height: 100, - }, - }, - { - key: "canvas.frozen", - defaultValue: false, - }, - ]; - - for (const setting of SETTINGS) { - log("Ensuring setting", setting.key); - await prisma.setting.upsert({ - where: { key: setting.key }, - update: {}, - create: { - key: setting.key, - value: JSON.stringify(setting.defaultValue), - }, - }); - } -} - -main() - .then(async () => { - await prisma.$disconnect(); - }) - .catch(async (e) => { - // eslint-disable-next-line no-console - console.error(e); - await prisma.$disconnect(); - process.exit(1); - }); diff --git a/packages/server/src/types.ts b/packages/server/src/types.ts index 6237ad1..43cf8f3 100644 --- a/packages/server/src/types.ts +++ b/packages/server/src/types.ts @@ -74,6 +74,9 @@ declare global { PIXEL_LOG_PATH?: string; + CAPTCHA_HMAC_SECRET?: string; + CAPTCHA_HMAC_SECRET_FILE?: string; + CACHE_WORKERS?: string; SENTRY_DSN?: string; diff --git a/packages/server/src/utils/validate_environment.ts b/packages/server/src/utils/validate_environment.ts index 5451070..a9d26e5 100644 --- a/packages/server/src/utils/validate_environment.ts +++ b/packages/server/src/utils/validate_environment.ts @@ -1,3 +1,4 @@ +import { CaptchaController } from "../controllers/CaptchaController"; import { getLogger } from "../lib/Logger"; const Logger = getLogger("MAIN"); @@ -127,3 +128,5 @@ if (process.env.NODE_ENV !== "development" && process.env.DEV_ADMIN_TOKEN) { } // #endregion + +CaptchaController.validateEnvironment(); -- GitLab From 70b2fa23e4ddab8472e5710ccdd49e721e8b9db7 Mon Sep 17 00:00:00 2001 From: Grant Date: Sun, 22 Jun 2025 00:11:38 -0600 Subject: [PATCH 10/10] fix tests --- .../server/src/__test__/api/client.test.ts | 8 +++--- .../src/__test__/controllers/canvas.test.ts | 27 ++++++++++--------- .../server/src/__test__/lib/express.test.ts | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/server/src/__test__/api/client.test.ts b/packages/server/src/__test__/api/client.test.ts index 1cb2cf7..40df6b4 100644 --- a/packages/server/src/__test__/api/client.test.ts +++ b/packages/server/src/__test__/api/client.test.ts @@ -472,7 +472,7 @@ describe.skip("Client endpoints /api", () => { const CanvasController = ( await import("../../controllers/CanvasController") ).CanvasController; - const Settings = (await import("../../lib/Settings")).Settings; + const Settings = (await import("../../settings/Settings")).Settings; mockReset(prismaMock); prismaMock.setting.findMany.mockResolvedValue([ @@ -485,7 +485,7 @@ describe.skip("Client endpoints /api", () => { value: JSON.stringify({ width: 100, height: 100 }), }, ]); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await CanvasController.initialize(); ExpressController.initialize(); @@ -570,7 +570,7 @@ describe.skip("Client endpoints /api", () => { .ExpressController; CanvasController = (await import("../../controllers/CanvasController")) .CanvasController; - const Settings = (await import("../../lib/Settings")).Settings; + const Settings = (await import("../../settings/Settings")).Settings; mockReset(prismaMock); prismaMock.setting.findMany.mockResolvedValue([ @@ -583,7 +583,7 @@ describe.skip("Client endpoints /api", () => { value: JSON.stringify({ width: 100, height: 100 }), }, ]); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await CanvasController.initialize(); ExpressController.initialize(); diff --git a/packages/server/src/__test__/controllers/canvas.test.ts b/packages/server/src/__test__/controllers/canvas.test.ts index 2923a26..1a6fcb7 100644 --- a/packages/server/src/__test__/controllers/canvas.test.ts +++ b/packages/server/src/__test__/controllers/canvas.test.ts @@ -8,6 +8,9 @@ jest.mock("../../controllers/SocketController", () => { broadcastConfig: jest.fn(), handleConnection: jest.fn(), setupMasterShard: jest.fn(), + getUserFromSocket: jest.fn(), + getUserSockets: jest.fn(), + MOD_NS: jest.fn(), }; return { @@ -37,12 +40,12 @@ import { mockReset } from "jest-mock-extended"; import { type CanvasController as ICanvasController } from "../../controllers/CanvasController"; import { type Redis as IRedis } from "../../controllers/RedisController"; import { type SocketController as ISocketController } from "../../controllers/SocketController"; -import { type Settings as ISettings } from "../../lib/Settings"; +import { type Settings as ISettings } from "../../settings/Settings"; import { type Palette as IPalette } from "../../utils/Palette"; import type * as IWorkers from "../../workers/worker"; import { prismaMock } from "../_prisma.mock"; -// jest.mock("../../lib/Settings", () => { +// jest.mock("../../settings/Settings", () => { // return { // Settings // } @@ -97,11 +100,11 @@ describe("Canvas", () => { .CanvasController; SocketController = (await import("../../controllers/SocketController")) .SocketController; - Settings = (await import("../../lib/Settings")).Settings; + Settings = (await import("../../settings/Settings")).Settings; Palette = (await import("../../utils/Palette")).Palette; setupPrismaMock(prismaMock); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await Palette.load(); // Redis.initialize(); @@ -141,11 +144,11 @@ describe("Canvas", () => { .CanvasController; SocketController = (await import("../../controllers/SocketController")) .SocketController; - Settings = (await import("../../lib/Settings")).Settings; + Settings = (await import("../../settings/Settings")).Settings; Palette = (await import("../../utils/Palette")).Palette; setupPrismaMock(prismaMock); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await Palette.load(); // Redis.initialize(); @@ -213,11 +216,11 @@ describe("Canvas", () => { .CanvasController; SocketController = (await import("../../controllers/SocketController")) .SocketController; - Settings = (await import("../../lib/Settings")).Settings; + Settings = (await import("../../settings/Settings")).Settings; Palette = (await import("../../utils/Palette")).Palette; setupPrismaMock(prismaMock); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await Palette.load(); // Redis.initialize(); @@ -323,11 +326,11 @@ describe("Canvas", () => { .CanvasController; // SocketController = (await import("../../controllers/SocketController")) // .SocketController; - Settings = (await import("../../lib/Settings")).Settings; + Settings = (await import("../../settings/Settings")).Settings; Palette = (await import("../../utils/Palette")).Palette; setupPrismaMock(prismaMock); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await Palette.load(); // Redis.initialize(); @@ -991,11 +994,11 @@ describe("Canvas", () => { .CanvasController; // SocketController = (await import("../../controllers/SocketController")) // .SocketController; - Settings = (await import("../../lib/Settings")).Settings; + Settings = (await import("../../settings/Settings")).Settings; Palette = (await import("../../utils/Palette")).Palette; setupPrismaMock(prismaMock); - await Settings.loadAllSettings(); + await Settings.get().initialize(); await Palette.load(); // Redis.initialize(); diff --git a/packages/server/src/__test__/lib/express.test.ts b/packages/server/src/__test__/lib/express.test.ts index 9eacc21..ac7f821 100644 --- a/packages/server/src/__test__/lib/express.test.ts +++ b/packages/server/src/__test__/lib/express.test.ts @@ -4,7 +4,7 @@ import request from "supertest"; import { type ExpressController as IExpressController } from "../../controllers/ExpressController"; import { type prismaMock as IPrismaMock } from "../_prisma.mock"; -jest.mock("../../lib/Settings"); +jest.mock("../../settings/Settings"); jest.mock("connect-redis"); jest.mock("express", () => { const real = jest.requireActual("express"); -- GitLab