From 78471cca5c5545c173651e62fc6f347db0cee7eb Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Mon, 9 Sep 2024 15:19:59 +0200 Subject: [PATCH 01/35] sentry update --- desci-server/.gitignore | 3 + desci-server/package.json | 10 +- desci-server/src/server.ts | 4 +- desci-server/tsconfig.json | 14 +- desci-server/yarn.lock | 506 ++++++++++++++++++++++++++++++++++--- 5 files changed, 488 insertions(+), 49 deletions(-) diff --git a/desci-server/.gitignore b/desci-server/.gitignore index e9b57f85c..358616150 100755 --- a/desci-server/.gitignore +++ b/desci-server/.gitignore @@ -16,3 +16,6 @@ server.log repo-tmp queries.sql + +# Sentry Config File +.sentryclirc diff --git a/desci-server/package.json b/desci-server/package.json index 7db90e069..9735e4a81 100755 --- a/desci-server/package.json +++ b/desci-server/package.json @@ -25,7 +25,7 @@ "script:seed-social-data": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/seed-social-data.ts", "script:DESTRUCTIVE-clear-social-data": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/DESTRUCTIVE-clear-social-data.ts", "script:seed-community-member": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/seed-community-members.ts", - "build": "rimraf dist && tsc && yarn copy-files", + "build": "rimraf dist && tsc && yarn copy-files && yarn sentry:sourcemaps", "copy-files": "copyfiles -u 1 src/**/*.cjs dist/", "generate": "npx prisma generate", "migrate:local": "DATABASE_URL=postgresql://walter:white@localhost:5433/boilerplate npx prisma migrate dev", @@ -50,7 +50,8 @@ "docker:prod": "../dockerProd.sh", "podman:dev": "podman-compose --file docker-compose.yml --file docker-compose.dev.yml up --build", "email-dev": "email dev --dir ./src/templates/emails --port 3777", - "check-deps": "npx npm-check" + "check-deps": "npx npm-check", + "sentry:sourcemaps": "sentry-cli sourcemaps inject --org desci-labs --project nodes-backend ./dist && sentry-cli sourcemaps upload --org desci-labs --project nodes-backend ./dist" }, "dependencies": { "@automerge/automerge-repo": "^1.0.19", @@ -68,7 +69,8 @@ "@prisma/client": "4.10.1", "@react-email/components": "0.0.15", "@sendgrid/mail": "^7.7.0", - "@sentry/node": "^7.12.0", + "@sentry/cli": "^2.35.0", + "@sentry/node": "^8.29.0", "@sentry/tracing": "^7.12.0", "@types/lodash-es": "^4.17.12", "@types/mkdirp": "^1.0.2", @@ -182,4 +184,4 @@ "prisma": { "seed": "node --no-warnings=ExperimentalWarning --loader ts-node/esm prisma/seed.ts" } -} +} \ No newline at end of file diff --git a/desci-server/src/server.ts b/desci-server/src/server.ts index 392fecc1a..a84f90f26 100644 --- a/desci-server/src/server.ts +++ b/desci-server/src/server.ts @@ -230,9 +230,7 @@ class AppServer { // We recommend adjusting this value in production tracesSampleRate: 1.0, }); - this.app.use(Sentry.Handlers.requestHandler()); - this.app.use(Sentry.Handlers.tracingHandler()); - this.app.use(Sentry.Handlers.errorHandler()); + Sentry.setupExpressErrorHandler(this.app); } else { logger.info('[DeSci Nodes] Telemetry disabled'); } diff --git a/desci-server/tsconfig.json b/desci-server/tsconfig.json index 6b32eb774..c0d0c4e60 100755 --- a/desci-server/tsconfig.json +++ b/desci-server/tsconfig.json @@ -13,6 +13,7 @@ // "verbatimModuleSyntax": true, "lib": ["esnext", "dom"], + "outDir": "./dist", "removeComments": true, "emitDecoratorMetadata": true, @@ -21,14 +22,23 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "skipLibCheck": true, + // dagConcat.cjs requires this "allowJs": true, - "inlineSourceMap": true, + "paths": { // Overrides a built-in Response type "express": ["./src/types/express"] }, - "jsx": "react" + + "jsx": "react", + "sourceMap": true, + "inlineSources": true, + + // Set `sourceRoot` to "/" to strip the build path prefix + // from generated source code references. + // This improves issue grouping in Sentry. + "sourceRoot": "/" }, "include": ["./src/**/*.tsx", "./src/**/*.ts", "./src/**/*.cjs", "src/utils/dagConcat.ts"], "exclude": ["test/**/*.ts"], diff --git a/desci-server/yarn.lock b/desci-server/yarn.lock index 0385be942..9909ed205 100644 --- a/desci-server/yarn.lock +++ b/desci-server/yarn.lock @@ -3605,7 +3605,21 @@ dependencies: "@opentelemetry/api" "^1.0.0" -"@opentelemetry/api@1.x": +"@opentelemetry/api-logs@0.52.1": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz#52906375da4d64c206b0c4cb8ffa209214654ecc" + integrity sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A== + dependencies: + "@opentelemetry/api" "^1.0.0" + +"@opentelemetry/api-logs@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.53.0.tgz#c478cbd8120ec2547b64edfa03a552cfe42170be" + integrity sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw== + dependencies: + "@opentelemetry/api" "^1.0.0" + +"@opentelemetry/api@1.x", "@opentelemetry/api@^1.8", "@opentelemetry/api@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -3674,6 +3688,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.8.0.tgz#6141ff59f7c07fac8eda3f9f208b6aaf06893471" integrity sha512-ueLmocbWDi1aoU4IPdOQyt4qz/Dx+NYyU4qoa3d683usbnkDLUXYXJFfKIMPFV2BbrI5qtnpTtzErCKewoM8aw== +"@opentelemetry/context-async-hooks@^1.25.1": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.26.0.tgz#fa92f722cf685685334bba95f258d3ef9fce60f6" + integrity sha512-HedpXXYzzbaoutw6DFLWLDket2FwLkLpil4hGCZ1xYEIMTcivdfwEOISgdbLEWyG3HW52gTq2V9mOVJrONgiwg== + "@opentelemetry/core@1.14.0": version "1.14.0" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.14.0.tgz#64e876b29cb736c984d54164cd47433f513eafd3" @@ -3702,6 +3721,13 @@ dependencies: "@opentelemetry/semantic-conventions" "1.25.0" +"@opentelemetry/core@1.26.0", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.25.1": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.26.0.tgz#7d84265aaa850ed0ca5813f97d831155be42b328" + integrity sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/core@1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.8.0.tgz#cca18594dd48ded6dc0d08c7e789c79af0315934" @@ -3933,6 +3959,16 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-connect@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.39.0.tgz#32bdbaac464cba061c95df6c850ee81efdd86f8b" + integrity sha512-pGBiKevLq7NNglMgqzmeKczF4XQMTOUOTkK8afRHMZMnrK3fcETyTH7lVaSozwiOM3Ws+SuEmXZT7DYrrhxGlg== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@types/connect" "3.4.36" + "@opentelemetry/instrumentation-connect@^0.31.4": version "0.31.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.31.4.tgz#c1693d26f103dc133cb6a9708cc9ed2fda288a52" @@ -3959,6 +3995,15 @@ "@opentelemetry/semantic-conventions" "^1.0.0" semver "^7.3.2" +"@opentelemetry/instrumentation-express@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.42.0.tgz#279f195aa66baee2b98623a16666c6229c8e7564" + integrity sha512-YNcy7ZfGnLsVEqGXQPT+S0G1AE46N21ORY7i7yUQyfhGAL4RBjnZUqefMI0NwqIl6nGbr1IpF0rZGoN8Q7x12Q== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-express@^0.32.4": version "0.32.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.32.4.tgz#bced338cc6c2a5aeb3433f12302c0460207c7090" @@ -3969,6 +4014,15 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/express" "4.17.13" +"@opentelemetry/instrumentation-fastify@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.39.0.tgz#96a040e4944daf77c53a8fe5a128bc3b2568e4aa" + integrity sha512-SS9uSlKcsWZabhBp2szErkeuuBDgxOUlllwkS92dVaWRnMmwysPhcEgHKB8rUe3BHg/GnZC1eo1hbTZv4YhfoA== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-fastify@^0.31.4": version "0.31.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.31.4.tgz#d3a17a190ba0b258330423982f7437c66e8f2b70" @@ -3978,6 +4032,14 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-fs@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.15.0.tgz#41658507860f39fee5209bca961cea8d24ca2a83" + integrity sha512-JWVKdNLpu1skqZQA//jKOcKdJC66TWKqa2FUFq70rKohvaSq47pmXlnabNO+B/BvLfmidfiaN35XakT5RyMl2Q== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-fs@^0.7.4": version "0.7.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.7.4.tgz#28a15842fc9b8d373f87c7fbf560049717f58156" @@ -3987,6 +4049,13 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-generic-pool@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.39.0.tgz#2b9af16ad82d5cbe67125c0125753cecd162a728" + integrity sha512-y4v8Y+tSfRB3NNBvHjbjrn7rX/7sdARG7FuK6zR8PGb28CTa0kHpEGCJqvL9L8xkTNvTXo+lM36ajFGUaK1aNw== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-generic-pool@^0.31.4": version "0.31.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.31.4.tgz#049efa886f666c9e21cf0f32c06ef45642cbf992" @@ -3996,6 +4065,13 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/generic-pool" "^3.1.9" +"@opentelemetry/instrumentation-graphql@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.43.0.tgz#71bb94ea775c70dbd388c739b397ec1418f3f170" + integrity sha512-aI3YMmC2McGd8KW5du1a2gBA0iOMOGLqg4s9YjzwbjFwjlmMNFSK1P3AIg374GWg823RPUGfVTIgZ/juk9CVOA== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-graphql@^0.34.3": version "0.34.3" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.34.3.tgz#f55cb9223f616a27c8fa4303ee8c8b8f64059ddd" @@ -4011,6 +4087,15 @@ "@opentelemetry/instrumentation" "0.40.0" "@opentelemetry/semantic-conventions" "1.14.0" +"@opentelemetry/instrumentation-hapi@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.41.0.tgz#de8711907256d8fae1b5faf71fc825cef4a7ddbb" + integrity sha512-jKDrxPNXDByPlYcMdZjNPYCvw0SQJjN+B1A+QH+sx+sAHsKSAf9hwFiJSrI6C4XdOls43V/f/fkp9ITkHhKFbQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-hapi@^0.31.4": version "0.31.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.31.4.tgz#df54ed4d36618f5adff0235bd95e68ae0da7ecd0" @@ -4021,6 +4106,16 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/hapi__hapi" "20.0.9" +"@opentelemetry/instrumentation-http@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.53.0.tgz#0d806adf1b3aba036bc46e16162e3c0dbb8a6b60" + integrity sha512-H74ErMeDuZfj7KgYCTOFGWF5W9AfaPnqLQQxeFq85+D29wwV2yqHbz2IKLYpkOh7EI6QwDEl7rZCIxjJLyc/CQ== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/instrumentation" "0.53.0" + "@opentelemetry/semantic-conventions" "1.27.0" + semver "^7.5.2" + "@opentelemetry/instrumentation-http@^0.40.0": version "0.40.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.40.0.tgz#ed4206ea96d64b5fd83b796f027642e42dca0cd1" @@ -4031,6 +4126,15 @@ "@opentelemetry/semantic-conventions" "1.14.0" semver "^7.3.5" +"@opentelemetry/instrumentation-ioredis@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.43.0.tgz#dbadabaeefc4cb47c406f878444f1bcac774fa89" + integrity sha512-i3Dke/LdhZbiUAEImmRG3i7Dimm/BD7t8pDDzwepSvIQ6s2X6FPia7561gw+64w+nx0+G9X14D7rEfaMEmmjig== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/redis-common" "^0.36.2" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-ioredis@^0.34.3": version "0.34.3" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.34.3.tgz#95fb4b6ff2b5eb29e01ee8d36ec24c69f2996019" @@ -4049,6 +4153,15 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-koa@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.43.0.tgz#963fd192a1b5f6cbae5dabf4ec82e3105cbb23b1" + integrity sha512-lDAhSnmoTIN6ELKmLJBplXzT/Jqs5jGZehuG22EdSMaTwgjMpxMDI1YtlKEhiWPWkrz5LUsd0aOO0ZRc9vn3AQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-koa@^0.34.6": version "0.34.6" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.34.6.tgz#88f2ab16f94f7c41bcd4b3487c254f9374a438c1" @@ -4076,6 +4189,15 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/memcached" "^2.2.6" +"@opentelemetry/instrumentation-mongodb@0.47.0": + version "0.47.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.47.0.tgz#f8107d878281433905e717f223fb4c0f10356a7b" + integrity sha512-yqyXRx2SulEURjgOQyJzhCECSh5i1uM49NUaq9TqLd6fA7g26OahyJfsr9NE38HFqGRHpi4loyrnfYGdrsoVjQ== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/sdk-metrics" "^1.9.1" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-mongodb@^0.35.0": version "0.35.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.35.0.tgz#6ec571ef1c2ad8c88724ace826fdaf060dc8076a" @@ -4085,6 +4207,15 @@ "@opentelemetry/sdk-metrics" "^1.9.1" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-mongoose@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.42.0.tgz#375afd21adfcd897a8f521c1ffd2d91e6a428705" + integrity sha512-AnWv+RaR86uG3qNEMwt3plKX1ueRM7AspfszJYVkvkehiicC3bHQA6vWdb6Zvy5HAE14RyFbu9+2hUUjR2NSyg== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-mongoose@^0.32.4": version "0.32.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.32.4.tgz#f421d477264abd46e66d78ac65ca59f98c39b07c" @@ -4094,6 +4225,15 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-mysql2@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.41.0.tgz#6377b6e2d2487fd88e1d79aa03658db6c8d51651" + integrity sha512-REQB0x+IzVTpoNgVmy5b+UnH1/mDByrneimP6sbDHkp1j8QOl1HyWOrBH/6YWR0nrbU3l825Em5PlybjT3232g== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/sql-common" "^0.40.1" + "@opentelemetry/instrumentation-mysql2@^0.33.4": version "0.33.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.33.4.tgz#fd69e38d1b5eefff6e4362fc257e60c5bec5a10f" @@ -4102,6 +4242,15 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-mysql@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.41.0.tgz#2d50691ead5219774bd36d66c35d5b4681485dd7" + integrity sha512-jnvrV6BsQWyHS2qb2fkfbfSb1R/lmYwqEZITwufuRl37apTopswu9izc0b1CYRp/34tUG/4k/V39PND6eyiNvw== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@types/mysql" "2.15.26" + "@opentelemetry/instrumentation-mysql@^0.33.3": version "0.33.3" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.33.3.tgz#1f09c9cd19d971c2d5f142bb83c35f25c72975d9" @@ -4111,6 +4260,14 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/mysql" "2.15.19" +"@opentelemetry/instrumentation-nestjs-core@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.40.0.tgz#2c0e6405b56caaec32747d55c57ff9a034668ea8" + integrity sha512-WF1hCUed07vKmf5BzEkL0wSPinqJgH7kGzOjjMAiTGacofNXjb/y4KQ8loj2sNsh5C/NN7s1zxQuCgbWbVTGKg== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-nestjs-core@^0.32.5": version "0.32.5" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.32.5.tgz#ed4f81651f141a298b3a8d6107f50bc47b66004b" @@ -4127,6 +4284,17 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-pg@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.44.0.tgz#1e97a0aeb2dca068ee23ce75884a0a0063a7ce3f" + integrity sha512-oTWVyzKqXud1BYEGX1loo2o4k4vaU1elr3vPO8NZolrBtFvQ34nx4HgUaexUDuEog00qQt+MLR5gws/p+JXMLQ== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/sql-common" "^0.40.1" + "@types/pg" "8.6.1" + "@types/pg-pool" "2.0.6" + "@opentelemetry/instrumentation-pg@^0.35.3": version "0.35.3" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.35.3.tgz#47b29891e2be797a2148e699bb43094fdf33a281" @@ -4145,6 +4313,15 @@ dependencies: "@opentelemetry/instrumentation" "^0.40.0" +"@opentelemetry/instrumentation-redis-4@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.42.0.tgz#fc01104cfe884c7546385eaae03c57a47edd19d1" + integrity sha512-NaD+t2JNcOzX/Qa7kMy68JbmoVIV37fT/fJYzLKu2Wwd+0NCxt+K2OOsOakA8GVg8lSpFdbx4V/suzZZ2Pvdjg== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/redis-common" "^0.36.2" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-redis-4@^0.34.6": version "0.34.6" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.34.6.tgz#2503f33b60f2cbe3311558fc95037d348e020575" @@ -4224,6 +4401,41 @@ semver "^7.3.2" shimmer "^1.2.1" +"@opentelemetry/instrumentation@0.53.0", "@opentelemetry/instrumentation@^0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.53.0.tgz#e6369e4015eb5112468a4d45d38dcada7dad892d" + integrity sha512-DMwg0hy4wzf7K73JJtl95m/e0boSoWhH07rfvHvYzQtBD3Bmv0Wc1x733vyZBqmFm8OjJD0/pfiUg1W3JjFX0A== + dependencies: + "@opentelemetry/api-logs" "0.53.0" + "@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" + +"@opentelemetry/instrumentation@^0.46.0": + version "0.46.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.46.0.tgz#a8a252306f82e2eace489312798592a14eb9830e" + integrity sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.7.1" + require-in-the-middle "^7.1.1" + semver "^7.5.2" + shimmer "^1.2.1" + +"@opentelemetry/instrumentation@^0.49 || ^0.50 || ^0.51 || ^0.52.0": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz#2e7e46a38bd7afbf03cf688c862b0b43418b7f48" + integrity sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw== + dependencies: + "@opentelemetry/api-logs" "0.52.1" + "@types/shimmer" "^1.0.2" + import-in-the-middle "^1.8.1" + require-in-the-middle "^7.1.1" + semver "^7.5.2" + shimmer "^1.2.1" + "@opentelemetry/otlp-exporter-base@0.34.0": version "0.34.0" resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.34.0.tgz#c6020b63590d4b8ac3833eda345a6f582fa014b1" @@ -4362,6 +4574,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.35.1.tgz#01356f6845d4f9f9fdfd2c4c562a74316d2d24d3" integrity sha512-qLXe7h9VzFLx3LaizFiUlpuohCRyvHlDW5b9synE6omHKTZr/n0EHEdmhp3GezBeAqMGI+q499Mht4SmStaSqQ== +"@opentelemetry/redis-common@^0.36.2": + version "0.36.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz#906ac8e4d804d4109f3ebd5c224ac988276fdc47" + integrity sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g== + "@opentelemetry/resource-detector-alibaba-cloud@^0.27.7": version "0.27.7" resolved "https://registry.yarnpkg.com/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.27.7.tgz#9eccdbf3b70eb891dcbd8f36a8019610ce838254" @@ -4429,6 +4646,14 @@ "@opentelemetry/core" "1.25.0" "@opentelemetry/semantic-conventions" "1.25.0" +"@opentelemetry/resources@1.26.0", "@opentelemetry/resources@^1.25.1": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.26.0.tgz#da4c7366018bd8add1f3aa9c91c6ac59fd503cef" + integrity sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/resources@1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.8.0.tgz#260be9742cf7bceccc0db928d8ca8d64391acfe3" @@ -4561,6 +4786,15 @@ "@opentelemetry/resources" "1.8.0" "@opentelemetry/semantic-conventions" "1.8.0" +"@opentelemetry/sdk-trace-base@^1.22", "@opentelemetry/sdk-trace-base@^1.25.1": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz#0c913bc6d2cfafd901de330e4540952269ae579c" + integrity sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/resources" "1.26.0" + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/sdk-trace-base@^1.23.0": version "1.25.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.0.tgz#263f9ce19001c5cd7a814d0eb40ebc6469ae763d" @@ -4623,11 +4857,23 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.0.tgz#390eb4d42a29c66bdc30066af9035645e9bb7270" integrity sha512-M+kkXKRAIAiAP6qYyesfrC5TOmDpDVtsxuGfPcqd9B/iBrac+E14jYwrgm0yZBUIbIP2OnqC3j+UgkXLm1vxUQ== +"@opentelemetry/semantic-conventions@1.27.0", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.25.1", "@opentelemetry/semantic-conventions@^1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" + integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== + "@opentelemetry/semantic-conventions@1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.8.0.tgz#fe2aa90e6df050a11cd57f5c0f47b0641fd2cad3" integrity sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew== +"@opentelemetry/sql-common@^0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz#93fbc48d8017449f5b3c3274f2268a08af2b83b6" + integrity sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg== + dependencies: + "@opentelemetry/core" "^1.1.0" + "@penseapp/discord-notification@^2.0.9": version "2.0.9" resolved "https://registry.yarnpkg.com/@penseapp/discord-notification/-/discord-notification-2.0.9.tgz#0a52bb2eda2c575e076ee00476fd8dc717023a67" @@ -4758,6 +5004,15 @@ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.10.1.tgz#c7062747f254e5d5fce98a8cae566c25f9f29fb2" integrity sha512-B3tcTxjx196nuAu1GOTKO9cGPUgTFHYRdkPkTS4m5ptb2cejyBlH9X7GOfSt3xlI7p4zAJDshJP4JJivCg9ouA== +"@prisma/instrumentation@5.19.1": + version "5.19.1" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.19.1.tgz#146319cf85f22b7a43296f0f40cfeac55516e66e" + integrity sha512-VLnzMQq7CWroL5AeaW0Py2huiNKeoMfCH3SUxstdzPrlWQi6UQ9UrfcbUkNHlVFqOMacqy8X/8YtE0kuKDpD9w== + dependencies: + "@opentelemetry/api" "^1.8" + "@opentelemetry/instrumentation" "^0.49 || ^0.50 || ^0.51 || ^0.52.0" + "@opentelemetry/sdk-trace-base" "^1.22" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -5360,6 +5615,60 @@ "@sentry/types" "7.106.1" "@sentry/utils" "7.106.1" +"@sentry/cli-darwin@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.35.0.tgz#4bc9a07690f0de75d930ba47f4655f6465191768" + integrity sha512-dRtDaASkB1ncSbCLMIL8bxki4dPMimSdYz74XOUJ5IvDVVzEInEO7PqvyOj/cyafB+1FSNudaZ90ZRvsNN1Maw== + +"@sentry/cli-linux-arm64@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.35.0.tgz#bad8a45b81d2b317f702991783a503f566b2294e" + integrity sha512-NpyVz2lQWWkMa9GZkt0m4cA/wsgYnWOE6Z+4ePUGjbOIG3Ws9DLaHjYxUUYI79kxfbVCp7wLo1S6kOkj+M1Dlw== + +"@sentry/cli-linux-arm@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.35.0.tgz#dacfc219876f5dce3d8c65dab7128ea3e493f561" + integrity sha512-zNL+/HnepZ4/MkIS8wfoUQxSa+k6r0DSSdX1TpDH5436u+3LB5rfCTBfZ624DWHKMoXX+1dI+rWSi+zL8QFMsg== + +"@sentry/cli-linux-i686@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.35.0.tgz#d0e6401b60b0a4b6c3578998995ba6cb31c1bf20" + integrity sha512-vIYwZVqx+kYZdPsenIm+UqjSCKe9Q2Aof6kzrzW0DPR1WyqIWbWG4NbiugiPTiuA1dLjUjYpGP8wyIqb8hxv4w== + +"@sentry/cli-linux-x64@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.35.0.tgz#a1e8e7bff960ed8916b4cc9c0ef75a057e30f989" + integrity sha512-7Wy5QNt6wZ8EaxEbHqP0DEiyUcXRVItRt9jzhpa2nCaawL+fwDOQCjUkHGsdIC+y14UqA+er9CaPCSp8sA6Vaw== + +"@sentry/cli-win32-i686@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.35.0.tgz#c1b090f7c740c5b22d1019ca48a84f58cd4b2670" + integrity sha512-XDcBUtO5A9elH+xgFNs6NBjkMBnz0sZLo5DU7LE77qKXULnlLeJ63eZD1ukQIRPvxEDsIEPOllRweLuAlUMDtw== + +"@sentry/cli-win32-x64@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.35.0.tgz#f592af483da239be846e556f57f5c6fc7dc1dc54" + integrity sha512-86yHO+31qAXUeAdSCH7MNodn/cn/9xd2fTrxjtfNZWO0pX0jW91sCdomfBxhu5b977cyV9gNcqeBbc9XSIKIIA== + +"@sentry/cli@^2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.35.0.tgz#5514eb8f5808bc70707ffa186156f8ff7ca5971e" + integrity sha512-7sHRJViEgHTfEXf+HD1Fb2cwmnxlILmb2NNxghP2vvrgC2PhuwuJU7AX4zg7HjJgxH9HBmnn4AJskDujaJ/6cQ== + dependencies: + https-proxy-agent "^5.0.0" + node-fetch "^2.6.7" + progress "^2.0.3" + proxy-from-env "^1.1.0" + which "^2.0.2" + optionalDependencies: + "@sentry/cli-darwin" "2.35.0" + "@sentry/cli-linux-arm" "2.35.0" + "@sentry/cli-linux-arm64" "2.35.0" + "@sentry/cli-linux-i686" "2.35.0" + "@sentry/cli-linux-x64" "2.35.0" + "@sentry/cli-win32-i686" "2.35.0" + "@sentry/cli-win32-x64" "2.35.0" + "@sentry/core@7.106.1": version "7.106.1" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.106.1.tgz#d4470f2a2f8e0c24e3afe4d7f83f98f5d2858605" @@ -5368,15 +5677,60 @@ "@sentry/types" "7.106.1" "@sentry/utils" "7.106.1" -"@sentry/node@^7.12.0": - version "7.106.1" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.106.1.tgz#8728fd79b8575ef0678a3a47f928c2830c9c9b6e" - integrity sha512-KB2Lkb9WqocD/gbKIZCk2mQQmx+G3khI9ChJUw6GJzd5mvdQ5xxr4/yQKIHR6L9z3oGWBno9/Xc8Aw9s3ZwGwQ== +"@sentry/core@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.29.0.tgz#52032ece2d7b60f3775f10189c27e26b1cebdbca" + integrity sha512-scMbZaJ0Ov8NPgWn86EdjhyTLrhvRVbTxjg0imJAvhIvRbblH3xyqye/17Qnk2fOp8TNDOl7TBZHi0NCFQ5HUw== + dependencies: + "@sentry/types" "8.29.0" + "@sentry/utils" "8.29.0" + +"@sentry/node@^8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.29.0.tgz#6e462b8802356a630c56733dc795a4035464c4ab" + integrity sha512-RCKpWR6DUWmlxtms10MRXwJZRrFt1a2P38FjwEEahcdcK1R6wB8GPf0GO4JnJAiw6oeM0MERSqLIcSLT8+FxtA== + dependencies: + "@opentelemetry/api" "^1.9.0" + "@opentelemetry/context-async-hooks" "^1.25.1" + "@opentelemetry/core" "^1.25.1" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-connect" "0.39.0" + "@opentelemetry/instrumentation-express" "0.42.0" + "@opentelemetry/instrumentation-fastify" "0.39.0" + "@opentelemetry/instrumentation-fs" "0.15.0" + "@opentelemetry/instrumentation-generic-pool" "0.39.0" + "@opentelemetry/instrumentation-graphql" "0.43.0" + "@opentelemetry/instrumentation-hapi" "0.41.0" + "@opentelemetry/instrumentation-http" "0.53.0" + "@opentelemetry/instrumentation-ioredis" "0.43.0" + "@opentelemetry/instrumentation-koa" "0.43.0" + "@opentelemetry/instrumentation-mongodb" "0.47.0" + "@opentelemetry/instrumentation-mongoose" "0.42.0" + "@opentelemetry/instrumentation-mysql" "0.41.0" + "@opentelemetry/instrumentation-mysql2" "0.41.0" + "@opentelemetry/instrumentation-nestjs-core" "0.40.0" + "@opentelemetry/instrumentation-pg" "0.44.0" + "@opentelemetry/instrumentation-redis-4" "0.42.0" + "@opentelemetry/resources" "^1.25.1" + "@opentelemetry/sdk-trace-base" "^1.25.1" + "@opentelemetry/semantic-conventions" "^1.25.1" + "@prisma/instrumentation" "5.19.1" + "@sentry/core" "8.29.0" + "@sentry/opentelemetry" "8.29.0" + "@sentry/types" "8.29.0" + "@sentry/utils" "8.29.0" + import-in-the-middle "^1.11.0" + optionalDependencies: + opentelemetry-instrumentation-fetch-node "1.2.3" + +"@sentry/opentelemetry@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.29.0.tgz#6ae54c640155925fc42ac86f37d125f9bb983794" + integrity sha512-MtfjDMUuKFYlyw9hZohp9xnphz+6QosyHb2zCV3e/fANoA53FDxmg/Co7haMohUCiOwwJPytUmSQQaP0nyL2Uw== dependencies: - "@sentry-internal/tracing" "7.106.1" - "@sentry/core" "7.106.1" - "@sentry/types" "7.106.1" - "@sentry/utils" "7.106.1" + "@sentry/core" "8.29.0" + "@sentry/types" "8.29.0" + "@sentry/utils" "8.29.0" "@sentry/tracing@^7.12.0": version "7.106.1" @@ -5390,6 +5744,11 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.106.1.tgz#ea7c6b9090790e600c4f907f1c8bf94c8db2ecc4" integrity sha512-g3OcyAHGugBwkQP4fZYCCZqF2ng9K7yQc9FVngKq/y7PwHm84epXdYYGDGgfQOIC1d5/GMaPxmzI5IIrZexzkg== +"@sentry/types@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.29.0.tgz#c19e43524b8e7766028f4da8f02eddcc33518541" + integrity sha512-j4gX3ctzgD4xVWllXAhm6M+kHFEvrFoUPFq60X/pgkjsWCocGuhtNfB0rW43ICG8hCnlz8IYl7O7b8V8qY7SPg== + "@sentry/utils@7.106.1": version "7.106.1" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.106.1.tgz#7761ed50d489286dd1e482570c498f675445ce82" @@ -5397,6 +5756,13 @@ dependencies: "@sentry/types" "7.106.1" +"@sentry/utils@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.29.0.tgz#d4a36643369e30ba62ef8f40f149420a100f64bb" + integrity sha512-nb93/m3SjQChQJFqJj3oNW3Rz/12yrT7jypTCire3c2hpYWG2uR5n8VY9UUMTA6HLNvdom6tckK7p3bXGXlF0w== + dependencies: + "@sentry/types" "8.29.0" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -6711,6 +7077,13 @@ dependencies: "@types/node" "*" +"@types/connect@3.4.36": + version "3.4.36" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.36.tgz#e511558c15a39cb29bd5357eebb57bd1459cd1ab" + integrity sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w== + dependencies: + "@types/node" "*" + "@types/content-disposition@*": version "0.5.8" resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.8.tgz#6742a5971f490dc41e59d277eee71361fea0b537" @@ -7034,6 +7407,13 @@ dependencies: "@types/node" "*" +"@types/mysql@2.15.26": + version "2.15.26" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.26.tgz#f0de1484b9e2354d587e7d2bd17a873cc8300836" + integrity sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@>=10.0.0", "@types/node@>=13.7.0", "@types/node@^20.10.4": version "20.11.27" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.27.tgz#debe5cfc8a507dd60fe2a3b4875b1604f215c2ac" @@ -7089,6 +7469,13 @@ dependencies: "@types/pg" "*" +"@types/pg-pool@2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.6.tgz#1376d9dc5aec4bb2ec67ce28d7e9858227403c77" + integrity sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ== + dependencies: + "@types/pg" "*" + "@types/pg@*": version "8.11.2" resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.11.2.tgz#e5c306601d2e0cc54c0801cc61a41761c8a95c92" @@ -7191,6 +7578,11 @@ resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.5.tgz#491d8984d4510e550bfeb02d518791d7f59d2b88" integrity sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww== +"@types/shimmer@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.2.0.tgz#9b706af96fa06416828842397a70dfbbf1c14ded" + integrity sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg== + "@types/superagent@*": version "8.1.4" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.4.tgz#f8290d1b7d6081f84f3047851c190c4a3c8cdb21" @@ -7532,6 +7924,11 @@ acorn-import-assertions@^1.9.0: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -8726,6 +9123,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" +cjs-module-lexer@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== + classic-level@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.4.1.tgz#169ecf9f9c6200ad42a98c8576af449c1badbaee" @@ -9367,6 +9769,13 @@ debug@^4.3.3: dependencies: ms "2.1.2" +debug@^4.3.5: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -11538,6 +11947,26 @@ import-in-the-middle@1.3.5: dependencies: module-details-from-path "^1.0.3" +import-in-the-middle@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz#3e111ff79c639d0bde459bd7ba29dd9fdf357364" + integrity sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +import-in-the-middle@^1.11.0, import-in-the-middle@^1.8.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.0.tgz#a94c4925b8da18256cde3b3b7b38253e6ca5e708" + integrity sha512-5DimNQGoe0pLUHbR9qK84iWaWjjbsxiqXnw6Qz64+azRgleqv9k2kTt5fw7QsOpmaGYtuxxursnPPsnTKEx10Q== + dependencies: + acorn "^8.8.2" + acorn-import-attributes "^1.9.5" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + import-meta-resolve@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz#0b1195915689f60ab00f830af0f15cc841e8919e" @@ -13515,7 +13944,7 @@ node-domexception@^1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.8, node-fetch@^2.6.9: +node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.8, node-fetch@^2.6.9: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -13815,6 +14244,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +opentelemetry-instrumentation-fetch-node@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/opentelemetry-instrumentation-fetch-node/-/opentelemetry-instrumentation-fetch-node-1.2.3.tgz#beb24048bdccb1943ba2a5bbadca68020e448ea7" + integrity sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A== + dependencies: + "@opentelemetry/instrumentation" "^0.46.0" + "@opentelemetry/semantic-conventions" "^1.17.0" + opentracing@^0.14.4: version "0.14.7" resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.7.tgz#25d472bd0296dc0b64d7b94cbc995219031428f5" @@ -14470,7 +14907,7 @@ progress-events@^1.0.0: resolved "https://registry.yarnpkg.com/progress-events/-/progress-events-1.0.0.tgz#34f5e8fdb5dae3561837b22672d1e02277bb2109" integrity sha512-zIB6QDrSbPfRg+33FZalluFIowkbV5Xh1xSuetjG+rlC5he6u2dc6VQJ0TbMdlN3R1RHdpOqxEFMKTnQ+itUwA== -progress@^2.0.0: +progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -15007,6 +15444,15 @@ require-in-the-middle@^7.1.0: module-details-from-path "^1.0.3" resolve "^1.22.1" +require-in-the-middle@^7.1.1: + version "7.4.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.4.0.tgz#606977820d4b5f9be75e5a108ce34cfed25b3bb4" + integrity sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ== + dependencies: + debug "^4.3.5" + module-details-from-path "^1.0.3" + resolve "^1.22.8" + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -15042,7 +15488,7 @@ resolve-global@1.0.0, resolve-global@^1.0.0: dependencies: global-dirs "^0.1.1" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.22.8: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -15283,6 +15729,11 @@ semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.8, semve dependencies: lru-cache "^6.0.0" +semver@^7.5.2: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + semver@~7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -15756,16 +16207,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -15838,7 +16280,7 @@ stringify-object@3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -15852,13 +16294,6 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -16894,7 +17329,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -16912,15 +17347,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 2c9efb1e93f4fd0e5737f91dd42075bc742bddb2 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Mon, 9 Sep 2024 15:45:27 +0200 Subject: [PATCH 02/35] fix env --- .github/workflows/build-server.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index 31341a659..5b9558ebe 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -23,6 +23,7 @@ env: AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} CONTAINER_IMAGE: desci-server DOCKER_BUILDKIT: 1 From 633a2a36def96c16d0ebe35a61199ad2c8adb4be Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Mon, 9 Sep 2024 15:54:43 +0200 Subject: [PATCH 03/35] add sentry build step --- .github/workflows/build-server.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index 5b9558ebe..c4676434d 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -58,6 +58,10 @@ jobs: echo "{\"proxies\":[{\"address\":\"\"}]}" > desci-server/src/desci-contracts-config/unknown-research-object.json echo "{\"proxies\":[{\"address\":\"\"}]}" > desci-server/src/desci-contracts-config/unknown-dpid.json + - name: Set up Sentry CLI + run: | + sentry-cli --version + - name: Run tests run: | cd desci-server && export DOCKER_BUILDKIT=1 && yarn --ignore-engines && yarn test @@ -114,6 +118,10 @@ jobs: echo "{\"proxies\":[{\"address\":\"\"}]}" > desci-server/src/desci-contracts-config/unknown-research-object.json echo "{\"proxies\":[{\"address\":\"\"}]}" > desci-server/src/desci-contracts-config/unknown-dpid.json + - name: Set up Sentry CLI + run: | + sentry-cli --version + - name: Build and tag the image (DEV) if: github.ref == 'refs/heads/develop' run: | From b1b957a6b547881b3f6f152404683485950d66c9 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 10 Sep 2024 01:53:35 +0200 Subject: [PATCH 04/35] add sentry build step --- .github/workflows/build-and-test.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 567ab7bc8..b355ee139 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -55,6 +55,10 @@ jobs: echo "{\"proxies\":[{\"address\":\"\"}]}" > desci-server/src/desci-contracts-config/unknown-research-object.json echo "{\"proxies\":[{\"address\":\"\"}]}" > desci-server/src/desci-contracts-config/unknown-dpid.json + - name: Set up Sentry CLI + run: | + sentry-cli --version + - name: Run tests run: | cd desci-server && export DOCKER_BUILDKIT=1 && yarn --ignore-engines && yarn test From dd604cdd948413d039a728cff381ac5844a49a5c Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 10 Sep 2024 02:06:40 +0200 Subject: [PATCH 05/35] add sentry build step --- .github/workflows/build-and-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index b355ee139..a18e84a65 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -57,6 +57,7 @@ jobs: - name: Set up Sentry CLI run: | + npm install -g @sentry/cli sentry-cli --version - name: Run tests From 53387638a0fc369a446a14f24c35db557f4f7277 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Wed, 11 Sep 2024 17:38:33 +0200 Subject: [PATCH 06/35] sentry cli token --- .github/workflows/build-and-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index a18e84a65..ea996bc75 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -22,6 +22,7 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} CONTAINER_IMAGE: desci-server DOCKER_BUILDKIT: 1 + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} jobs: build-and-test: From 456c62bfa12dac218ae02b20b29da1edd29edf45 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Wed, 11 Sep 2024 20:19:21 +0200 Subject: [PATCH 07/35] sentry cli token --- .github/workflows/build-and-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index ea996bc75..9b3eb3d9e 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -60,6 +60,7 @@ jobs: run: | npm install -g @sentry/cli sentry-cli --version + sentry-cli login --auth-token $SENTRY_AUTH_TOKEN - name: Run tests run: | From 34148db0e2e7d3c6c6a00ce48c0841b95e78445a Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Sun, 15 Sep 2024 18:13:56 +0200 Subject: [PATCH 08/35] dont autorun sentry on yarn build --- desci-server/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desci-server/package.json b/desci-server/package.json index 7077dab2d..abbcf4d5f 100755 --- a/desci-server/package.json +++ b/desci-server/package.json @@ -25,7 +25,7 @@ "script:seed-social-data": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/seed-social-data.ts", "script:DESTRUCTIVE-clear-social-data": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/DESTRUCTIVE-clear-social-data.ts", "script:seed-community-member": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/seed-community-members.ts", - "build": "rimraf dist && tsc && yarn copy-files && yarn sentry:sourcemaps", + "build": "rimraf dist && tsc && yarn copy-files", "copy-files": "copyfiles -u 1 src/**/*.cjs dist/", "generate": "npx prisma generate", "migrate:local": "DATABASE_URL=postgresql://walter:white@localhost:5433/boilerplate npx prisma migrate dev", @@ -184,4 +184,4 @@ "prisma": { "seed": "node --no-warnings=ExperimentalWarning --loader ts-node/esm prisma/seed.ts" } -} \ No newline at end of file +} From 3bcf7969332cfca674053d94b74e7c9ab11f39e5 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 17 Sep 2024 00:38:41 +0200 Subject: [PATCH 09/35] run sentry? --- .github/workflows/build-server.yaml | 1 + desci-server/package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index c4676434d..b25936ff2 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -121,6 +121,7 @@ jobs: - name: Set up Sentry CLI run: | sentry-cli --version + sentry-cli login --auth-token $SENTRY_AUTH_TOKEN - name: Build and tag the image (DEV) if: github.ref == 'refs/heads/develop' diff --git a/desci-server/package.json b/desci-server/package.json index abbcf4d5f..739b8e4aa 100755 --- a/desci-server/package.json +++ b/desci-server/package.json @@ -25,7 +25,7 @@ "script:seed-social-data": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/seed-social-data.ts", "script:DESTRUCTIVE-clear-social-data": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/DESTRUCTIVE-clear-social-data.ts", "script:seed-community-member": "debug=* node --no-warnings --enable-source-maps --loader ts-node/esm ./src/scripts/seed-community-members.ts", - "build": "rimraf dist && tsc && yarn copy-files", + "build": "rimraf dist && tsc && yarn copy-files; if [ \"$SENTRY_AUTH_TOKEN\" ]; then yarn sentry:sourcemaps; else echo 'SENTRY_AUTH_TOKEN not set, sourcemaps will not upload'; fi", "copy-files": "copyfiles -u 1 src/**/*.cjs dist/", "generate": "npx prisma generate", "migrate:local": "DATABASE_URL=postgresql://walter:white@localhost:5433/boilerplate npx prisma migrate dev", @@ -51,7 +51,7 @@ "podman:dev": "podman-compose --file docker-compose.yml --file docker-compose.dev.yml up --build", "email-dev": "email dev --dir ./src/templates/emails --port 3777", "check-deps": "npx npm-check", - "sentry:sourcemaps": "sentry-cli sourcemaps inject --org desci-labs --project nodes-backend ./dist && sentry-cli sourcemaps upload --org desci-labs --project nodes-backend ./dist" + "sentry:sourcemaps": "sentry-cli sourcemaps inject --org desci-labs --project nodes-backend ./dist && sentry-cli sourcemaps upload --org desci-labs --project nodes-backend ./dist && echo 'Sentry sourcemaps uploaded'" }, "dependencies": { "@automerge/automerge-repo": "^1.0.19", From 8c055543a8342c8cea16e244a668e4c7714e9bed Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 17 Sep 2024 00:46:42 +0200 Subject: [PATCH 10/35] echo sentrytoken to env for build --- .github/workflows/build-and-test.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 9b3eb3d9e..181bf3e8e 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -61,6 +61,8 @@ jobs: npm install -g @sentry/cli sentry-cli --version sentry-cli login --auth-token $SENTRY_AUTH_TOKEN + # append to .env + echo "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> .env - name: Run tests run: | From 6594d3ce8596fb20179c5e5b3a85b43b4b73fdb8 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 17 Sep 2024 00:54:04 +0200 Subject: [PATCH 11/35] fix newline --- .github/workflows/build-and-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 181bf3e8e..75808b2e8 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -62,7 +62,7 @@ jobs: sentry-cli --version sentry-cli login --auth-token $SENTRY_AUTH_TOKEN # append to .env - echo "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> .env + echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> .env - name: Run tests run: | From 2c3fddde39f3b2928a5637e8af006178cdfe500b Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 17 Sep 2024 01:02:24 +0200 Subject: [PATCH 12/35] add sentry token to inner env --- .github/workflows/build-and-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 75808b2e8..7b4a08242 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -63,6 +63,7 @@ jobs: sentry-cli login --auth-token $SENTRY_AUTH_TOKEN # append to .env echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> .env + echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> ./desci-server/.env - name: Run tests run: | From 8c14ce63e5167c64c615c8a680af8caec90a6fea Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 17 Sep 2024 01:14:48 +0200 Subject: [PATCH 13/35] test build --- .github/workflows/build-and-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 7b4a08242..ae1f22605 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -63,7 +63,7 @@ jobs: sentry-cli login --auth-token $SENTRY_AUTH_TOKEN # append to .env echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> .env - echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> ./desci-server/.env + yarn build - name: Run tests run: | From ddc75324ded90170eedd041acab4b748817d9e96 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 17 Sep 2024 01:19:42 +0200 Subject: [PATCH 14/35] test build --- .github/workflows/build-and-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index ae1f22605..179b4ac83 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -62,8 +62,8 @@ jobs: sentry-cli --version sentry-cli login --auth-token $SENTRY_AUTH_TOKEN # append to .env - echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> .env - yarn build + echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> desci-server/.env + cd desci-server && yarn build - name: Run tests run: | From d3c4daa637f401280acd1d0fdb1a8191ec53a7c4 Mon Sep 17 00:00:00 2001 From: kadami <86646883+kadamidev@users.noreply.github.com> Date: Fri, 20 Sep 2024 20:52:29 +0000 Subject: [PATCH 15/35] fix check on nested fields for filtering --- .../src/services/ElasticSearchService.ts | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/desci-server/src/services/ElasticSearchService.ts b/desci-server/src/services/ElasticSearchService.ts index 492369b8f..100732aa3 100644 --- a/desci-server/src/services/ElasticSearchService.ts +++ b/desci-server/src/services/ElasticSearchService.ts @@ -61,6 +61,8 @@ export const RELEVANT_FIELDS = { ], }; +const NESTED_WORKS_ENTITIES = ['authors', 'best_locations', 'concepts', 'locations', 'topics', 'sources']; + type SortOrder = 'asc' | 'desc'; type SortField = { [field: string]: { order: SortOrder; missing?: string | number; type?: string; script?: any } }; @@ -192,7 +194,7 @@ function buildFilter(filter: Filter) { }; case 'term': const termFieldParts = filter.field.split('.'); - const isNested = termFieldParts.length > 1; + const isNested = NESTED_WORKS_ENTITIES.includes(termFieldParts[0]); const queryType = Array.isArray(filter.value) ? 'terms' : 'term'; let valFormatted = filter.value; if (Array.isArray(valFormatted)) { @@ -213,15 +215,24 @@ function buildFilter(filter: Filter) { } return query; case 'match_phrase': + const fieldParts = filter.field.split('.'); + const isNestedMatchPhrase = NESTED_WORKS_ENTITIES.includes(fieldParts[0]); + if (Array.isArray(filter.value)) { - const queries = filter.value.map((value) => ({ - nested: { - path: filter.field.split('.')[0], - query: { - match_phrase: { [filter.field]: { query: value, analyzer: 'edge_ngram_analyzer' } }, - }, - }, - })); + const queries = filter.value.map((value) => { + if (isNestedMatchPhrase) { + return { + nested: { + path: fieldParts[0], + query: { + match_phrase: { [filter.field]: { query: value, analyzer: 'edge_ngram_analyzer' } }, + }, + }, + }; + } else { + return { match_phrase: { [filter.field]: { query: value, analyzer: 'edge_ngram_analyzer' } } }; + } + }); return { bool: { @@ -231,8 +242,7 @@ function buildFilter(filter: Filter) { }; } - const fieldParts = filter.field.split('.'); - if (fieldParts.length > 1) { + if (isNestedMatchPhrase) { return { nested: { path: fieldParts[0], @@ -242,6 +252,7 @@ function buildFilter(filter: Filter) { }, }; } + return { match_phrase: { [filter.field]: { query: filter.value, analyzer: 'edge_ngram_analyzer' } } }; case 'match': const matchQuery = { From 0137f4c887b05bc1fdd29aaf252da443298efec1 Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Sat, 21 Sep 2024 01:23:30 -0500 Subject: [PATCH 16/35] add community control plane(admin) routes, control plane route data validation and schemas, controllers and implement createCommunity api --- .../controllers/admin/communities/index.ts | 65 ++++++++++ desci-server/src/core/ApiResponse.ts | 9 +- desci-server/src/middleware/validator.ts | 3 +- desci-server/src/routes/v1/admin.ts | 18 --- .../src/routes/v1/admin/communities/index.ts | 113 ++++++++++++++++++ .../src/routes/v1/admin/communities/schema.ts | 66 ++++++++++ desci-server/src/routes/v1/admin/index.ts | 22 ++++ desci-server/src/routes/v1/index.ts | 2 +- desci-server/src/services/Communities.ts | 18 ++- desci-server/src/services/data/processing.ts | 35 +++++- desci-server/src/types/ProcessEnv.d.ts | 1 + 11 files changed, 327 insertions(+), 25 deletions(-) create mode 100644 desci-server/src/controllers/admin/communities/index.ts delete mode 100644 desci-server/src/routes/v1/admin.ts create mode 100644 desci-server/src/routes/v1/admin/communities/index.ts create mode 100644 desci-server/src/routes/v1/admin/communities/schema.ts create mode 100644 desci-server/src/routes/v1/admin/index.ts diff --git a/desci-server/src/controllers/admin/communities/index.ts b/desci-server/src/controllers/admin/communities/index.ts new file mode 100644 index 000000000..eab4bb6bd --- /dev/null +++ b/desci-server/src/controllers/admin/communities/index.ts @@ -0,0 +1,65 @@ +import { NextFunction, Request, Response } from 'express'; +import { z } from 'zod'; + +import { + asyncMap, + BadRequestError, + communityService, + logger as parentLogger, + SuccessMessageResponse, + SuccessResponse, +} from '../../../internal.js'; +import { addCommunitySchema } from '../../../routes/v1/admin/communities/schema.js'; +import { processUploadToIpfs } from '../../../services/data/processing.js'; + +const logger = parentLogger.child({ module: 'Admin/Communities/controller' }); + +export const todoApi = async (_req: Request, res: Response, next: NextFunction) => { + new SuccessMessageResponse().send(res); +}; + +export const createCommunity = async (req: Request, res: Response, _next: NextFunction) => { + const body = req.body as Required['body']>; + + let assets: { key: string; url: string }[]; + if (req.files) { + let uploads = Array.isArray(req.files) ? req.files : Object.values(req.files).map((files) => files[0]); + uploads = uploads.map((file) => { + file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; + return file; + }); + const { ok, value } = await processUploadToIpfs({ files: uploads }); + if (ok) { + assets = value.map((ipfsImg) => ({ + key: ipfsImg.path, + url: `${process.env.IPFS_RESOLVER_OVERRIDE}/${ipfsImg.cid}`, + })); + } else { + throw new BadRequestError('Could not upload file to ipfs'); + } + } + + const image_url = body.imageUrl || assets.find((img) => img.key.toLowerCase().includes('imageurl'))?.url; + delete body.imageUrl; + + if (!image_url) throw new BadRequestError('No community logo uploaded'); + + // logger.info({ ...body, image_url }, 'payload'); + const community = await communityService.createCommunity({ ...body, image_url }); + new SuccessResponse(community).send(res); +}; + +export const listAllCommunities = async (_req: Request, res: Response, next: NextFunction) => { + const communities = await communityService.adminGetCommunities(); + logger.info({ communities }, 'List communities'); + const data = await asyncMap(communities, async (community) => { + const engagements = await communityService.getCommunityEngagementSignals(community.id); + const verifiedEngagements = await communityService.getCommunityRadarEngagementSignal(community.id); + return { + ...community, + engagements, + verifiedEngagements, + }; + }); + new SuccessResponse(data).send(res); +}; diff --git a/desci-server/src/core/ApiResponse.ts b/desci-server/src/core/ApiResponse.ts index 82a192182..238621108 100644 --- a/desci-server/src/core/ApiResponse.ts +++ b/desci-server/src/core/ApiResponse.ts @@ -1,5 +1,7 @@ import { Response } from 'express'; +import { logger } from '../logger.js'; + enum ResponseStatus { SUCCESS = 200, BAD_REQUEST = 400, @@ -24,7 +26,8 @@ export abstract class ApiResponse { protected prepare(res: Response, response: T, headers: { [key: string]: string }): Response { for (const [key, value] of Object.entries(headers)) res.append(key, value); - return res.status(this.status).json(ApiResponse.sanitize(response)); + const data = ApiResponse.sanitize(response); + return data ? res.status(this.status).json(data) : res.status(this.status).send(); } public send(res: Response, headers: Headers = {}): Response { @@ -35,8 +38,8 @@ export abstract class ApiResponse { const clone: T = {} as T; Object.assign(clone, response); delete clone.status; - for (const field in clone) if (clone[field] === 'undefined') delete clone[field]; - // if (!clone['message']) delete clone.message; + for (const field in clone) if (!clone[field]) delete clone[field]; + if (Object.keys(clone).length === 0) return undefined; return clone; } } diff --git a/desci-server/src/middleware/validator.ts b/desci-server/src/middleware/validator.ts index 2acafc15e..88b6a1f4f 100644 --- a/desci-server/src/middleware/validator.ts +++ b/desci-server/src/middleware/validator.ts @@ -1,7 +1,7 @@ import { NextFunction, Request, Response } from 'express'; import { ZodError, z } from 'zod'; -import { BadRequestError, InternalError, asyncHandler } from '../internal.js'; +import { BadRequestError, InternalError, asyncHandler, logger } from '../internal.js'; export const validate = (schema: z.ZodObject) => asyncHandler(async (req: Request, res: Response, next: NextFunction) => { @@ -9,6 +9,7 @@ export const validate = (schema: z.ZodObject) => await schema.parseAsync(req); next(); } catch (err) { + // logger.error({ body: req.body, file: req.file }, 'zod validator'); if (err instanceof ZodError) { console.log('Error', err); throw new BadRequestError( diff --git a/desci-server/src/routes/v1/admin.ts b/desci-server/src/routes/v1/admin.ts deleted file mode 100644 index 35b3c37a5..000000000 --- a/desci-server/src/routes/v1/admin.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Router } from 'express'; - -import { createCsv, getAnalytics } from '../../controllers/admin/analytics.js'; -import { debugAllNodesHandler, debugNodeHandler } from '../../controllers/admin/debug.js'; -import { listDoiRecords } from '../../internal.js'; -import { ensureAdmin } from '../../middleware/ensureAdmin.js'; -import { ensureUser } from '../../middleware/permissions.js'; - -const router = Router(); - -router.get('/analytics', [ensureUser, ensureAdmin], getAnalytics); -router.get('/analytics/csv', [ensureUser, ensureAdmin], createCsv); -router.get('/doi/list', [ensureUser, ensureAdmin], listDoiRecords); - -router.get('/debug', [ensureUser, ensureAdmin], debugAllNodesHandler); -router.get('/debug/:uuid', [ensureUser, ensureAdmin], debugNodeHandler); - -export default router; diff --git a/desci-server/src/routes/v1/admin/communities/index.ts b/desci-server/src/routes/v1/admin/communities/index.ts new file mode 100644 index 000000000..9d179c1e6 --- /dev/null +++ b/desci-server/src/routes/v1/admin/communities/index.ts @@ -0,0 +1,113 @@ +import { NextFunction, Response, Router } from 'express'; +import { Request } from 'express'; +import multer from 'multer'; +import multerS3 from 'multer-s3'; + +import { createCommunity, listAllCommunities, todoApi } from '../../../../controllers/admin/communities/index.js'; +import { asyncHandler, ensureAdmin, ensureUser, logger as parentLogger, validate } from '../../../../internal.js'; +import { isS3Configured, s3Client } from '../../../../services/s3.js'; + +import { + addAttestationSchema, + addCommunitySchema, + addEntryAttestationSchema, + addMemberSchema, + removeEntryAttestationSchema, + removeMemberSchema, +} from './schema.js'; + +const logger = parentLogger.child({ module: 'Admin/communities' }); +const router = Router(); + +const upload = isS3Configured + ? multer({ + preservePath: true, + storage: multerS3({ + s3: s3Client, + bucket: process.env.AWS_S3_BUCKET_NAME, + key: (req, file, cb) => { + // const userId = (req as any).user.id; + const { name, communitySlug } = (req as any).body; + if (!name || !name) { + cb(new Error('Missing required params to form key')); + } + const key = `${communitySlug}${name ? +'/' + name : ''}/${file.filename}`; // adjust for dir uploads, doesn't start with '/' + logger.info({ fileName: key }, 'Upload asset'); + cb(null, key); + }, + }), + }) + : multer({ preservePath: true }); + +const uploadHandler = upload.fields([{ name: 'imageUrl', maxCount: 1 }, { name: 'verifiedImageUrl' }]); + +const wrappedHandler = (req: Request, res: Response, next: NextFunction) => { + uploadHandler(req, res, (err) => { + if (err) { + if (err instanceof multer.MulterError) { + throw err; + } else { + logger.error({ err }, 'Upload Handler Error encountered'); + res.status(401).send({ msg: 'unauthorized' }); + return; + } + } + next(); + }); +}; + +const sanitizeBody = async (req: Request, _res: Response, next: NextFunction) => { + logger.info({ body: req.body }, 'sanitizeBody'); + req.body?.keywords ? (req.body.keywords = JSON.parse(req.body.keywords)) : null; + req.body?.links ? (req.body.links = JSON.parse(req.body.links)) : null; + next(); +}; + +router.get('/', [ensureUser, ensureAdmin], asyncHandler(listAllCommunities)); + +// todo: api to create desci community +router.post( + '/', + [ensureUser, ensureAdmin, /* upload.single('image'),*/ wrappedHandler, sanitizeBody, validate(addCommunitySchema)], + asyncHandler(createCommunity), +); + +// todo: api to update desci community +router.post( + ':communityId/', + [ensureUser, ensureAdmin, validate(addCommunitySchema), wrappedHandler], + asyncHandler(todoApi), +); + +// todo: api to create attestation for desci community ( with option to add it as an entryAttestation) +router.post( + ':communityId/attestations', + [ensureUser, ensureAdmin, validate(addAttestationSchema), wrappedHandler], + asyncHandler(todoApi), +); + +// todo: api to add a desci community member +router.post(':communityId/members', [ensureUser, ensureAdmin, validate(addMemberSchema)], asyncHandler(todoApi)); + +// todo: api to remove a desci community member +router.delete( + ':communityId/members/:memberId', + [ensureUser, ensureAdmin, validate(removeMemberSchema)], + asyncHandler(todoApi), +); + +// todo: api to link attestation to community (this adds it to the communityEntryAttestation) +router.post( + ':communityId/addEntryAttestation', + [ensureUser, ensureAdmin, validate(addEntryAttestationSchema)], + asyncHandler(todoApi), +); + +// todo: api to remove attestation as required in for community (remove communityEntryAttestation) +router.post( + ':communityId/removeEntryAttestation', + [ensureUser, ensureAdmin, validate(removeEntryAttestationSchema)], + asyncHandler(todoApi), +); + +export default router; diff --git a/desci-server/src/routes/v1/admin/communities/schema.ts b/desci-server/src/routes/v1/admin/communities/schema.ts new file mode 100644 index 000000000..8d9480eef --- /dev/null +++ b/desci-server/src/routes/v1/admin/communities/schema.ts @@ -0,0 +1,66 @@ +import { CommunityMembershipRole } from '@prisma/client'; +import { z } from 'zod'; + +export const addCommunitySchema = z.object({ + body: z.object({ + name: z.string(), + subtitle: z.string().min(1, 'Subtitle cannot be empty'), + description: z.string().min(1, 'Description cannot be empty'), + hidden: z.boolean().default(false), + keywords: z.array(z.string()).min(1, 'Community must have at least one keyword'), + imageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", + slug: z.string().min(3), + links: z.array(z.string().url()), + }), +}); + +export const addAttestationSchema = z.object({ + params: z.object({ + communityId: z.coerce.number(), + }), + body: z.object({ + name: z.string(), + communitySlug: z.string(), + description: z.string(), + imageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", + verifiedImageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", + protected: z.boolean().default(false), + }), +}); + +export const addMemberSchema = z.object({ + params: z.object({ + communityId: z.coerce.number(), + }), + body: z.object({ + userId: z.coerce.number(), + role: z.enum([CommunityMembershipRole.ADMIN, CommunityMembershipRole.MEMBER]), + }), +}); + +export const removeMemberSchema = z.object({ + params: z.object({ + communityId: z.coerce.number(), + }), + body: z.object({ + memberId: z.coerce.number(), + }), +}); + +export const addEntryAttestationSchema = z.object({ + params: z.object({ + communityId: z.coerce.number(), + }), + body: z.object({ + attestationId: z.coerce.number(), + }), +}); + +export const removeEntryAttestationSchema = z.object({ + params: z.object({ + communityId: z.coerce.number(), + }), + body: z.object({ + attestationId: z.coerce.number(), + }), +}); diff --git a/desci-server/src/routes/v1/admin/index.ts b/desci-server/src/routes/v1/admin/index.ts new file mode 100644 index 000000000..561a9e111 --- /dev/null +++ b/desci-server/src/routes/v1/admin/index.ts @@ -0,0 +1,22 @@ +import { Router } from 'express'; + +import { createCsv, getAnalytics } from '../../../controllers/admin/analytics.js'; +import { debugAllNodesHandler, debugNodeHandler } from '../../../controllers/admin/debug.js'; +import { listDoiRecords } from '../../../internal.js'; +import { ensureAdmin } from '../../../middleware/ensureAdmin.js'; +import { ensureUser } from '../../../middleware/permissions.js'; + +import communities from './communities/index.js'; + +const router = Router(); + +router.get('/analytics', [ensureUser, ensureAdmin], getAnalytics); +router.get('/analytics/csv', [ensureUser, ensureAdmin], createCsv); +router.get('/doi/list', [ensureUser, ensureAdmin], listDoiRecords); + +router.get('/debug', [ensureUser, ensureAdmin], debugAllNodesHandler); +router.get('/debug/:uuid', [ensureUser, ensureAdmin], debugNodeHandler); + +router.use('/communities', [ensureUser, ensureAdmin], communities); + +export default router; diff --git a/desci-server/src/routes/v1/index.ts b/desci-server/src/routes/v1/index.ts index a87e0ebae..616777fc6 100755 --- a/desci-server/src/routes/v1/index.ts +++ b/desci-server/src/routes/v1/index.ts @@ -10,7 +10,7 @@ import { nft } from '../../controllers/raw/nft.js'; import { asyncHandler } from '../../internal.js'; import { ensureUser } from '../../middleware/permissions.js'; -import admin from './admin.js'; +import admin from './admin/index.js'; import attestations from './attestations/index.js'; import auth from './auth.js'; import communities from './communities/index.js'; diff --git a/desci-server/src/services/Communities.ts b/desci-server/src/services/Communities.ts index 8b3114251..f9fc29ea1 100644 --- a/desci-server/src/services/Communities.ts +++ b/desci-server/src/services/Communities.ts @@ -1,5 +1,5 @@ import { Attestation, CommunityMembershipRole, NodeAttestation, NodeFeedItem, Prisma } from '@prisma/client'; -import _ from 'lodash'; +import _, { includes } from 'lodash'; import { prisma } from '../client.js'; import { DuplicateDataError, logger } from '../internal.js'; @@ -18,6 +18,22 @@ export class CommunityService { return community; } + async adminGetCommunities() { + return prisma.desciCommunity.findMany({ + orderBy: { createdAt: 'asc' }, + include: { + CommunityMember: { + select: { id: true, role: true, userId: true, user: { select: { name: true, userOrganizations: true } } }, + orderBy: { role: 'asc' }, + }, + CommunityEntryAttestation: { + select: { id: true, attestationVersion: { select: { name: true, image_url: true } } }, + orderBy: { createdAt: 'asc' }, + }, + }, + }); + } + async getAllCommunities() { return prisma.desciCommunity.findMany({ select: { diff --git a/desci-server/src/services/data/processing.ts b/desci-server/src/services/data/processing.ts index 254545b03..2b9454a69 100644 --- a/desci-server/src/services/data/processing.ts +++ b/desci-server/src/services/data/processing.ts @@ -348,7 +348,7 @@ export function extractRootDagCidFromManifest(manifest: ResearchObjectV1, manife } export async function getManifestFromNode( - node: { manifestUrl: string, cid?: string }, + node: { manifestUrl: string; cid?: string }, queryString?: string, ): Promise<{ manifest: ResearchObjectV1; manifestCid: string }> { // debugger; @@ -748,3 +748,36 @@ export async function assignTypeMapInManifest( return manifest; } } + +/** + * Proccesses regular file uploads, pins S3 files to IPFS, adds them to the end of the context DAG node, creates data references for them and updates the manifest. + */ +export async function processUploadToIpfs({ + files, +}: { + files: + | { + [fieldname: string]: Express.Multer.File[]; + } + | Express.Multer.File[]; +}): Promise> { + let pinResult: IpfsPinnedResult[] = []; + try { + const uploads = Array.isArray(files) ? files : Object.values(files).map((files) => files[0]); + // Pin new files, add draftNodeTree entries + pinResult = await pinNewFiles(uploads, false); + if (pinResult) { + logger.info({ pinResult }, 'Files uploaded to Ipfs'); + } + + return { + ok: true, + value: pinResult, + }; + // SUCCESS + } catch (error) { + logger.error({ error }, 'Error processing S3 data to IPFS'); + const controlledErr = 'type' in error ? error : createUnhandledError(error); + return { ok: false, value: controlledErr }; + } +} diff --git a/desci-server/src/types/ProcessEnv.d.ts b/desci-server/src/types/ProcessEnv.d.ts index e7fe3592b..35ac96f86 100755 --- a/desci-server/src/types/ProcessEnv.d.ts +++ b/desci-server/src/types/ProcessEnv.d.ts @@ -20,5 +20,6 @@ declare namespace NodeJS { CROSSREF_API_KEY: string; AUTOMATED_METADATA_API: string; AUTOMATED_METADATA_API_KEY: string; + IPFS_RESOLVER_OVERRIDE: string; } } From d498f8f54e7bdea0e538d2881e253cdc5a1ae6e2 Mon Sep 17 00:00:00 2001 From: m0ar Date: Mon, 23 Sep 2024 10:23:54 +0200 Subject: [PATCH 17/35] nodeslib: force sep eth chainid in CACAO unless overridden --- nodes-lib/package.json | 2 +- nodes-lib/src/util/signing.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nodes-lib/package.json b/nodes-lib/package.json index 1debe7631..9964f222e 100644 --- a/nodes-lib/package.json +++ b/nodes-lib/package.json @@ -1,6 +1,6 @@ { "name": "@desci-labs/nodes-lib", - "version": "0.0.11-rc1", + "version": "0.0.11", "homepage": "https://github.com/desci-labs/nodes#readme", "description": "Stand-alone client library for interacting with desci-server", "repository": { diff --git a/nodes-lib/src/util/signing.ts b/nodes-lib/src/util/signing.ts index 333f5dd8b..c68642984 100644 --- a/nodes-lib/src/util/signing.ts +++ b/nodes-lib/src/util/signing.ts @@ -76,7 +76,9 @@ export const authorizedSessionDidFromSigner = async ( const address = await signer.getAddress(); const network = await jsonRpcProvider.getNetwork(); - const chainId = `eip155:${chainIdOverride ?? network.chainId}`; + // Force sepolia chainID in CACAO to prevent segmenting the + // user's streams over AccountIDs with different chainIDs + const chainId = `eip155:${chainIdOverride ?? "11155111" }`; const caipAccountId = new AccountId({ address, chainId }); let authMethod: AuthMethod; From ae4275306e44c1eb895aed43434ae1dbfa0def5e Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Mon, 23 Sep 2024 04:06:16 -0500 Subject: [PATCH 18/35] clean up --- desci-server/src/controllers/admin/communities/index.ts | 1 - desci-server/src/core/ApiResponse.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/desci-server/src/controllers/admin/communities/index.ts b/desci-server/src/controllers/admin/communities/index.ts index eab4bb6bd..ae46c523a 100644 --- a/desci-server/src/controllers/admin/communities/index.ts +++ b/desci-server/src/controllers/admin/communities/index.ts @@ -44,7 +44,6 @@ export const createCommunity = async (req: Request, res: Response, _next: NextFu if (!image_url) throw new BadRequestError('No community logo uploaded'); - // logger.info({ ...body, image_url }, 'payload'); const community = await communityService.createCommunity({ ...body, image_url }); new SuccessResponse(community).send(res); }; diff --git a/desci-server/src/core/ApiResponse.ts b/desci-server/src/core/ApiResponse.ts index 238621108..bd81e30c7 100644 --- a/desci-server/src/core/ApiResponse.ts +++ b/desci-server/src/core/ApiResponse.ts @@ -1,7 +1,5 @@ import { Response } from 'express'; -import { logger } from '../logger.js'; - enum ResponseStatus { SUCCESS = 200, BAD_REQUEST = 400, From 2d6afe043ee5ea72ecfe84b1234ff57da7b80f53 Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Mon, 23 Sep 2024 04:27:12 -0500 Subject: [PATCH 19/35] sanity check before attempting to retrieve automerge url doc --- desci-repo/src/controllers/nodes/documents.ts | 7 +------ desci-server/src/services/repoService.ts | 5 ++++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/desci-repo/src/controllers/nodes/documents.ts b/desci-repo/src/controllers/nodes/documents.ts index 3a70577eb..0292f664c 100644 --- a/desci-repo/src/controllers/nodes/documents.ts +++ b/desci-repo/src/controllers/nodes/documents.ts @@ -59,18 +59,13 @@ export const createNodeDocument = async function (req: Request, res: Response) { export const getLatestNodeManifest = async function (req: Request, res: Response) { logger.info({ params: req.params }, 'START [getLatestNodeManifest]'); try { - console.log('[getLatestNodeManifest]', req.params); + logger.info({ params: req.params }, '[getLatestNodeManifest]'); if (!req.params.uuid) { res.status(400).send({ ok: false, message: 'Invalid data' }); return; } const { uuid } = req.params; - - // const queryResult = await pool.query('SELECT * FROM nodes WHERE uuid = $1', [uuid]); - // console.log('user:', queryResult.rows[0]); - - logger.info('[getLatestNodeManifest]', { uuid }); const node = await findNodeByUuid(uuid); logger.trace('[getLatestNodeManifest::node]', { node }); diff --git a/desci-server/src/services/repoService.ts b/desci-server/src/services/repoService.ts index 2c9a75557..9a6cdcb7c 100644 --- a/desci-server/src/services/repoService.ts +++ b/desci-server/src/services/repoService.ts @@ -85,12 +85,15 @@ class RepoService { } async getDraftDocument(arg: { uuid: NodeUuid }) { + if (!arg.uuid) { + logger.warn({ arg }, 'Attempt to retrieve draft manifest for empty UUID'); + return null; + } logger.info({ arg }, 'Retrieve Draft Document'); try { const response = await this.#client.get>( `${this.baseUrl}/v1/nodes/documents/draft/${arg.uuid}`, ); - logger.info({ response: response.status }, 'Draft Retrieval Response'); if (response.status === 200 && response.data.ok) { return response.data.document; } else { From 4179629e1abb7cc8c57e956caad2e1dba1d32042 Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Mon, 23 Sep 2024 07:32:12 -0500 Subject: [PATCH 20/35] implement community update api --- .../controllers/admin/communities/index.ts | 69 +++++++++++++++++-- .../src/routes/v1/admin/communities/index.ts | 33 +++++---- .../src/routes/v1/admin/communities/schema.ts | 24 ++++++- desci-server/src/services/Communities.ts | 7 ++ desci-server/src/services/data/processing.ts | 2 +- 5 files changed, 114 insertions(+), 21 deletions(-) diff --git a/desci-server/src/controllers/admin/communities/index.ts b/desci-server/src/controllers/admin/communities/index.ts index ae46c523a..72adb24f8 100644 --- a/desci-server/src/controllers/admin/communities/index.ts +++ b/desci-server/src/controllers/admin/communities/index.ts @@ -5,11 +5,13 @@ import { asyncMap, BadRequestError, communityService, + DuplicateDataError, + NotFoundError, logger as parentLogger, SuccessMessageResponse, SuccessResponse, } from '../../../internal.js'; -import { addCommunitySchema } from '../../../routes/v1/admin/communities/schema.js'; +import { addCommunitySchema, updateCommunitySchema } from '../../../routes/v1/admin/communities/schema.js'; import { processUploadToIpfs } from '../../../services/data/processing.js'; const logger = parentLogger.child({ module: 'Admin/Communities/controller' }); @@ -21,15 +23,67 @@ export const todoApi = async (_req: Request, res: Response, next: NextFunction) export const createCommunity = async (req: Request, res: Response, _next: NextFunction) => { const body = req.body as Required['body']>; + const exists = await communityService.findCommunityByNameOrSlug(body.slug); + if (exists) throw new DuplicateDataError(); + + let assets: { key: string; url: string }[]; + let uploads = Array.isArray(req.files) + ? req.files + : Object.values(req.files) + .map((files) => files[0]) + .filter(Boolean); + + if (uploads) { + uploads = uploads.map((file) => { + file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; + return file; + }); + logger.info({ uploads }, 'Uploads'); + const { ok, value } = await processUploadToIpfs({ files: uploads }); + if (ok && value) { + assets = value.map((ipfsImg) => ({ + key: ipfsImg.path, + url: `${process.env.IPFS_RESOLVER_OVERRIDE}/${ipfsImg.cid}`, + })); + } else { + throw new BadRequestError('Could not upload file to ipfs'); + } + } + + const image_url = assets.find((img) => img.key.toLowerCase().includes('imageurl'))?.url || body.imageUrl; + delete body.imageUrl; + + if (!image_url) throw new BadRequestError('No community logo uploaded'); + + const hidden = body.hidden.toString() === 'true' ? true : false; + const community = await communityService.createCommunity({ ...body, hidden, image_url }); + new SuccessResponse(community).send(res); +}; + +export const updateCommunity = async (req: Request, res: Response, _next: NextFunction) => { + const body = req.body as Required['body']>; + const { communityId } = req.params as z.infer['params']; + logger.info({ body, communityId }, 'updateCommunity'); + + let community = await communityService.findCommunityById(parseInt(communityId)); + + if (!community) throw new NotFoundError(); + let assets: { key: string; url: string }[]; - if (req.files) { - let uploads = Array.isArray(req.files) ? req.files : Object.values(req.files).map((files) => files[0]); + let uploads = Array.isArray(req.files) + ? req.files + : Object.values(req.files) + ?.map((files) => files[0]) + .filter(Boolean); + + logger.info({ uploads: !!uploads }, 'Uploads'); + if (uploads?.length) { uploads = uploads.map((file) => { file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; return file; }); const { ok, value } = await processUploadToIpfs({ files: uploads }); - if (ok) { + if (ok && value) { assets = value.map((ipfsImg) => ({ key: ipfsImg.path, url: `${process.env.IPFS_RESOLVER_OVERRIDE}/${ipfsImg.cid}`, @@ -39,12 +93,15 @@ export const createCommunity = async (req: Request, res: Response, _next: NextFu } } - const image_url = body.imageUrl || assets.find((img) => img.key.toLowerCase().includes('imageurl'))?.url; + // enforce strict non-empty check on image_url field + const image_url = + assets?.find((img) => img.key.toLowerCase().includes('imageurl'))?.url || body?.imageUrl || community.image_url; delete body.imageUrl; if (!image_url) throw new BadRequestError('No community logo uploaded'); + const hidden = body.hidden.toString() === 'true' ? true : false; - const community = await communityService.createCommunity({ ...body, image_url }); + community = await communityService.updateCommunityById(parseInt(communityId), { ...body, hidden, image_url }); new SuccessResponse(community).send(res); }; diff --git a/desci-server/src/routes/v1/admin/communities/index.ts b/desci-server/src/routes/v1/admin/communities/index.ts index 9d179c1e6..b43af850b 100644 --- a/desci-server/src/routes/v1/admin/communities/index.ts +++ b/desci-server/src/routes/v1/admin/communities/index.ts @@ -3,7 +3,12 @@ import { Request } from 'express'; import multer from 'multer'; import multerS3 from 'multer-s3'; -import { createCommunity, listAllCommunities, todoApi } from '../../../../controllers/admin/communities/index.js'; +import { + createCommunity, + listAllCommunities, + todoApi, + updateCommunity, +} from '../../../../controllers/admin/communities/index.js'; import { asyncHandler, ensureAdmin, ensureUser, logger as parentLogger, validate } from '../../../../internal.js'; import { isS3Configured, s3Client } from '../../../../services/s3.js'; @@ -14,6 +19,7 @@ import { addMemberSchema, removeEntryAttestationSchema, removeMemberSchema, + updateCommunitySchema, } from './schema.js'; const logger = parentLogger.child({ module: 'Admin/communities' }); @@ -39,7 +45,10 @@ const upload = isS3Configured }) : multer({ preservePath: true }); -const uploadHandler = upload.fields([{ name: 'imageUrl', maxCount: 1 }, { name: 'verifiedImageUrl' }]); +const uploadHandler = upload.fields([ + { name: 'imageUrl', maxCount: 1 }, + { name: 'verifiedImageUrl', maxCount: 1 }, +]); const wrappedHandler = (req: Request, res: Response, next: NextFunction) => { uploadHandler(req, res, (err) => { @@ -65,23 +74,21 @@ const sanitizeBody = async (req: Request, _res: Response, next: NextFunction) => router.get('/', [ensureUser, ensureAdmin], asyncHandler(listAllCommunities)); -// todo: api to create desci community router.post( '/', - [ensureUser, ensureAdmin, /* upload.single('image'),*/ wrappedHandler, sanitizeBody, validate(addCommunitySchema)], + [ensureUser, ensureAdmin, wrappedHandler, sanitizeBody, validate(addCommunitySchema)], asyncHandler(createCommunity), ); -// todo: api to update desci community -router.post( - ':communityId/', - [ensureUser, ensureAdmin, validate(addCommunitySchema), wrappedHandler], - asyncHandler(todoApi), +router.put( + '/:communityId', + [ensureUser, ensureAdmin, wrappedHandler, sanitizeBody, validate(updateCommunitySchema)], + asyncHandler(updateCommunity), ); // todo: api to create attestation for desci community ( with option to add it as an entryAttestation) router.post( - ':communityId/attestations', + '/:communityId/attestations', [ensureUser, ensureAdmin, validate(addAttestationSchema), wrappedHandler], asyncHandler(todoApi), ); @@ -91,21 +98,21 @@ router.post(':communityId/members', [ensureUser, ensureAdmin, validate(addMember // todo: api to remove a desci community member router.delete( - ':communityId/members/:memberId', + '/:communityId/members/:memberId', [ensureUser, ensureAdmin, validate(removeMemberSchema)], asyncHandler(todoApi), ); // todo: api to link attestation to community (this adds it to the communityEntryAttestation) router.post( - ':communityId/addEntryAttestation', + '/:communityId/addEntryAttestation', [ensureUser, ensureAdmin, validate(addEntryAttestationSchema)], asyncHandler(todoApi), ); // todo: api to remove attestation as required in for community (remove communityEntryAttestation) router.post( - ':communityId/removeEntryAttestation', + '/:communityId/removeEntryAttestation', [ensureUser, ensureAdmin, validate(removeEntryAttestationSchema)], asyncHandler(todoApi), ); diff --git a/desci-server/src/routes/v1/admin/communities/schema.ts b/desci-server/src/routes/v1/admin/communities/schema.ts index 8d9480eef..b2dc34bf0 100644 --- a/desci-server/src/routes/v1/admin/communities/schema.ts +++ b/desci-server/src/routes/v1/admin/communities/schema.ts @@ -6,7 +6,10 @@ export const addCommunitySchema = z.object({ name: z.string(), subtitle: z.string().min(1, 'Subtitle cannot be empty'), description: z.string().min(1, 'Description cannot be empty'), - hidden: z.boolean().default(false), + hidden: z.coerce + .boolean() + .transform((value) => (value.toString() === 'true' ? true : false)) + .default(false), keywords: z.array(z.string()).min(1, 'Community must have at least one keyword'), imageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", slug: z.string().min(3), @@ -14,6 +17,25 @@ export const addCommunitySchema = z.object({ }), }); +export const updateCommunitySchema = z.object({ + body: z.object({ + name: z.string().optional(), + subtitle: z.string().min(1, 'Subtitle cannot be empty').optional(), + description: z.string().min(1, 'Description cannot be empty').optional(), + hidden: z.coerce + .boolean() + .transform((value) => (value.toString() === 'true' ? true : false)) + .default(false), + keywords: z.array(z.string()).min(1, 'Community must have at least one keyword').optional(), + imageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", + slug: z.string().min(3).optional(), + links: z.array(z.string().url()).optional(), + }), + params: z.object({ + communityId: z.string(), + }), +}); + export const addAttestationSchema = z.object({ params: z.object({ communityId: z.coerce.number(), diff --git a/desci-server/src/services/Communities.ts b/desci-server/src/services/Communities.ts index f9fc29ea1..bac794c04 100644 --- a/desci-server/src/services/Communities.ts +++ b/desci-server/src/services/Communities.ts @@ -88,6 +88,13 @@ export class CommunityService { }); } + async updateCommunityById(id: number, community: Prisma.DesciCommunityUpdateInput) { + return prisma.desciCommunity.update({ + where: { id }, + data: community, + }); + } + /** * This query retrieves data from the "NodeAttestation" table along with the counts of related records from the * "Annotation", "NodeAttestationReaction", and "NodeAttestationVerification" tables. diff --git a/desci-server/src/services/data/processing.ts b/desci-server/src/services/data/processing.ts index 2b9454a69..b17c5f9bb 100644 --- a/desci-server/src/services/data/processing.ts +++ b/desci-server/src/services/data/processing.ts @@ -434,7 +434,7 @@ export async function pinNewFiles(files: any[], wrapWithDirectory = false): Prom if (structuredFilesForPinning.length) { if (structuredFilesForPinning.length) uploaded = await pinDirectory(structuredFilesForPinning, wrapWithDirectory); if (!uploaded.length) throw createIpfsUploadFailureError(); - logger.info('[UPDATE DATASET] Pinned files: ', uploaded.length); + logger.info({ uploaded }, '[UPDATE DATASET] Pinned files: '); } return uploaded; } From fe3a28eb0f8c3927f8dbbd6f4db9c9e19777cbc5 Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Mon, 23 Sep 2024 08:55:42 -0500 Subject: [PATCH 21/35] add and update attestation api --- .../controllers/admin/communities/index.ts | 127 +++++++++++++++++- .../src/routes/v1/admin/communities/index.ts | 16 ++- .../src/routes/v1/admin/communities/schema.ts | 12 +- desci-server/src/services/Attestation.ts | 5 +- 4 files changed, 145 insertions(+), 15 deletions(-) diff --git a/desci-server/src/controllers/admin/communities/index.ts b/desci-server/src/controllers/admin/communities/index.ts index 72adb24f8..db0b2ca94 100644 --- a/desci-server/src/controllers/admin/communities/index.ts +++ b/desci-server/src/controllers/admin/communities/index.ts @@ -3,6 +3,7 @@ import { z } from 'zod'; import { asyncMap, + attestationService, BadRequestError, communityService, DuplicateDataError, @@ -11,7 +12,12 @@ import { SuccessMessageResponse, SuccessResponse, } from '../../../internal.js'; -import { addCommunitySchema, updateCommunitySchema } from '../../../routes/v1/admin/communities/schema.js'; +import { + addAttestationSchema, + addCommunitySchema, + updateAttestationSchema, + updateCommunitySchema, +} from '../../../routes/v1/admin/communities/schema.js'; import { processUploadToIpfs } from '../../../services/data/processing.js'; const logger = parentLogger.child({ module: 'Admin/Communities/controller' }); @@ -33,7 +39,7 @@ export const createCommunity = async (req: Request, res: Response, _next: NextFu .map((files) => files[0]) .filter(Boolean); - if (uploads) { + if (uploads?.length > 0) { uploads = uploads.map((file) => { file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; return file; @@ -50,7 +56,7 @@ export const createCommunity = async (req: Request, res: Response, _next: NextFu } } - const image_url = assets.find((img) => img.key.toLowerCase().includes('imageurl'))?.url || body.imageUrl; + const image_url = assets?.find((img) => img.key.toLowerCase().includes('imageurl'))?.url || body.imageUrl; delete body.imageUrl; if (!image_url) throw new BadRequestError('No community logo uploaded'); @@ -65,7 +71,7 @@ export const updateCommunity = async (req: Request, res: Response, _next: NextFu const { communityId } = req.params as z.infer['params']; logger.info({ body, communityId }, 'updateCommunity'); - let community = await communityService.findCommunityById(parseInt(communityId)); + let community = await communityService.findCommunityById(+communityId); if (!community) throw new NotFoundError(); @@ -77,7 +83,7 @@ export const updateCommunity = async (req: Request, res: Response, _next: NextFu .filter(Boolean); logger.info({ uploads: !!uploads }, 'Uploads'); - if (uploads?.length) { + if (uploads?.length > 0) { uploads = uploads.map((file) => { file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; return file; @@ -101,11 +107,11 @@ export const updateCommunity = async (req: Request, res: Response, _next: NextFu if (!image_url) throw new BadRequestError('No community logo uploaded'); const hidden = body.hidden.toString() === 'true' ? true : false; - community = await communityService.updateCommunityById(parseInt(communityId), { ...body, hidden, image_url }); + community = await communityService.updateCommunityById(+communityId, { ...body, hidden, image_url }); new SuccessResponse(community).send(res); }; -export const listAllCommunities = async (_req: Request, res: Response, next: NextFunction) => { +export const listAllCommunities = async (_req: Request, res: Response, _next: NextFunction) => { const communities = await communityService.adminGetCommunities(); logger.info({ communities }, 'List communities'); const data = await asyncMap(communities, async (community) => { @@ -119,3 +125,110 @@ export const listAllCommunities = async (_req: Request, res: Response, next: Nex }); new SuccessResponse(data).send(res); }; + +export const createAttestation = async (req: Request, res: Response, _next: NextFunction) => { + const body = req.body as Required['body']>; + const { communityId } = req.params as z.infer['params']; + logger.info({ communityId, body }, 'Payload'); + + const community = await communityService.findCommunityById(Number(communityId)); + if (!community) throw new NotFoundError(`Community ${communityId} not found`); + + let assets: { key: string; url: string }[]; + let uploads = Array.isArray(req.files) + ? req.files + : Object.values(req.files) + .map((files) => files[0]) + .filter(Boolean); + + if (uploads) { + uploads = uploads.map((file) => { + file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; + return file; + }); + + const { ok, value } = await processUploadToIpfs({ files: uploads }); + if (ok && value) { + assets = value.map((ipfsImg) => ({ + key: ipfsImg.path, + url: `${process.env.IPFS_RESOLVER_OVERRIDE}/${ipfsImg.cid}`, + })); + } else { + throw new BadRequestError('Could not upload file to ipfs'); + } + } + + const image_url = assets.find((img) => img.key.toLowerCase().includes('imageurl'))?.url || body.imageUrl; + delete body.imageUrl; + const verified_image_url = + assets.find((img) => img.key.toLowerCase().includes('verifiedimageurl'))?.url || body.verifiedImageUrl; + delete body.verifiedImageUrl; + + logger.info({ image_url, verified_image_url }, 'Assets'); + + if (!image_url) throw new BadRequestError('No community logo uploaded'); + + const isProtected = body.protected.toString() === 'true' ? true : false; + const attestation = await attestationService.create({ + ...body, + image_url, + verified_image_url, + communityId: community.id, + protected: isProtected, + }); + new SuccessResponse(attestation).send(res); +}; + +export const updateAttestation = async (req: Request, res: Response, _next: NextFunction) => { + const body = req.body as Required['body']>; + const { attestationId } = req.params as z.infer['params']; + logger.info({ attestationId, body }, 'Payload'); + + const exists = await attestationService.findAttestationById(Number(attestationId)); + if (!exists) throw new NotFoundError(`Attestation ${attestationId} not found`); + + let assets: { key: string; url: string }[] | undefined; + let uploads = Array.isArray(req.files) + ? req.files + : Object.values(req.files) + .map((files) => files[0]) + .filter(Boolean); + + logger.info({ uploads: uploads?.map((up) => up.fieldname) }, 'Uploads'); + if (uploads?.length > 0) { + uploads = uploads.map((file) => { + file.originalname = `${file.fieldname}.${file.originalname.split('.')?.[1]}`; + return file; + }); + const { ok, value } = await processUploadToIpfs({ files: uploads }); + logger.info({ ok, value }, 'Uploads REsult'); + if (ok && value) { + assets = value.map((ipfsImg) => ({ + key: ipfsImg.path, + url: `${process.env.IPFS_RESOLVER_OVERRIDE}/${ipfsImg.cid}`, + })); + } else { + throw new BadRequestError('Could not upload file to ipfs'); + } + } + + const image_url = assets?.find((img) => img.key.toLowerCase().includes('imageurl'))?.url || body.imageUrl; + const verified_image_url = + assets?.find((img) => img.key.toLowerCase().includes('verifiedimageurl'))?.url || body.verifiedImageUrl; + delete body.imageUrl; + delete body.verifiedImageUrl; + + logger.info({ image_url, verified_image_url }, 'Assets'); + + if (!image_url) throw new BadRequestError('No attestation image uploaded'); + + const isProtected = body.protected.toString() === 'true' ? true : false; + const attestation = await attestationService.updateAttestation(exists.id, { + ...body, + image_url, + verified_image_url, + communityId: exists.communityId, + protected: isProtected, + }); + new SuccessResponse(attestation).send(res); +}; diff --git a/desci-server/src/routes/v1/admin/communities/index.ts b/desci-server/src/routes/v1/admin/communities/index.ts index b43af850b..0a32bbf35 100644 --- a/desci-server/src/routes/v1/admin/communities/index.ts +++ b/desci-server/src/routes/v1/admin/communities/index.ts @@ -4,9 +4,11 @@ import multer from 'multer'; import multerS3 from 'multer-s3'; import { + createAttestation, createCommunity, listAllCommunities, todoApi, + updateAttestation, updateCommunity, } from '../../../../controllers/admin/communities/index.js'; import { asyncHandler, ensureAdmin, ensureUser, logger as parentLogger, validate } from '../../../../internal.js'; @@ -19,6 +21,7 @@ import { addMemberSchema, removeEntryAttestationSchema, removeMemberSchema, + updateAttestationSchema, updateCommunitySchema, } from './schema.js'; @@ -86,15 +89,20 @@ router.put( asyncHandler(updateCommunity), ); -// todo: api to create attestation for desci community ( with option to add it as an entryAttestation) router.post( '/:communityId/attestations', - [ensureUser, ensureAdmin, validate(addAttestationSchema), wrappedHandler], - asyncHandler(todoApi), + [ensureUser, ensureAdmin, wrappedHandler, validate(addAttestationSchema)], + asyncHandler(createAttestation), +); + +router.put( + '/:communityId/attestations/:attestationId', + [ensureUser, ensureAdmin, wrappedHandler, validate(updateAttestationSchema)], + asyncHandler(updateAttestation), ); // todo: api to add a desci community member -router.post(':communityId/members', [ensureUser, ensureAdmin, validate(addMemberSchema)], asyncHandler(todoApi)); +router.post('/:communityId/members', [ensureUser, ensureAdmin, validate(addMemberSchema)], asyncHandler(todoApi)); // todo: api to remove a desci community member router.delete( diff --git a/desci-server/src/routes/v1/admin/communities/schema.ts b/desci-server/src/routes/v1/admin/communities/schema.ts index b2dc34bf0..5ffcaff83 100644 --- a/desci-server/src/routes/v1/admin/communities/schema.ts +++ b/desci-server/src/routes/v1/admin/communities/schema.ts @@ -32,7 +32,7 @@ export const updateCommunitySchema = z.object({ links: z.array(z.string().url()).optional(), }), params: z.object({ - communityId: z.string(), + communityId: z.coerce.number(), }), }); @@ -42,14 +42,20 @@ export const addAttestationSchema = z.object({ }), body: z.object({ name: z.string(), - communitySlug: z.string(), description: z.string(), imageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", verifiedImageUrl: z.string().url().optional(), //"https://pub.desci.com/ipfs/bafkreie7kxhzpzhsbywcrpgyv5yvy3qxcjsibuxsnsh5olaztl2uvnrzx4", - protected: z.boolean().default(false), + protected: z.coerce + .boolean() + .transform((value) => (value.toString() === 'true' ? true : false)) + .default(false), }), }); +export const updateAttestationSchema = addAttestationSchema.extend({ + params: z.object({ attestationId: z.coerce.number(), communityId: z.coerce.number() }), +}); + export const addMemberSchema = z.object({ params: z.object({ communityId: z.coerce.number(), diff --git a/desci-server/src/services/Attestation.ts b/desci-server/src/services/Attestation.ts index 475f58be1..48a690d8a 100644 --- a/desci-server/src/services/Attestation.ts +++ b/desci-server/src/services/Attestation.ts @@ -171,7 +171,10 @@ export class AttestationService { const attestation = await this.findAttestationById(attestationId); if (!attestation) throw new AttestationNotFoundError(); - + await prisma.attestation.update({ + where: { id: attestationId }, + data: { verified_image_url: data.verified_image_url }, + }); await this.#publishVersion({ name: data.name as string, description: data.description, From eb1fc1b9114db6f4684033b956ddf5f95c196ce7 Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Mon, 23 Sep 2024 11:33:24 -0500 Subject: [PATCH 22/35] add and remove community member apis --- .../controllers/admin/communities/index.ts | 40 +++++++++++++++++++ .../src/routes/v1/admin/communities/index.ts | 8 ++-- .../src/routes/v1/admin/communities/schema.ts | 6 +-- desci-server/src/services/Communities.ts | 8 ++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/desci-server/src/controllers/admin/communities/index.ts b/desci-server/src/controllers/admin/communities/index.ts index db0b2ca94..e6ffc0221 100644 --- a/desci-server/src/controllers/admin/communities/index.ts +++ b/desci-server/src/controllers/admin/communities/index.ts @@ -1,3 +1,4 @@ +import { CommunityMembershipRole } from '@prisma/client'; import { NextFunction, Request, Response } from 'express'; import { z } from 'zod'; @@ -9,12 +10,15 @@ import { DuplicateDataError, NotFoundError, logger as parentLogger, + prisma, SuccessMessageResponse, SuccessResponse, } from '../../../internal.js'; import { addAttestationSchema, addCommunitySchema, + addMemberSchema, + removeMemberSchema, updateAttestationSchema, updateCommunitySchema, } from '../../../routes/v1/admin/communities/schema.js'; @@ -232,3 +236,39 @@ export const updateAttestation = async (req: Request, res: Response, _next: Next }); new SuccessResponse(attestation).send(res); }; + +export const addMember = async (req: Request, res: Response, _next: NextFunction) => { + const { userId, role }: Required['body']> = req.body; + const { communityId }: z.infer['params'] = req.params; + + const user = await prisma.user.findFirst({ where: { id: userId } }); + if (!user) throw new NotFoundError('Invalid user'); + + const community = await communityService.findCommunityById(Number(communityId)); + if (!community) throw new NotFoundError(`No Desci community with ID: ${Number(communityId)} found!`); + + const exists = await communityService.findMemberByUserId(Number(communityId), userId); + if (exists) throw new DuplicateDataError(); + + const member = await communityService.addCommunityMember(parseInt(communityId), { + userId, + communityId: parseInt(communityId), + role, + }); + + new SuccessResponse(member).send(res); +}; + +export const removeMember = async (req: Request, res: Response, _next: NextFunction) => { + const { communityId, memberId }: z.infer['params'] = req.params; + + const community = await communityService.findCommunityById(Number(communityId)); + if (!community) throw new NotFoundError(`No Desci community with ID: ${Number(communityId)} found!`); + + const exists = await communityService.findMemberById(Number(memberId)); + if (!exists) throw new NotFoundError(); + + await communityService.removeMemberById(Number(memberId)); + + new SuccessMessageResponse().send(res); +}; diff --git a/desci-server/src/routes/v1/admin/communities/index.ts b/desci-server/src/routes/v1/admin/communities/index.ts index 0a32bbf35..5c807d836 100644 --- a/desci-server/src/routes/v1/admin/communities/index.ts +++ b/desci-server/src/routes/v1/admin/communities/index.ts @@ -4,9 +4,11 @@ import multer from 'multer'; import multerS3 from 'multer-s3'; import { + addMember, createAttestation, createCommunity, listAllCommunities, + removeMember, todoApi, updateAttestation, updateCommunity, @@ -101,14 +103,12 @@ router.put( asyncHandler(updateAttestation), ); -// todo: api to add a desci community member -router.post('/:communityId/members', [ensureUser, ensureAdmin, validate(addMemberSchema)], asyncHandler(todoApi)); +router.post('/:communityId/members', [ensureUser, ensureAdmin, validate(addMemberSchema)], asyncHandler(addMember)); -// todo: api to remove a desci community member router.delete( '/:communityId/members/:memberId', [ensureUser, ensureAdmin, validate(removeMemberSchema)], - asyncHandler(todoApi), + asyncHandler(removeMember), ); // todo: api to link attestation to community (this adds it to the communityEntryAttestation) diff --git a/desci-server/src/routes/v1/admin/communities/schema.ts b/desci-server/src/routes/v1/admin/communities/schema.ts index 5ffcaff83..9600b2a73 100644 --- a/desci-server/src/routes/v1/admin/communities/schema.ts +++ b/desci-server/src/routes/v1/admin/communities/schema.ts @@ -58,10 +58,10 @@ export const updateAttestationSchema = addAttestationSchema.extend({ export const addMemberSchema = z.object({ params: z.object({ - communityId: z.coerce.number(), + communityId: z.string(), }), body: z.object({ - userId: z.coerce.number(), + userId: z.number(), role: z.enum([CommunityMembershipRole.ADMIN, CommunityMembershipRole.MEMBER]), }), }); @@ -69,8 +69,6 @@ export const addMemberSchema = z.object({ export const removeMemberSchema = z.object({ params: z.object({ communityId: z.coerce.number(), - }), - body: z.object({ memberId: z.coerce.number(), }), }); diff --git a/desci-server/src/services/Communities.ts b/desci-server/src/services/Communities.ts index bac794c04..6e9a467aa 100644 --- a/desci-server/src/services/Communities.ts +++ b/desci-server/src/services/Communities.ts @@ -318,9 +318,17 @@ export class CommunityService { return await prisma.communityMember.findUnique({ where: { userId_communityId: { userId, communityId } } }); } + async findMemberById(id: number) { + return await prisma.communityMember.findUnique({ where: { id } }); + } + async removeMember(communityId: number, userId: number) { return prisma.communityMember.delete({ where: { userId_communityId: { userId, communityId } } }); } + + async removeMemberById(id: number) { + return prisma.communityMember.delete({ where: { id } }); + } } export const communityService = new CommunityService(); From c40c9739f8a0744b3c120b4150aa6d70af7098af Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Tue, 24 Sep 2024 04:49:37 -0500 Subject: [PATCH 23/35] implement apis for adding/removing community required attestations --- .../controllers/admin/communities/index.ts | 37 +++++++++++++++++++ .../src/routes/v1/admin/communities/index.ts | 16 ++++---- .../src/routes/v1/admin/communities/schema.ts | 11 ------ desci-server/src/services/Attestation.ts | 24 ++++++++++++ 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/desci-server/src/controllers/admin/communities/index.ts b/desci-server/src/controllers/admin/communities/index.ts index e6ffc0221..2daba8585 100644 --- a/desci-server/src/controllers/admin/communities/index.ts +++ b/desci-server/src/controllers/admin/communities/index.ts @@ -17,6 +17,7 @@ import { import { addAttestationSchema, addCommunitySchema, + addEntryAttestationSchema, addMemberSchema, removeMemberSchema, updateAttestationSchema, @@ -272,3 +273,39 @@ export const removeMember = async (req: Request, res: Response, _next: NextFunct new SuccessMessageResponse().send(res); }; + +export const addEntryAttestation = async (req: Request, res: Response, _next: NextFunction) => { + const { communityId, attestationId }: z.infer['params'] = req.params; + + const community = await communityService.findCommunityById(Number(communityId)); + if (!community) throw new NotFoundError(`No Desci community with ID: ${Number(communityId)} not found!`); + + const attestation = await attestationService.findAttestationById(+attestationId); + if (!attestation) throw new NotFoundError(`No attestation with ID: ${Number(attestationId)} not found!`); + + const exists = await attestationService.getCommunityEntryAttestation(Number(communityId), Number(attestationId)); + if (exists) throw new DuplicateDataError(); + + const data = await attestationService.addCommunityEntryAttestation({ + communityId: Number(communityId), + attestationId: Number(attestationId), + attestationVersion: attestation.AttestationVersion[attestation.AttestationVersion.length - 1].id, + }); + + new SuccessResponse(data).send(res); +}; + +export const removeEntryAttestation = async (req: Request, res: Response, _next: NextFunction) => { + const { communityId, attestationId }: z.infer['params'] = req.params; + + const attestation = await attestationService.findAttestationById(+attestationId); + if (!attestation) throw new NotFoundError(`No attestation with ID: ${Number(attestationId)} not found!`); + + const data = await attestationService.removeCommunityEntryAttestation({ + communityId: Number(communityId), + attestationId: Number(attestationId), + attestationVersion: attestation.AttestationVersion[attestation.AttestationVersion.length - 1].id, + }); + + new SuccessResponse(data).send(res); +}; diff --git a/desci-server/src/routes/v1/admin/communities/index.ts b/desci-server/src/routes/v1/admin/communities/index.ts index 5c807d836..f09a1b45f 100644 --- a/desci-server/src/routes/v1/admin/communities/index.ts +++ b/desci-server/src/routes/v1/admin/communities/index.ts @@ -4,12 +4,13 @@ import multer from 'multer'; import multerS3 from 'multer-s3'; import { + addEntryAttestation, addMember, createAttestation, createCommunity, listAllCommunities, + removeEntryAttestation, removeMember, - todoApi, updateAttestation, updateCommunity, } from '../../../../controllers/admin/communities/index.js'; @@ -21,7 +22,6 @@ import { addCommunitySchema, addEntryAttestationSchema, addMemberSchema, - removeEntryAttestationSchema, removeMemberSchema, updateAttestationSchema, updateCommunitySchema, @@ -113,16 +113,16 @@ router.delete( // todo: api to link attestation to community (this adds it to the communityEntryAttestation) router.post( - '/:communityId/addEntryAttestation', + '/:communityId/addEntryAttestation/:attestationId', [ensureUser, ensureAdmin, validate(addEntryAttestationSchema)], - asyncHandler(todoApi), + asyncHandler(addEntryAttestation), ); // todo: api to remove attestation as required in for community (remove communityEntryAttestation) -router.post( - '/:communityId/removeEntryAttestation', - [ensureUser, ensureAdmin, validate(removeEntryAttestationSchema)], - asyncHandler(todoApi), +router.delete( + '/:communityId/removeEntryAttestation/:attestationId', + [ensureUser, ensureAdmin, validate(addEntryAttestationSchema)], + asyncHandler(removeEntryAttestation), ); export default router; diff --git a/desci-server/src/routes/v1/admin/communities/schema.ts b/desci-server/src/routes/v1/admin/communities/schema.ts index 9600b2a73..1c3467e13 100644 --- a/desci-server/src/routes/v1/admin/communities/schema.ts +++ b/desci-server/src/routes/v1/admin/communities/schema.ts @@ -76,17 +76,6 @@ export const removeMemberSchema = z.object({ export const addEntryAttestationSchema = z.object({ params: z.object({ communityId: z.coerce.number(), - }), - body: z.object({ - attestationId: z.coerce.number(), - }), -}); - -export const removeEntryAttestationSchema = z.object({ - params: z.object({ - communityId: z.coerce.number(), - }), - body: z.object({ attestationId: z.coerce.number(), }), }); diff --git a/desci-server/src/services/Attestation.ts b/desci-server/src/services/Attestation.ts index 48a690d8a..8a7fbbb1a 100644 --- a/desci-server/src/services/Attestation.ts +++ b/desci-server/src/services/Attestation.ts @@ -17,6 +17,7 @@ import { DuplicateReactionError, DuplicateVerificationError, NoAccessError, + NotFoundError, VerificationError, VerificationNotFoundError, ensureUuidEndsWithDot, @@ -239,6 +240,23 @@ export class AttestationService { }); } + async removeCommunityEntryAttestation({ + communityId, + attestationId, + attestationVersion: version, + }: { + communityId: number; + attestationId: number; + attestationVersion: number; + }) { + const existingSelection = await prisma.communityEntryAttestation.findFirst({ + where: { desciCommunityId: communityId, attestationId, attestationVersionId: version }, + }); + if (!existingSelection) return null; + + return await prisma.communityEntryAttestation.delete({ where: { id: existingSelection.id } }); + } + async getAllNodeAttestations(uuid: string) { return prisma.nodeAttestation.findMany({ where: { nodeUuid: ensureUuidEndsWithDot(uuid), revoked: false }, @@ -300,6 +318,12 @@ export class AttestationService { }); } + async getCommunityEntryAttestation(communityId: number, attestationId: number) { + return prisma.communityEntryAttestation.findFirst({ + where: { desciCommunityId: communityId, attestationId, required: true }, + }); + } + async getCommunityEntryAttestations(communityId: number) { const community = await communityService.findCommunityById(communityId); if (!community) throw new CommunityNotFoundError(); From 42c5f7c55595f2d885477370b185e4cb7090243a Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Tue, 24 Sep 2024 11:49:41 +0200 Subject: [PATCH 24/35] sanitize output for email --- desci-server/src/services/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desci-server/src/services/auth.ts b/desci-server/src/services/auth.ts index 3145f4e20..193df0ea7 100644 --- a/desci-server/src/services/auth.ts +++ b/desci-server/src/services/auth.ts @@ -124,7 +124,7 @@ const sendMagicLinkEmail = async (email: string, ip?: string) => { }); if (env.SHOULD_SEND_EMAIL) { - logger.info({ fn: 'sendMagicLinkEmail', email, token }, `Sending actual email to ${email} token: ${token}`); + logger.info({ fn: 'sendMagicLinkEmail', email }, `Sending actual email`); const url = `${env.DAPP_URL}/web/login?e=${email}&c=${token}`; const goodIp = ip?.length > 0 && ip !== '::1' && ip !== '127.0.0.1' && ip !== 'localhost'; From 8a3ffc64c421eca41b6737d38f3beb56b5620516 Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Tue, 24 Sep 2024 05:00:09 -0500 Subject: [PATCH 25/35] clean up --- desci-server/src/routes/v1/admin/communities/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/desci-server/src/routes/v1/admin/communities/index.ts b/desci-server/src/routes/v1/admin/communities/index.ts index f09a1b45f..d6acaa0bb 100644 --- a/desci-server/src/routes/v1/admin/communities/index.ts +++ b/desci-server/src/routes/v1/admin/communities/index.ts @@ -111,14 +111,12 @@ router.delete( asyncHandler(removeMember), ); -// todo: api to link attestation to community (this adds it to the communityEntryAttestation) router.post( '/:communityId/addEntryAttestation/:attestationId', [ensureUser, ensureAdmin, validate(addEntryAttestationSchema)], asyncHandler(addEntryAttestation), ); -// todo: api to remove attestation as required in for community (remove communityEntryAttestation) router.delete( '/:communityId/removeEntryAttestation/:attestationId', [ensureUser, ensureAdmin, validate(addEntryAttestationSchema)], From 164b56e13da4846709a0fe4df4419e7488d8682b Mon Sep 17 00:00:00 2001 From: m0ar Date: Tue, 24 Sep 2024 12:19:50 +0200 Subject: [PATCH 26/35] ceramic: bump nodes to 6.5.0 --- ceramic-k8s/ceramic_deployment_dev.yaml | 2 +- ceramic-k8s/ceramic_deployment_prod.yaml | 2 +- docker-compose.dev.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ceramic-k8s/ceramic_deployment_dev.yaml b/ceramic-k8s/ceramic_deployment_dev.yaml index 58946e7ec..68f3fd464 100644 --- a/ceramic-k8s/ceramic_deployment_dev.yaml +++ b/ceramic-k8s/ceramic_deployment_dev.yaml @@ -41,7 +41,7 @@ spec: App: JsCeramicDev spec: containers: - - image: ceramicnetwork/js-ceramic:6.4.0 + - image: ceramicnetwork/js-ceramic:6.5.0 name: js-ceramic-dev command: ["/bin/bash", "-c"] args: diff --git a/ceramic-k8s/ceramic_deployment_prod.yaml b/ceramic-k8s/ceramic_deployment_prod.yaml index ce84fecec..7ed15bb76 100644 --- a/ceramic-k8s/ceramic_deployment_prod.yaml +++ b/ceramic-k8s/ceramic_deployment_prod.yaml @@ -62,7 +62,7 @@ spec: App: JsCeramicProd spec: containers: - - image: ceramicnetwork/js-ceramic:6.4.0 + - image: ceramicnetwork/js-ceramic:6.5.0 name: js-ceramic-prod command: ["/bin/bash", "-c"] volumeMounts: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 2be51c7fe..faba1ceca 100755 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -184,7 +184,7 @@ services: - ./local-data/redis:/data ceramic: - image: ceramicnetwork/js-ceramic:6.4.0 + image: ceramicnetwork/js-ceramic:6.5.0 container_name: ceramic ports: - "7007:7007" From 82b1782a58bf3724aceb014c4a55862cd959c017 Mon Sep 17 00:00:00 2001 From: m0ar Date: Tue, 24 Sep 2024 16:09:29 +0200 Subject: [PATCH 27/35] server: include stream & dpid with other public info in checkNodeAccess --- desci-server/src/controllers/nodes/byDpid.ts | 4 ++++ .../src/controllers/nodes/checkNodeAccess.ts | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/desci-server/src/controllers/nodes/byDpid.ts b/desci-server/src/controllers/nodes/byDpid.ts index 10b575e24..b3a2077e0 100755 --- a/desci-server/src/controllers/nodes/byDpid.ts +++ b/desci-server/src/controllers/nodes/byDpid.ts @@ -14,6 +14,8 @@ type NodeByDpidParams = { type NodeByDpidSuccess = { uuid: string; + dpidAlias: number; + ceramicStream: string; }; type NodeByDpidError = { @@ -43,6 +45,8 @@ export const nodeByDpid = async ( node = await prisma.node.findFirstOrThrow({ select: { uuid: true, + dpidAlias: true, + ceramicStream: true, }, where: { dpidAlias: { diff --git a/desci-server/src/controllers/nodes/checkNodeAccess.ts b/desci-server/src/controllers/nodes/checkNodeAccess.ts index da59758e1..4f6c8feb6 100644 --- a/desci-server/src/controllers/nodes/checkNodeAccess.ts +++ b/desci-server/src/controllers/nodes/checkNodeAccess.ts @@ -13,8 +13,10 @@ type GetCheckNodeAccessResponse = { isOwner: boolean; isShared: boolean; hasAccess: boolean; - sharedOn?: number; isPublished: boolean; + ceramicStream?: string; + dpidAlias?: number; + sharedOn?: number; recentCid?: string; manifestUrl?: string; }; @@ -42,14 +44,10 @@ export const checkNodeAccess = async ( const node = await prisma.node.findFirst({ select: { uuid: true, - id: true, - createdAt: true, - updatedAt: true, ownerId: true, - title: true, manifestUrl: true, - cid: true, - NodeCover: true, + dpidAlias: true, + ceramicStream: true, versions: { select: { manifestUrl: true, @@ -92,6 +90,8 @@ export const checkNodeAccess = async ( isShared: !isOwner && !!privSharedNode, hasAccess, isPublished, + ceramicStream: node.ceramicStream, + dpidAlias: node.dpidAlias, sharedOn: privSharedNode?.createdAt.getTime(), recentCid: latestPublishedVersion?.manifestUrl, manifestUrl: hasAccess ? node.versions[0]?.manifestUrl : undefined, From 04ba63ee3b0cfa38b5c2e6c2cb0ab33a3bf0f00c Mon Sep 17 00:00:00 2001 From: shadrach-tayo Date: Tue, 24 Sep 2024 17:45:26 -0500 Subject: [PATCH 28/35] fix: Orcid Nodes dataset work record dpid path, clear orcid work record if no claims are found --- desci-server/src/services/orcid.ts | 36 +++++++++++++++++------------- desci-server/src/theGraph.ts | 22 +++++++++++++----- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/desci-server/src/services/orcid.ts b/desci-server/src/services/orcid.ts index c3cc0a60a..751be20d6 100644 --- a/desci-server/src/services/orcid.ts +++ b/desci-server/src/services/orcid.ts @@ -54,7 +54,6 @@ class OrcidApiService { throw new Error('User does not have an orcid auth token'); } - // todo: refresh token if necessary try { const url = `https://${ORCID_DOMAIN}/oauth/token`; @@ -125,6 +124,9 @@ class OrcidApiService { * @returns */ async removeClaimRecord({ claimId, nodeUuid, orcid }: { claimId: number; nodeUuid: string; orcid: string }) { + const user = await prisma.user.findUnique({ where: { orcid } }); + const authToken = await this.getAccessToken(user.id); + const putCode = await prisma.orcidPutCodes.findFirst({ where: { claimId, @@ -133,18 +135,13 @@ class OrcidApiService { }, }); - if (!putCode) return; - - const user = await prisma.user.findUnique({ where: { orcid } }); - const authToken = await this.getAccessToken(user.id); - logger.info({ userId: user.id, authToken: !!authToken, nodeUuid }, '[ORCID::DELETE]:: START'); + // remove claim record on Orcid profile + if (putCode) { + logger.info({ userId: user.id, authToken: !!authToken, nodeUuid }, '[ORCID::DELETE]:: START'); - await this.removeWorkRecord({ orcid, putCode, authToken }); + await this.removeWorkRecord({ orcid, putCode, authToken }); + } - const { researchObjects } = await getIndexedResearchObjects([nodeUuid]); - const researchObject = researchObjects[0] as IndexedResearchObject; - const manifestCid = hexToCid(researchObject.recentCid); - const latestManifest = await getManifestByCid(manifestCid); let claims = await attestationService.getProtectedNodeClaims(nodeUuid); claims = claims.filter((claim) => claim.verifications > 0); logger.info({ claims: claims.length }, '[ORCID::DELETE]:: CHECK CLAIMS'); @@ -185,7 +182,7 @@ class OrcidApiService { code, orcid, }, - 'ORCID API DELETE RECORD', + 'ORCID API DELETE WORK RECORD', ); const response = await fetch(url, { method: 'DELETE', @@ -253,8 +250,14 @@ class OrcidApiService { let claims = await attestationService.getProtectedNodeClaims(nodeUuid); claims = claims.filter((claim) => claim.verifications > 0); - // TODO: if claims is empty remove orcid record - if (claims.length === 0) return; + if (claims.length === 0) { + const orcidPutCode = await prisma.orcidPutCodes.findUnique({ + where: { orcid_uuid_reference: { orcid, uuid: nodeUuid, reference: PutcodeReference.PREPRINT } }, + }); + + await orcidApiService.removeWorkRecord({ authToken, putCode: orcidPutCode, orcid: user.orcid }); + return; + } const latestVersion = researchObject.versions[researchObject.versions.length - 1]; const publicationDate = new Date(parseInt(latestVersion.time) * 1000).toLocaleDateString().replaceAll('/', '-'); @@ -270,8 +273,9 @@ class OrcidApiService { manifest: latestManifest, }); const claimRecordPromises = claims.map((claim) => { - const claimedVersionNumber = claims[claims.length - 1].nodeVersion; - const claimedVersion = researchObject.versions[claimedVersionNumber]; + const versionIndex = claims[claims.length - 1].nodeVersion; + const claimedVersionNumber = versionIndex + 1; + const claimedVersion = researchObject.versions[versionIndex]; const publicationDate = new Date(parseInt(claimedVersion.time) * 1000) .toLocaleDateString() .replaceAll('/', '-'); diff --git a/desci-server/src/theGraph.ts b/desci-server/src/theGraph.ts index 1e3cf9475..709b3b36e 100644 --- a/desci-server/src/theGraph.ts +++ b/desci-server/src/theGraph.ts @@ -77,7 +77,7 @@ export const getIndexedResearchObjects = async ( For stream resolution, build a map to allow for also returning the UUID to match the format returned by the graph lookup */ - const streamLookupMap: Record = {}; + let streamLookupMap: Record = {}; /** For legacy nodes, the graph lookup only needs the UUID */ const legacyUuids = []; @@ -100,6 +100,16 @@ export const getIndexedResearchObjects = async ( } } + /** + * fallback to _getIndexedResearchObjects() when resolving locally + * because calls to getHistoryFromStreams() never returns due to + * RESOLVER_URL not configured for local dpid resolution + */ + if (process.env.NODE_ENV === 'dev') { + legacyUuids.push(...paddedUuids); + streamLookupMap = {}; + } + let streamHistory = []; if (Object.keys(streamLookupMap).length > 0) { logger.info({ streamLookupMap }, 'Querying resolver for history'); @@ -109,7 +119,7 @@ export const getIndexedResearchObjects = async ( let legacyHistory = []; if (legacyUuids.length > 0) { - logger.info({ legacyUuids }, 'Falling back to subgraph query for history'); + logger.info({ legacyUuids, _urlSafeBase64s }, 'Falling back to subgraph query for history'); legacyHistory = (await _getIndexedResearchObjects(legacyUuids)).researchObjects; logger.info({ legacyHistory }, 'Subgraph history for nodes found'); } @@ -201,7 +211,7 @@ export const _getIndexedResearchObjects = async ( export const getTimeForTxOrCommits = async (txOrCommits: string[]): Promise> => { const isTx = (id: string) => id.startsWith('0x'); const txIds = txOrCommits.filter(isTx); - const commitIdStrs = txOrCommits.filter(id => !isTx(id)); + const commitIdStrs = txOrCommits.filter((id) => !isTx(id)); const commitTimeMap = await getCommitTimestamps(commitIdStrs); const txTimeMap = await getTxTimestamps(txIds); @@ -221,15 +231,15 @@ const getTxTimestamps = async (txIds: string[]): Promise> try { const graphTxTimestamps = await getTxTimeFromGraph(txIds); const timeMap = graphTxTimestamps.reduce( - (acc, { id, time }) => ({ ...acc, [id]: time}), + (acc, { id, time }) => ({ ...acc, [id]: time }), {} as Record, ); return timeMap; } catch (err) { logger.error({ txIds, err }, 'failed to get tx timestamps from graph, returning empty map'); return {}; - }; -} + } +}; type TransactionsWithTimestamp = { researchObjectVersions: { id: string; time: string }[]; From 85e042d4698565e43586bd25ddc12d4b474a3718 Mon Sep 17 00:00:00 2001 From: kadami <86646883+kadamidev@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:44:32 +0000 Subject: [PATCH 29/35] adjust match filter to handle arrays --- .../src/services/ElasticSearchService.ts | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/desci-server/src/services/ElasticSearchService.ts b/desci-server/src/services/ElasticSearchService.ts index 100732aa3..9a9a84383 100644 --- a/desci-server/src/services/ElasticSearchService.ts +++ b/desci-server/src/services/ElasticSearchService.ts @@ -255,15 +255,31 @@ function buildFilter(filter: Filter) { return { match_phrase: { [filter.field]: { query: filter.value, analyzer: 'edge_ngram_analyzer' } } }; case 'match': - const matchQuery = { - match: { - [filter.field]: { - query: filter.value, - operator: filter.matchLogic || 'or', - // ...(filter.fuzziness && { fuzziness: filter.fuzziness }), + let matchQuery; + if (Array.isArray(filter.value)) { + matchQuery = { + bool: { + should: filter.value.map((value) => ({ + match: { + [filter.field]: { + query: value, + operator: filter.matchLogic || 'or', + }, + }, + })), + minimum_should_match: 1, }, - }, - }; + }; + } else { + matchQuery = { + match: { + [filter.field]: { + query: filter.value, + operator: filter.matchLogic || 'or', + }, + }, + }; + } if (filter.field.includes('.') && !filter.field.includes('institutions')) { const [nestedPath, nestedField] = filter.field.split('.'); From 56ee6069087a3d1cbc5d46c340d01a6c32b15852 Mon Sep 17 00:00:00 2001 From: m0ar Date: Fri, 27 Sep 2024 10:12:21 +0200 Subject: [PATCH 30/35] ceramic: enable metrics scraping endpoiunts, fixed dev deployment strat, bump dev to anchor healing rc --- .ceramicDev.config.json | 7 +-- ceramic-k8s/ceramic_deployment_dev.yaml | 58 +++++++++++++++--------- ceramic-k8s/ceramic_deployment_prod.yaml | 50 +++++++++++++------- docker-compose.dev.yml | 1 + 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/.ceramicDev.config.json b/.ceramicDev.config.json index 3f5d12749..d49f91f76 100644 --- a/.ceramicDev.config.json +++ b/.ceramicDev.config.json @@ -1,5 +1,5 @@ { - "anchor": { }, + "anchor": {}, "http-api": { "cors-allowed-origins": [ ".*" @@ -16,8 +16,8 @@ "log-level": 2 }, "metrics": { - "metrics-exporter-enabled": false, - "metrics-port": 9090 + "prometheus-exporter-enabled": true, + "prometheus-exporter-port": 9464 }, "network": { "name": "inmemory" @@ -33,3 +33,4 @@ "models": [] } } + diff --git a/ceramic-k8s/ceramic_deployment_dev.yaml b/ceramic-k8s/ceramic_deployment_dev.yaml index 68f3fd464..5aa80dcdf 100644 --- a/ceramic-k8s/ceramic_deployment_dev.yaml +++ b/ceramic-k8s/ceramic_deployment_dev.yaml @@ -11,10 +11,7 @@ spec: matchLabels: App: JsCeramicDev strategy: - rollingUpdate: - maxSurge: 25% - maxUnavailable: 25% - type: RollingUpdate + type: Recreate template: metadata: annotations: @@ -41,7 +38,9 @@ spec: App: JsCeramicDev spec: containers: - - image: ceramicnetwork/js-ceramic:6.5.0 + # Enables a fix allowing us to heal dropped anchors on genesis commits: + # https://github.com/ceramicnetwork/js-ceramic/pull/3280 + - image: ceramicnetwork/js-ceramic:6.6.0-rc.0 name: js-ceramic-dev command: ["/bin/bash", "-c"] args: @@ -49,6 +48,8 @@ spec: ports: - containerPort: 7007 name: http-api + - containerPort: 9464 + name: metrics resources: limits: cpu: 2 @@ -70,9 +71,38 @@ spec: port: http-api failureThreshold: 60 periodSeconds: 1 - serviceAccountName: "vault-auth" --- +apiVersion: v1 +kind: Service +metadata: + name: js-ceramic-dev-service-internal + labels: + App: JsCeramicDev +spec: + type: ClusterIP + selector: + App: JsCeramicDev + ports: + - name: metrics + port: 9464 + targetPort: metrics +--- +apiVersion: v1 +kind: Service +metadata: + name: public-js-ceramic-dev-service + labels: + App: JsCeramicDev +spec: + type: LoadBalancer + selector: + App: JsCeramicDev + ports: + - protocol: TCP + port: 80 + targetPort: http-api +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -91,19 +121,3 @@ spec: name: public-js-ceramic-dev-service port: number: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: public-js-ceramic-dev-service - labels: - App: JsCeramicDev -spec: - type: LoadBalancer - selector: - App: JsCeramicDev - ports: - - protocol: TCP - name: http - port: 80 - targetPort: 7007 diff --git a/ceramic-k8s/ceramic_deployment_prod.yaml b/ceramic-k8s/ceramic_deployment_prod.yaml index 7ed15bb76..f2c0352e3 100644 --- a/ceramic-k8s/ceramic_deployment_prod.yaml +++ b/ceramic-k8s/ceramic_deployment_prod.yaml @@ -73,6 +73,8 @@ spec: ports: - containerPort: 7007 name: http-api + - containerPort: 9464 + name: metrics resources: limits: cpu: 2 @@ -107,9 +109,39 @@ spec: - name: ceramic-prod-persistent-storage persistentVolumeClaim: claimName: ceramic-prod-persistent-storage-pvc - serviceAccountName: "vault-auth" --- +apiVersion: v1 +kind: Service +metadata: + name: js-ceramic-prod-service-internal + labels: + App: JsCeramicProd +spec: + type: ClusterIP + selector: + App: JsCeramicProd + ports: + - name: metrics + port: 9464 + targetPort: metrics +--- +apiVersion: v1 +kind: Service +metadata: + name: public-js-ceramic-prod-service + labels: + App: JsCeramicProd +spec: + type: LoadBalancer + selector: + App: JsCeramicProd + ports: + - protocol: TCP + name: http + port: 80 + targetPort: http-api +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -128,19 +160,3 @@ spec: name: public-js-ceramic-prod-service port: number: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: public-js-ceramic-prod-service - labels: - App: JsCeramicProd -spec: - type: LoadBalancer - selector: - App: JsCeramicProd - ports: - - protocol: TCP - name: http - port: 80 - targetPort: 7007 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index faba1ceca..6302874a1 100755 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -188,6 +188,7 @@ services: container_name: ceramic ports: - "7007:7007" + - "9464:9464" environment: NODE_ENV: production CERAMIC_PUBSUB_QPS_LIMIT: 500 From 90af1fe881715d87867cc9c7713365c3f7a824f2 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Fri, 27 Sep 2024 18:49:48 +0200 Subject: [PATCH 31/35] fix radar query for ceramic streams --- desci-server/src/controllers/communities/util.ts | 8 ++++---- desci-server/src/services/Attestation.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/desci-server/src/controllers/communities/util.ts b/desci-server/src/controllers/communities/util.ts index 71d135df6..e57bef7bb 100644 --- a/desci-server/src/controllers/communities/util.ts +++ b/desci-server/src/controllers/communities/util.ts @@ -32,7 +32,7 @@ export const resolveLatestNode = async (radar: Partial) => { const selectAttributes: (keyof typeof discovery)[] = ['ownerId', 'NodeCover']; const node: Partial = _.pick(discovery, selectAttributes); const publishedVersions = - (await prisma.$queryRaw`SELECT * from "NodeVersion" where "nodeId" = ${discovery.id} AND "transactionId" IS NOT NULL ORDER BY "createdAt" DESC`) as NodeVersion[]; + (await prisma.$queryRaw`SELECT * from "NodeVersion" where "nodeId" = ${discovery.id} AND ("transactionId" IS NOT NULL or "commitId" IS NOT NULL) ORDER BY "createdAt" DESC`) as NodeVersion[]; // const nodeVersions = (await getNodeVersion logger.info({ uuid: discovery.uuid, publishedVersions }, 'Resolve node'); @@ -61,13 +61,13 @@ export const resolveLatestNode = async (radar: Partial) => { }; export const getNodeVersion = async (uuid: string) => { - let indexingResults: { researchObjects: IndexedResearchObject[]}; + let indexingResults: { researchObjects: IndexedResearchObject[] }; try { indexingResults = await getIndexedResearchObjects([uuid]); const researchObject = indexingResults.researchObjects[0]; return researchObject?.versions?.length ?? 0; } catch (e) { - logger.error({ uuid, indexingResults }, "getNodeVersion failed"); + logger.error({ uuid, indexingResults }, 'getNodeVersion failed'); throw e; - }; + } }; diff --git a/desci-server/src/services/Attestation.ts b/desci-server/src/services/Attestation.ts index 8a7fbbb1a..e25bac597 100644 --- a/desci-server/src/services/Attestation.ts +++ b/desci-server/src/services/Attestation.ts @@ -75,7 +75,7 @@ export class AttestationService { const node = await prisma.node.findFirst({ where: { uuid: nodeUuid } }); const publishedNodeVersions = - (await prisma.$queryRaw`SELECT COUNT(*) from "NodeVersion" where "nodeId" = ${node.id} AND "transactionId" IS NOT NULL`) as number; + (await prisma.$queryRaw`SELECT COUNT(*) from "NodeVersion" where "nodeId" = ${node.id} AND ("transactionId" IS NOT NULL or "commitId" IS NOT NULL)`) as number; if (nodeVersion >= publishedNodeVersions) { logger.warn({ nodeVersion, publishedNodeVersions }, 'Invalid Node version'); From 7995fc2c2101c4b90b8a226b2658654eaf2b9290 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Sat, 28 Sep 2024 04:40:40 +0200 Subject: [PATCH 32/35] update sentry tracing to log db and http reqs in sentry --- desci-server/package.json | 1 + desci-server/prisma/schema.prisma | 3 +- desci-server/src/server.ts | 3 +- desci-server/yarn.lock | 157 ++++++++++++++++++++++++++++-- 4 files changed, 156 insertions(+), 8 deletions(-) diff --git a/desci-server/package.json b/desci-server/package.json index 739b8e4aa..470a8a218 100755 --- a/desci-server/package.json +++ b/desci-server/package.json @@ -71,6 +71,7 @@ "@sendgrid/mail": "^7.7.0", "@sentry/cli": "^2.35.0", "@sentry/node": "^8.29.0", + "@sentry/profiling-node": "^8.32.0", "@sentry/tracing": "^7.12.0", "@types/lodash-es": "^4.17.12", "@types/mkdirp": "^1.0.2", diff --git a/desci-server/prisma/schema.prisma b/desci-server/prisma/schema.prisma index 8d18e432f..d44babc72 100755 --- a/desci-server/prisma/schema.prisma +++ b/desci-server/prisma/schema.prisma @@ -1,5 +1,6 @@ generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + previewFeatures = ["tracing"] } datasource db { diff --git a/desci-server/src/server.ts b/desci-server/src/server.ts index a84f90f26..8b0a74191 100644 --- a/desci-server/src/server.ts +++ b/desci-server/src/server.ts @@ -7,6 +7,7 @@ import type { Server as HttpServer } from 'http'; // import path from 'path'; import * as Sentry from '@sentry/node'; +import { nodeProfilingIntegration } from '@sentry/profiling-node'; import * as Tracing from '@sentry/tracing'; import bodyParser from 'body-parser'; import cookieParser from 'cookie-parser'; @@ -224,7 +225,7 @@ class AppServer { Sentry.init({ dsn: 'https://d508a5c408f34b919ccd94aac093e076@o1330109.ingest.sentry.io/6619754', release: 'desci-nodes-server@' + process.env.npm_package_version, - integrations: [new Tracing.Integrations.Prisma({ client: prisma })], + integrations: [Sentry.prismaIntegration(), nodeProfilingIntegration()], // Set tracesSampleRate to 1.0 to capture 100% // of transactions for performance monitoring. // We recommend adjusting this value in production diff --git a/desci-server/yarn.lock b/desci-server/yarn.lock index fa90e947d..73dfed96f 100644 --- a/desci-server/yarn.lock +++ b/desci-server/yarn.lock @@ -3922,6 +3922,15 @@ "@opentelemetry/instrumentation" "^0.40.0" "@opentelemetry/semantic-conventions" "^1.0.0" +"@opentelemetry/instrumentation-amqplib@^0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.42.0.tgz#b3cab5a7207736a30d769962eed3af3838f986c4" + integrity sha512-fiuU6OKsqHJiydHWgTRQ7MnIrJ2lEqsdgFtNIH4LbAUJl/5XmrIeoDzDnox+hfkgWK65jsleFuQDtYb5hW1koQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-aws-lambda@^0.35.3": version "0.35.3" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.35.3.tgz#19c42ba680f93a595a38edb06b28bac2b7da6f92" @@ -3979,6 +3988,13 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/connect" "3.4.35" +"@opentelemetry/instrumentation-dataloader@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.12.0.tgz#de03a3948dec4f15fed80aa424d6bd5d6a8d10c7" + integrity sha512-pnPxatoFE0OXIZDQhL2okF//dmbiWFzcSc8pUg9TqofCLYZySSxDCgQc69CJBo5JnI3Gz1KP+mOjS4WAeRIH4g== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-dataloader@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.4.3.tgz#13d2041d44d8ff4b9ebfc48bcbf9cbda74b61d00" @@ -4145,6 +4161,14 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/ioredis4" "npm:@types/ioredis@^4.28.10" +"@opentelemetry/instrumentation-kafkajs@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.3.0.tgz#6687bce4dac8b90ef8ccbf1b662d5d1e95a34414" + integrity sha512-UnkZueYK1ise8FXQeKlpBd7YYUtC7mM8J0wzUSccEfc/G8UqHQqAzIyYCUOUPUKp8GsjLnWOOK/3hJc4owb7Jg== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-knex@^0.31.4": version "0.31.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.31.4.tgz#15c747acf3de94f15bbad852140ec584b79d5cd9" @@ -4374,6 +4398,14 @@ "@opentelemetry/semantic-conventions" "^1.0.0" "@types/tedious" "^4.0.6" +"@opentelemetry/instrumentation-undici@0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.6.0.tgz#9436ee155c8dcb0b760b66947c0e0f347688a5ef" + integrity sha512-ABJBhm5OdhGmbh0S/fOTE4N69IZ00CsHC5ijMYfzbw3E5NwLgpQk5xsljaECrJ8wz1SfXbO03FiSuu5AyRAkvQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-winston@^0.31.4": version "0.31.4" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.31.4.tgz#09c160dfeb935ea6981d898176be79dea67f31c8" @@ -4646,7 +4678,7 @@ "@opentelemetry/core" "1.25.0" "@opentelemetry/semantic-conventions" "1.25.0" -"@opentelemetry/resources@1.26.0", "@opentelemetry/resources@^1.25.1": +"@opentelemetry/resources@1.26.0", "@opentelemetry/resources@^1.25.1", "@opentelemetry/resources@^1.26.0": version "1.26.0" resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.26.0.tgz#da4c7366018bd8add1f3aa9c91c6ac59fd503cef" integrity sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw== @@ -4786,7 +4818,7 @@ "@opentelemetry/resources" "1.8.0" "@opentelemetry/semantic-conventions" "1.8.0" -"@opentelemetry/sdk-trace-base@^1.22", "@opentelemetry/sdk-trace-base@^1.25.1": +"@opentelemetry/sdk-trace-base@^1.22", "@opentelemetry/sdk-trace-base@^1.25.1", "@opentelemetry/sdk-trace-base@^1.26.0": version "1.26.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz#0c913bc6d2cfafd901de330e4540952269ae579c" integrity sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw== @@ -5685,6 +5717,54 @@ "@sentry/types" "8.29.0" "@sentry/utils" "8.29.0" +"@sentry/core@8.32.0": + version "8.32.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.32.0.tgz#7c4b74afa7a15bd31f5e6881aac82ccfd753e1d6" + integrity sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA== + dependencies: + "@sentry/types" "8.32.0" + "@sentry/utils" "8.32.0" + +"@sentry/node@8.32.0": + version "8.32.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.32.0.tgz#68822b3246fb2ed7418f21795ed539a18058cfa8" + integrity sha512-a2PoFA9j/HmJVGF/zXJhLP6QhRHGye/2EznQdHOELsH1BkeMgBaXl7D52r2E/b7qki647lXrdbspB6jid8NycA== + dependencies: + "@opentelemetry/api" "^1.9.0" + "@opentelemetry/context-async-hooks" "^1.25.1" + "@opentelemetry/core" "^1.25.1" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-amqplib" "^0.42.0" + "@opentelemetry/instrumentation-connect" "0.39.0" + "@opentelemetry/instrumentation-dataloader" "0.12.0" + "@opentelemetry/instrumentation-express" "0.42.0" + "@opentelemetry/instrumentation-fastify" "0.39.0" + "@opentelemetry/instrumentation-fs" "0.15.0" + "@opentelemetry/instrumentation-generic-pool" "0.39.0" + "@opentelemetry/instrumentation-graphql" "0.43.0" + "@opentelemetry/instrumentation-hapi" "0.41.0" + "@opentelemetry/instrumentation-http" "0.53.0" + "@opentelemetry/instrumentation-ioredis" "0.43.0" + "@opentelemetry/instrumentation-kafkajs" "0.3.0" + "@opentelemetry/instrumentation-koa" "0.43.0" + "@opentelemetry/instrumentation-mongodb" "0.47.0" + "@opentelemetry/instrumentation-mongoose" "0.42.0" + "@opentelemetry/instrumentation-mysql" "0.41.0" + "@opentelemetry/instrumentation-mysql2" "0.41.0" + "@opentelemetry/instrumentation-nestjs-core" "0.40.0" + "@opentelemetry/instrumentation-pg" "0.44.0" + "@opentelemetry/instrumentation-redis-4" "0.42.0" + "@opentelemetry/instrumentation-undici" "0.6.0" + "@opentelemetry/resources" "^1.26.0" + "@opentelemetry/sdk-trace-base" "^1.26.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@prisma/instrumentation" "5.19.1" + "@sentry/core" "8.32.0" + "@sentry/opentelemetry" "8.32.0" + "@sentry/types" "8.32.0" + "@sentry/utils" "8.32.0" + import-in-the-middle "^1.11.0" + "@sentry/node@^8.29.0": version "8.29.0" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.29.0.tgz#6e462b8802356a630c56733dc795a4035464c4ab" @@ -5732,6 +5812,27 @@ "@sentry/types" "8.29.0" "@sentry/utils" "8.29.0" +"@sentry/opentelemetry@8.32.0": + version "8.32.0" + resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.32.0.tgz#4af02c17102635e4b34942d2e82d3620ddb7d95a" + integrity sha512-YCD8EnwJJ2ab3zWWtu5VrvHP/6Ss6GGQH0TYx2cfeGG3c0wTA/5zYx9JR4i3hUtOh1pifN34HlY0yyQHD4yctg== + dependencies: + "@sentry/core" "8.32.0" + "@sentry/types" "8.32.0" + "@sentry/utils" "8.32.0" + +"@sentry/profiling-node@^8.32.0": + version "8.32.0" + resolved "https://registry.yarnpkg.com/@sentry/profiling-node/-/profiling-node-8.32.0.tgz#cb4bf0512dc7a6f0a9058529f3c4dbb8963a597d" + integrity sha512-wKE2JAvDM3DjVNFKd2YLzwhXFS3SurLxDPpvA1EjMQ9Me7sR5ssO9jFn3e6et5oT3e3EzGDyTfbrJ81BNQuZhQ== + dependencies: + "@sentry/core" "8.32.0" + "@sentry/node" "8.32.0" + "@sentry/types" "8.32.0" + "@sentry/utils" "8.32.0" + detect-libc "^2.0.2" + node-abi "^3.61.0" + "@sentry/tracing@^7.12.0": version "7.106.1" resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.106.1.tgz#1142fa308f7cb90f16291b969b2d5fb14c1858ae" @@ -5749,6 +5850,11 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.29.0.tgz#c19e43524b8e7766028f4da8f02eddcc33518541" integrity sha512-j4gX3ctzgD4xVWllXAhm6M+kHFEvrFoUPFq60X/pgkjsWCocGuhtNfB0rW43ICG8hCnlz8IYl7O7b8V8qY7SPg== +"@sentry/types@8.32.0": + version "8.32.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.32.0.tgz#dfd8aa9449a5f793b9c720888819a74a11f1790d" + integrity sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg== + "@sentry/utils@7.106.1": version "7.106.1" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.106.1.tgz#7761ed50d489286dd1e482570c498f675445ce82" @@ -5763,6 +5869,13 @@ dependencies: "@sentry/types" "8.29.0" +"@sentry/utils@8.32.0": + version "8.32.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.32.0.tgz#99a4298ee8fd7208ade470931c19d71c571dfce8" + integrity sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ== + dependencies: + "@sentry/types" "8.32.0" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -9905,7 +10018,7 @@ detect-indent@6.1.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -detect-libc@^2.0.0: +detect-libc@^2.0.0, detect-libc@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== @@ -13929,6 +14042,13 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" +node-abi@^3.61.0: + version "3.68.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.68.0.tgz#8f37fb02ecf4f43ebe694090dcb52e0c4cc4ba25" + integrity sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A== + dependencies: + semver "^7.3.5" + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -16207,7 +16327,16 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16280,7 +16409,7 @@ stringify-object@3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16294,6 +16423,13 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -17329,7 +17465,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -17347,6 +17483,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 32044b52a2a6ed9d5700ddcb94db27a14bec6b75 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Mon, 30 Sep 2024 13:09:56 +0200 Subject: [PATCH 33/35] apply profile sample rate --- desci-server/src/server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/desci-server/src/server.ts b/desci-server/src/server.ts index 8b0a74191..172964d54 100644 --- a/desci-server/src/server.ts +++ b/desci-server/src/server.ts @@ -230,6 +230,7 @@ class AppServer { // of transactions for performance monitoring. // We recommend adjusting this value in production tracesSampleRate: 1.0, + profilesSampleRate: 1.0, }); Sentry.setupExpressErrorHandler(this.app); } else { From 2d8146a92c3e25d540289f9566c9156b3c922058 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Mon, 30 Sep 2024 21:23:56 +0200 Subject: [PATCH 34/35] fix build --- .github/workflows/build-server.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index b25936ff2..e25841008 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -60,7 +60,11 @@ jobs: - name: Set up Sentry CLI run: | + npm install -g @sentry/cli sentry-cli --version + sentry-cli login --auth-token $SENTRY_AUTH_TOKEN + # append to .env + echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> desci-server/.env - name: Run tests run: | From 6ac88929ffa48a267ec17fa7e5791ddfd3894f75 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Mon, 30 Sep 2024 21:25:34 +0200 Subject: [PATCH 35/35] fix build2 --- .github/workflows/build-server.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index e25841008..8d2ea2a31 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -124,8 +124,11 @@ jobs: - name: Set up Sentry CLI run: | + npm install -g @sentry/cli sentry-cli --version sentry-cli login --auth-token $SENTRY_AUTH_TOKEN + # append to .env + echo -e "\nSENTRY_AUTH_TOKEN=$SENTRY_AUTH" >> desci-server/.env - name: Build and tag the image (DEV) if: github.ref == 'refs/heads/develop'