diff --git a/backend/package-lock.json b/backend/package-lock.json index f97d0b522..5d5f2ae6c 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -256,6 +256,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2660,6 +2661,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.1.tgz", "integrity": "sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==", "license": "MIT", + "peer": true, "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "axios": "^1.3.1", @@ -2730,6 +2732,7 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2957,6 +2960,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.17.tgz", "integrity": "sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==", "license": "MIT", + "peer": true, "dependencies": { "file-type": "21.3.2", "iterare": "1.2.1", @@ -3016,6 +3020,7 @@ "integrity": "sha512-lD5mAYekTTurF3vDaa8C2OKPnjiz4tsfxIc5XlcSUzOhkwWf6Ay3HKvt6FmvuWQam6uIIHX52Clg+e6tAvf/cg==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", @@ -3056,6 +3061,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", "license": "MIT", + "peer": true, "dependencies": { "eventemitter2": "6.4.9" }, @@ -3112,6 +3118,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.17.tgz", "integrity": "sha512-mAf4eOsSBsTOn/VbrUO1gsjW6dVh91qqXPMXun4dN8SnNjf7PTQagM9o8d6ab8ZBpNe6UdZftdrZoDetU+n4Qg==", "license": "MIT", + "peer": true, "dependencies": { "cors": "2.8.6", "express": "5.2.1", @@ -3277,6 +3284,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-11.1.1.tgz", "integrity": "sha512-Ssql79H+EQY/Wg108eJqN4NiNsO/tLrj+qbzOWSQUf2JE4vJQ2RG3WTqUOrYjfjWmVHD3+Ys0+azed7LSMKScw==", "license": "MIT", + "peer": true, "dependencies": { "boxen": "5.1.2", "check-disk-space": "3.4.0" @@ -3386,6 +3394,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", "integrity": "sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==", "license": "MIT", + "peer": true, "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "@nestjs/core": "^10.0.0 || ^11.0.0", @@ -3668,6 +3677,7 @@ "integrity": "sha512-Q5FsI3Cw0fGMXhmsg7c08i4EmXCrcl+WnAxb6LYOLHw4JFFC3yzmx9LaXZ7QMbA+JZXbigU2TirI7RAfO0Qlnw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", @@ -3737,9 +3747,10 @@ "version": "1.15.21", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.21.tgz", "integrity": "sha512-fkk7NJcBscrR3/F8jiqlMptRHP650NxqDnspBMrRe5d8xOoCy9MLL5kOBLFXjFLfMo3KQQHhk+/jUULOMlR1uQ==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" @@ -3781,6 +3792,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3797,6 +3809,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3813,6 +3826,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -3829,6 +3843,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3845,6 +3860,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3861,6 +3877,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3877,6 +3894,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3893,6 +3911,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3909,6 +3928,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3925,6 +3945,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3941,6 +3962,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3957,6 +3979,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3970,14 +3993,14 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/types": { "version": "0.1.26", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.26.tgz", "integrity": "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -4143,6 +4166,7 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4321,6 +4345,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4526,6 +4551,7 @@ "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", @@ -5361,6 +5387,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "devOptional": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5410,6 +5437,7 @@ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5709,6 +5737,7 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", @@ -5881,6 +5910,7 @@ "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -6256,6 +6286,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6364,6 +6395,7 @@ "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.8.tgz", "integrity": "sha512-0HDaDLBBY/maa/LmUVAr70XUOwsiQD+jyzCBjmUErYZUKdMS9dT59PqW59PpVqfGM7ve6H0J6307JTpkCYefHQ==", "license": "MIT", + "peer": true, "dependencies": { "@cacheable/utils": "^2.3.3", "keyv": "^5.5.5" @@ -6611,6 +6643,7 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -6658,13 +6691,15 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/class-validator": { "version": "0.14.4", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.4.tgz", "integrity": "sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==", "license": "MIT", + "peer": true, "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", @@ -7159,27 +7194,6 @@ "node": ">=4" } }, - "node_modules/cssnano": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.3.tgz", - "integrity": "sha512-mLFHQAzyapMVFLiJIn7Ef4C2UCEvtlTlbyILR6B5ZsUAV3D/Pa761R5uC1YPhyBkRd3eqaDm2ncaNrD7R4mTRg==", - "license": "MIT", - "optional": true, - "dependencies": { - "cssnano-preset-default": "^7.0.11", - "lilconfig": "^3.1.3" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.32" - } - }, "node_modules/cssnano-preset-default": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.11.tgz", @@ -7917,6 +7931,7 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7977,6 +7992,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8232,6 +8248,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -9952,6 +9969,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -10838,6 +10856,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -12210,6 +12229,7 @@ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.4.tgz", "integrity": "sha512-k+jf6N8PfQJ0Fe8ZhJlgqU5qJU44Lpvp2yvidH3vp1lPnVQMgi4yEEMPXg5eJS1gFIJTVq1NHBk7Ia9ARdSBdQ==", "license": "MIT-0", + "peer": true, "engines": { "node": ">=6.0.0" } @@ -12651,6 +12671,7 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", "license": "MIT", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -12790,6 +12811,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", @@ -12899,6 +12921,7 @@ "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", "license": "MIT", + "peer": true, "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", @@ -12930,6 +12953,7 @@ "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-11.0.0.tgz", "integrity": "sha512-wqg5XIAGRRIWtTk8qPGxkbrfiwEWz1lgedVLvhLALudKXvg1/L2lTFgTGPJ4Z2e3qcRmxoFxDuSdMdMGNM6I1g==", "license": "MIT", + "peer": true, "dependencies": { "get-caller-file": "^2.0.5", "pino": "^10.0.0", @@ -13089,35 +13113,6 @@ "node": ">= 0.4" } }, - "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/postcss-calc": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", @@ -13761,6 +13756,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "devOptional": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -14264,7 +14260,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/require-directory": { "version": "2.1.1", @@ -14600,6 +14597,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -15497,6 +15495,7 @@ "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", "devOptional": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -15550,6 +15549,7 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -15943,6 +15943,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -16103,6 +16104,7 @@ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", "license": "MIT", + "peer": true, "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^4.2.0", @@ -16298,6 +16300,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16341,6 +16344,7 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { @@ -16738,7 +16742,6 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -16757,7 +16760,6 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -16771,7 +16773,6 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -16786,7 +16787,6 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -16796,8 +16796,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/mime-db": { "version": "1.52.0", @@ -16805,7 +16804,6 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -16816,7 +16814,6 @@ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -16830,7 +16827,6 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", diff --git a/backend/src/modules/governance/dto/proposal-votes-response.dto.ts b/backend/src/modules/governance/dto/proposal-votes-response.dto.ts index 196509cd1..978893de0 100644 --- a/backend/src/modules/governance/dto/proposal-votes-response.dto.ts +++ b/backend/src/modules/governance/dto/proposal-votes-response.dto.ts @@ -43,4 +43,13 @@ export class ProposalVotesResponseDto { @ApiProperty({ type: [RecentVoterDto] }) recentVoters: RecentVoterDto[]; + + @ApiProperty({ + example: 120, + description: 'Total number of votes for this proposal', + }) + total: number; + + @ApiProperty({ example: 0, description: 'Current page index (0-based)' }) + page: number; } diff --git a/backend/src/modules/governance/governance-proposals.controller.ts b/backend/src/modules/governance/governance-proposals.controller.ts index 2a5e93001..0dbc2cce6 100644 --- a/backend/src/modules/governance/governance-proposals.controller.ts +++ b/backend/src/modules/governance/governance-proposals.controller.ts @@ -21,18 +21,21 @@ export class GovernanceProposalsController { @Get() @ApiOperation({ summary: 'List governance proposals', - description: 'Returns indexed proposals from the DB cache, optionally filtered by status.', + description: + 'Returns indexed proposals from the DB cache, optionally filtered by status.', }) @ApiQuery({ name: 'status', required: false, enum: ProposalStatus, - description: 'Filter by proposal status (e.g. ACTIVE, PASSED, FAILED, CANCELLED)', + description: + 'Filter by proposal status (e.g. ACTIVE, PASSED, FAILED, CANCELLED)', example: 'ACTIVE', }) @ApiResponse({ status: 200, - description: 'List of proposals with computed vote percentages and timeline boundaries', + description: + 'List of proposals with computed vote percentages and timeline boundaries', type: [ProposalListItemDto], }) getProposals( @@ -42,8 +45,11 @@ export class GovernanceProposalsController { if (statusKey !== undefined) { // Accept both enum keys (ACTIVE) and enum values (Active) - const byKey = ProposalStatus[statusKey.toUpperCase() as keyof typeof ProposalStatus]; - const byValue = Object.values(ProposalStatus).includes(statusKey as ProposalStatus) + const byKey = + ProposalStatus[statusKey.toUpperCase() as keyof typeof ProposalStatus]; + const byValue = Object.values(ProposalStatus).includes( + statusKey as ProposalStatus, + ) ? (statusKey as ProposalStatus) : undefined; status = byKey ?? byValue; @@ -65,10 +71,10 @@ export class GovernanceProposalsController { 'Returns a proposal vote tally plus the most recent voters for a given proposal onChainId.', }) @ApiQuery({ - name: 'limit', + name: 'page', required: false, - description: 'Maximum number of recent voter entries to return', - example: 20, + description: 'Zero-based page index (20 votes per page)', + example: 0, }) @ApiResponse({ status: 200, @@ -77,8 +83,8 @@ export class GovernanceProposalsController { }) getProposalVotes( @Param('id', ParseIntPipe) id: number, - @Query('limit', new DefaultValuePipe(20), ParseIntPipe) limit: number, + @Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number, ): Promise { - return this.governanceService.getProposalVotesByOnChainId(id, limit); + return this.governanceService.getProposalVotesByOnChainId(id, page); } } diff --git a/backend/src/modules/governance/governance.service.ts b/backend/src/modules/governance/governance.service.ts index cdf254287..4ed95490e 100644 --- a/backend/src/modules/governance/governance.service.ts +++ b/backend/src/modules/governance/governance.service.ts @@ -7,7 +7,10 @@ import { UserService } from '../user/user.service'; import { DelegationResponseDto } from './dto/delegation-response.dto'; import { ProposalListItemDto } from './dto/proposal-list-item.dto'; import { ProposalVotesResponseDto } from './dto/proposal-votes-response.dto'; -import { GovernanceProposal, ProposalStatus } from './entities/governance-proposal.entity'; +import { + GovernanceProposal, + ProposalStatus, +} from './entities/governance-proposal.entity'; import { Vote, VoteDirection } from './entities/vote.entity'; import { VotingPowerResponseDto } from './dto/voting-power-response.dto'; @@ -25,7 +28,10 @@ export class GovernanceService { async getProposals(status?: ProposalStatus): Promise { const where = status ? { status } : {}; - const proposals = await this.proposalRepo.find({ where, order: { createdAt: 'DESC' } }); + const proposals = await this.proposalRepo.find({ + where, + order: { createdAt: 'DESC' }, + }); if (proposals.length === 0) { return []; @@ -34,21 +40,24 @@ export class GovernanceService { const proposalIds = proposals.map((p) => p.id); // Aggregate vote counts per proposal in a single query - const tallies: { proposalId: string; forCount: string; againstCount: string }[] = - await this.voteRepo - .createQueryBuilder('vote') - .select('vote.proposalId', 'proposalId') - .addSelect( - `SUM(CASE WHEN vote.direction = '${VoteDirection.FOR}' THEN 1 ELSE 0 END)`, - 'forCount', - ) - .addSelect( - `SUM(CASE WHEN vote.direction = '${VoteDirection.AGAINST}' THEN 1 ELSE 0 END)`, - 'againstCount', - ) - .where('vote.proposalId IN (:...ids)', { ids: proposalIds }) - .groupBy('vote.proposalId') - .getRawMany(); + const tallies: { + proposalId: string; + forCount: string; + againstCount: string; + }[] = await this.voteRepo + .createQueryBuilder('vote') + .select('vote.proposalId', 'proposalId') + .addSelect( + `SUM(CASE WHEN vote.direction = '${VoteDirection.FOR}' THEN 1 ELSE 0 END)`, + 'forCount', + ) + .addSelect( + `SUM(CASE WHEN vote.direction = '${VoteDirection.AGAINST}' THEN 1 ELSE 0 END)`, + 'againstCount', + ) + .where('vote.proposalId IN (:...ids)', { ids: proposalIds }) + .groupBy('vote.proposalId') + .getRawMany(); const tallyMap = new Map(tallies.map((t) => [t.proposalId, t])); @@ -58,8 +67,12 @@ export class GovernanceService { const againstCount = tally ? Number(tally.againstCount) : 0; const totalCount = forCount + againstCount; - const forPercent = totalCount > 0 ? Math.round((forCount / totalCount) * 10000) / 100 : 0; - const againstPercent = totalCount > 0 ? Math.round((againstCount / totalCount) * 10000) / 100 : 0; + const forPercent = + totalCount > 0 ? Math.round((forCount / totalCount) * 10000) / 100 : 0; + const againstPercent = + totalCount > 0 + ? Math.round((againstCount / totalCount) * 10000) / 100 + : 0; return { id: proposal.id, @@ -115,18 +128,18 @@ export class GovernanceService { async getProposalVotesByOnChainId( onChainId: number, - limit = 20, + page = 0, ): Promise { const proposal = await this.proposalRepo.findOneBy({ onChainId }); if (!proposal) { throw new NotFoundException(`Proposal ${onChainId} not found`); } - const safeLimit = Math.max(1, Math.min(100, Math.floor(limit))); - const votes = await this.voteRepo.find({ + const [votes, total] = await this.voteRepo.findAndCount({ where: { proposalId: proposal.id }, order: { createdAt: 'DESC' }, - take: safeLimit, + take: 20, + skip: page * 20, }); let forWeight = 0; @@ -158,6 +171,8 @@ export class GovernanceService { weight: String(vote.weight), votedAt: vote.createdAt.toISOString(), })), + total, + page, }; } } diff --git a/package.json b/package.json index 73dc9b433..049dcbeca 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ }, "lint-staged": { "backend/src/**/*.{ts,tsx}": [ - "npx prettier --write backend/src/**/*.{ts,tsx}", - "npx eslint --fix backend/src/**/*.{ts,tsx}" + "npx prettier --write", + "backend/node_modules/.bin/eslint --fix --config backend/eslint.config.mjs" ] }, "dependencies": {