diff --git a/common/bundestagio/src/models/NonNamedVotesAI/index.ts b/common/bundestagio/src/models/NonNamedVotesAI/index.ts new file mode 100644 index 000000000..311b8c7ec --- /dev/null +++ b/common/bundestagio/src/models/NonNamedVotesAI/index.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import NonNamedVotesAiSchema, { INonNamedVotesAi } from './schema'; + +export const NonNamedVotesAiModel = mongoose.model('NonNamedVotesAi', NonNamedVotesAiSchema); +export { NonNamedVotesAiSchema, INonNamedVotesAi }; diff --git a/common/bundestagio/src/models/NonNamedVotesAI/schema.ts b/common/bundestagio/src/models/NonNamedVotesAI/schema.ts new file mode 100644 index 000000000..09f5f25ee --- /dev/null +++ b/common/bundestagio/src/models/NonNamedVotesAI/schema.ts @@ -0,0 +1,22 @@ +import { Schema, SchemaTimestampsConfig, Document } from 'mongoose'; + +export interface INonNamedVotesAi extends Document, SchemaTimestampsConfig { + pdfUrl: string; + assistantId?: string; + vectorStoreId?: string; + threadId?: string; + fileId?: string; +} + +const NonNamedVotesAiSchema = new Schema( + { + pdfUrl: { type: String, unique: true, index: true, required: true }, + assistantId: { type: String }, + vectorStoreId: { type: String }, + threadId: { type: String }, + fileId: { type: String }, + }, + { timestamps: true }, +); + +export default NonNamedVotesAiSchema; diff --git a/common/bundestagio/src/models/index.ts b/common/bundestagio/src/models/index.ts index 530903fb8..0e817d5e9 100644 --- a/common/bundestagio/src/models/index.ts +++ b/common/bundestagio/src/models/index.ts @@ -6,3 +6,4 @@ export * from './NamedPoll'; export * from './Procedure'; export * from './User'; export * from './PlenaryMinute'; +export * from './NonNamedVotesAI'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8922b6bbe..c9342cc04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -192,7 +192,7 @@ importers: devDependencies: '@graphql-codegen/cli': specifier: 5.0.0 - version: 5.0.0(@parcel/watcher@2.4.1)(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2) + version: 5.0.0(@parcel/watcher@2.4.1)(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2) '@graphql-codegen/typescript': specifier: 4.0.1 version: 4.0.1(graphql@16.8.1) @@ -219,7 +219,7 @@ importers: version: 5.0.1(@types/eslint@8.56.10)(eslint-config-prettier@9.0.0(eslint@8.52.0))(eslint@8.52.0)(prettier@3.0.3) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + version: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) prettier: specifier: 3.0.3 version: 3.0.3 @@ -1092,6 +1092,46 @@ importers: specifier: ^5.4.5 version: 5.4.5 + services/non-named-votes-ai: + dependencies: + '@democracy-deutschland/bundestagio-common': + specifier: workspace:* + version: link:../../common/bundestagio + express: + specifier: ^4.19.2 + version: 4.19.2 + express-rate-limit: + specifier: ^7.4.0 + version: 7.4.0(express@4.19.2) + helmet: + specifier: ^7.1.0 + version: 7.1.0 + openai: + specifier: ^4.55.1 + version: 4.55.4 + pino: + specifier: ^9.3.2 + version: 9.3.2 + devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/node': + specifier: ^22.1.0 + version: 22.1.0 + tsconfig: + specifier: workspace:* + version: link:../../packages/tsconfig + tsup: + specifier: 'catalog:' + version: 8.0.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5))(typescript@5.4.5) + tsup-config: + specifier: workspace:* + version: link:../../packages/tsup-config + tsx: + specifier: ^4.11.0 + version: 4.11.0 + services/qr-code-handler: dependencies: express: @@ -3717,6 +3757,9 @@ packages: '@types/node@15.14.9': resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + '@types/node@18.19.44': + resolution: {integrity: sha512-ZsbGerYg72WMXUIE9fYxtvfzLEuq6q8mKERdWFnqTmOvudMxnz+CBNRoOwJ2kNpFOncrKjT1hZwxjlFgQ9qvQA==} + '@types/node@20.12.12': resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} @@ -4144,6 +4187,10 @@ packages: resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==} engines: {node: '>= 4.0.0'} + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -5938,6 +5985,12 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + express-rate-limit@7.4.0: + resolution: {integrity: sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==} + engines: {node: '>= 16'} + peerDependencies: + express: 4 || 5 || ^5.0.0-beta.1 + express@4.19.2: resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} engines: {node: '>= 0.10.0'} @@ -6150,6 +6203,9 @@ packages: forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + form-data-encoder@4.0.2: resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==} engines: {node: '>= 18'} @@ -6166,6 +6222,10 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -6520,6 +6580,10 @@ packages: resolution: {integrity: sha512-3KdYYOPcFaQ8539nxxsuR8Pr1PYw/rx2ju40rVTKVR0PBKVnVPaz1acBzsLfofYlWNZgf/2rJcd0trj9Ss5kuw==} engines: {node: '>=16.0.0'} + helmet@7.1.0: + resolution: {integrity: sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==} + engines: {node: '>=16.0.0'} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -7262,7 +7326,6 @@ packages: jsondiffpatch@0.1.43: resolution: {integrity: sha512-lvOkGuk7gl9Rr4M/SfN530TslMb9QZG9PM5uznjb6oVwkkHNt7rgPNOBO59mwegJ/0Msx/yjwYzdiuoET6a87Q==} hasBin: true - bundledDependencies: [] jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -7940,6 +8003,10 @@ packages: resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} engines: {node: ^16 || ^18 || >= 20} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -8131,6 +8198,15 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + openai@4.55.4: + resolution: {integrity: sha512-TEC75Y6U/OKIJp9fHao3zkTYfKLYGqXdD2TI+xN2Zd5W8KNKvv6E4/OBTOW7jg7fySfrBrhy5fYzBbyBcdHEtQ==} + hasBin: true + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -10533,6 +10609,10 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + webcrypto-core@1.8.0: resolution: {integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==} @@ -12084,7 +12164,7 @@ snapshots: graphql: 16.8.1 tslib: 2.6.2 - '@graphql-codegen/cli@5.0.0(@parcel/watcher@2.4.1)(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2)': + '@graphql-codegen/cli@5.0.0(@parcel/watcher@2.4.1)(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2)': dependencies: '@babel/generator': 7.24.6 '@babel/template': 7.24.6 @@ -12094,12 +12174,12 @@ snapshots: '@graphql-tools/apollo-engine-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/code-file-loader': 8.1.2(graphql@16.8.1) '@graphql-tools/git-loader': 8.0.6(graphql@16.8.1) - '@graphql-tools/github-loader': 8.0.1(@types/node@20.12.13)(graphql@16.8.1) + '@graphql-tools/github-loader': 8.0.1(@types/node@22.1.0)(graphql@16.8.1) '@graphql-tools/graphql-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/load': 8.0.2(graphql@16.8.1) - '@graphql-tools/prisma-loader': 8.0.4(@types/node@20.12.13)(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.2(@types/node@20.12.13)(graphql@16.8.1) + '@graphql-tools/prisma-loader': 8.0.4(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.2(@types/node@22.1.0)(graphql@16.8.1) '@graphql-tools/utils': 10.2.1(graphql@16.8.1) '@whatwg-node/fetch': 0.8.8 chalk: 4.1.2 @@ -12107,7 +12187,7 @@ snapshots: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.8.1 - graphql-config: 5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2) + graphql-config: 5.0.3(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2) inquirer: 8.2.6 is-glob: 4.0.3 jiti: 1.21.0 @@ -12423,6 +12503,19 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@graphql-tools/executor-http@1.0.9(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@repeaterjs/repeater': 3.0.6 + '@whatwg-node/fetch': 0.9.17 + extract-files: 11.0.0 + graphql: 16.8.1 + meros: 1.3.0(@types/node@22.1.0) + tslib: 2.6.2 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + '@graphql-tools/executor-legacy-ws@1.0.6(graphql@16.8.1)': dependencies: '@graphql-tools/utils': 10.2.1(graphql@16.8.1) @@ -12471,6 +12564,21 @@ snapshots: - encoding - supports-color + '@graphql-tools/github-loader@8.0.1(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/executor-http': 1.0.9(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/graphql-tag-pluck': 8.3.1(graphql@16.8.1) + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@whatwg-node/fetch': 0.9.17 + graphql: 16.8.1 + tslib: 2.6.2 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + - encoding + - supports-color + '@graphql-tools/graphql-file-loader@8.0.1(graphql@16.8.1)': dependencies: '@graphql-tools/import': 7.0.1(graphql@16.8.1) @@ -12559,6 +12667,32 @@ snapshots: - supports-color - utf-8-validate + '@graphql-tools/prisma-loader@8.0.4(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@graphql-tools/url-loader': 8.0.2(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@types/js-yaml': 4.0.9 + '@whatwg-node/fetch': 0.9.17 + chalk: 4.1.2 + debug: 4.3.4 + dotenv: 16.4.5 + graphql: 16.8.1 + graphql-request: 6.1.0(graphql@16.8.1) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.4 + jose: 5.3.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + scuid: 1.1.0 + tslib: 2.6.2 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - supports-color + - utf-8-validate + '@graphql-tools/relay-operation-optimizer@7.0.1(graphql@16.8.1)': dependencies: '@ardatan/relay-compiler': 12.0.0(graphql@16.8.1) @@ -12607,6 +12741,28 @@ snapshots: - encoding - utf-8-validate + '@graphql-tools/url-loader@8.0.2(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/delegate': 10.0.11(graphql@16.8.1) + '@graphql-tools/executor-graphql-ws': 1.1.2(graphql@16.8.1) + '@graphql-tools/executor-http': 1.0.9(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/executor-legacy-ws': 1.0.6(graphql@16.8.1) + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@graphql-tools/wrap': 10.0.5(graphql@16.8.1) + '@types/ws': 8.5.10 + '@whatwg-node/fetch': 0.9.17 + graphql: 16.8.1 + isomorphic-ws: 5.0.0(ws@8.17.0) + tslib: 2.6.2 + value-or-promise: 1.0.12 + ws: 8.17.0 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - utf-8-validate + '@graphql-tools/utils@10.2.1(graphql@16.8.1)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) @@ -12772,27 +12928,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12813,21 +12969,21 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12855,14 +13011,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12928,7 +13084,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -12998,7 +13154,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -13904,7 +14060,7 @@ snapshots: '@types/jsdom@21.1.6': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -13957,13 +14113,17 @@ snapshots: '@types/node-fetch@2.6.11': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 form-data: 4.0.0 '@types/node-gcm@1.0.5': {} '@types/node@15.14.9': {} + '@types/node@18.19.44': + dependencies: + undici-types: 5.26.5 + '@types/node@20.12.12': dependencies: undici-types: 5.26.5 @@ -14004,7 +14164,7 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/semver@7.5.8': {} @@ -14043,22 +14203,22 @@ snapshots: '@types/whatwg-url@8.2.2': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/webidl-conversions': 7.0.3 '@types/ws@8.5.10': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/xml2js@0.4.14': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/xmldom@0.1.34': {} '@types/xsd-schema-validator@0.5.7': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/yargs-parser@21.0.3': {} @@ -14506,7 +14666,7 @@ snapshots: '@wry/context@0.4.4': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 tslib: 1.14.1 '@wry/context@0.7.4': @@ -14590,6 +14750,10 @@ snapshots: dependencies: humanize-ms: 1.2.1 + agentkeepalive@4.5.0: + dependencies: + humanize-ms: 1.2.1 + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 @@ -15889,13 +16053,13 @@ snapshots: - supports-color - utf-8-validate - create-jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + create-jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -15904,13 +16068,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + create-jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -16758,8 +16922,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.2(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -16802,13 +16966,13 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.3.4 enhanced-resolve: 5.16.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -16830,24 +16994,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -16878,7 +17032,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -16888,7 +17042,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -17117,7 +17271,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 require-like: 0.1.2 event-stream@3.3.4: @@ -17170,6 +17324,10 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 + express-rate-limit@7.4.0(express@4.19.2): + dependencies: + express: 4.19.2 + express@4.19.2: dependencies: accepts: 1.3.8 @@ -17431,6 +17589,8 @@ snapshots: forever-agent@0.6.1: optional: true + form-data-encoder@1.7.2: {} + form-data-encoder@4.0.2: {} form-data@2.3.3: @@ -17452,6 +17612,11 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + forwarded@0.2.0: {} fraction.js@4.3.7: {} @@ -17773,7 +17938,7 @@ snapshots: graphemer@1.4.0: {} - graphql-config@5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2): + graphql-config@5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.4.5): dependencies: '@graphql-tools/graphql-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) @@ -17781,7 +17946,7 @@ snapshots: '@graphql-tools/merge': 9.0.4(graphql@16.8.1) '@graphql-tools/url-loader': 8.0.2(@types/node@20.12.13)(graphql@16.8.1) '@graphql-tools/utils': 10.2.1(graphql@16.8.1) - cosmiconfig: 8.3.6(typescript@5.2.2) + cosmiconfig: 8.3.6(typescript@5.4.5) graphql: 16.8.1 jiti: 1.21.0 minimatch: 4.2.3 @@ -17794,15 +17959,15 @@ snapshots: - typescript - utf-8-validate - graphql-config@5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.4.5): + graphql-config@5.0.3(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2): dependencies: '@graphql-tools/graphql-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/load': 8.0.2(graphql@16.8.1) '@graphql-tools/merge': 9.0.4(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.2(@types/node@20.12.13)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.2(@types/node@22.1.0)(graphql@16.8.1) '@graphql-tools/utils': 10.2.1(graphql@16.8.1) - cosmiconfig: 8.3.6(typescript@5.4.5) + cosmiconfig: 8.3.6(typescript@5.2.2) graphql: 16.8.1 jiti: 1.21.0 minimatch: 4.2.3 @@ -17927,6 +18092,8 @@ snapshots: ow: 0.28.2 tslib: 2.6.2 + helmet@7.1.0: {} + help-me@5.0.0: {} hoist-non-react-statics@3.3.2: @@ -18471,16 +18638,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + jest-cli@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + create-jest: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -18490,16 +18657,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + jest-cli@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + create-jest: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -18529,7 +18696,7 @@ snapshots: - ts-node optional: true - jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -18555,12 +18722,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.12.13 - ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2) + ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -18585,13 +18752,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -18616,12 +18783,11 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.12.13 - ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5) + '@types/node': 22.1.0 + ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2) transitivePeerDependencies: - babel-plugin-macros - supports-color - optional: true jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)): dependencies: @@ -18689,7 +18855,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.12.13 + '@types/node': 22.1.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -18763,7 +18929,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -18791,7 +18957,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 @@ -18837,7 +19003,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -18856,7 +19022,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -18865,7 +19031,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -18876,24 +19042,24 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-cli: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-cli: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -19513,6 +19679,10 @@ snapshots: optionalDependencies: '@types/node': 20.12.13 + meros@1.3.0(@types/node@22.1.0): + optionalDependencies: + '@types/node': 22.1.0 + methods@1.1.2: {} micromatch@4.0.7: @@ -19801,6 +19971,8 @@ snapshots: node-addon-api@7.1.0: optional: true + node-domexception@1.0.0: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -20004,6 +20176,18 @@ snapshots: dependencies: mimic-fn: 4.0.0 + openai@4.55.4: + dependencies: + '@types/node': 18.19.44 + '@types/node-fetch': 2.6.11 + abort-controller: 3.0.0 + agentkeepalive: 4.5.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + opener@1.5.2: {} openid-client@5.6.5: @@ -20639,7 +20823,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.12.13 + '@types/node': 22.1.0 long: 5.2.3 optional: true @@ -22330,7 +22514,7 @@ snapshots: '@swc/core': 1.5.24(@swc/helpers@0.5.11) optional: true - ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2): + ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -22344,32 +22528,32 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.5.24(@swc/helpers@0.5.11) - optional: true - ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5): + ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.12.13 + '@types/node': 22.1.0 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.4.5 + typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.5.24(@swc/helpers@0.5.11) + optional: true ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5): dependencies: @@ -22869,6 +23053,8 @@ snapshots: web-streams-polyfill@3.3.3: {} + web-streams-polyfill@4.0.0-beta.3: {} + webcrypto-core@1.8.0: dependencies: '@peculiar/asn1-schema': 2.3.8 diff --git a/services/non-named-votes-ai/.env.example b/services/non-named-votes-ai/.env.example new file mode 100644 index 000000000..f35949833 --- /dev/null +++ b/services/non-named-votes-ai/.env.example @@ -0,0 +1,12 @@ +# https://platform.openai.com/organization/api-keys +OPENAI_API_KEY= +# Secret key to authenticate the requests +SECRET=CHANGE_ME +# Port to run the server on +PORT=3005 +# MongoDB connection string +DB_URL=mongodb://localhost:27017/bundestagio +# Log level for pino +PINO_LOG_LEVEL=info +# Allowed domains for CORS +ALLOWED_DOMAINS=https://dserver.bundestag.de diff --git a/services/non-named-votes-ai/.gitignore b/services/non-named-votes-ai/.gitignore new file mode 100644 index 000000000..2eea525d8 --- /dev/null +++ b/services/non-named-votes-ai/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/services/non-named-votes-ai/FLOW.md b/services/non-named-votes-ai/FLOW.md new file mode 100644 index 000000000..53bab7e10 --- /dev/null +++ b/services/non-named-votes-ai/FLOW.md @@ -0,0 +1,66 @@ +# Flow Charts + +Dieser Service liefert den Textabschnitt, in dem über eine **nicht namentliche Abstimmung** abgestimmt wird. Dafür benötigt er eine **URL** zu einem PDF mit einem Plenarsaalprotokoll, den **Titel** der Gesetzgebung oder des Antrags und optional die **Dokumentennummer**. + +## High Level + +```mermaid +flowchart TD + Start([Input: '´plenaarsaalprotokoll-url, titel, dokumentennummer]) + Error([Es konnte kein text gefunden werden, da das Plenarsaalprotokoll nicht gefunden wurde]) + Output([Output: Abstimmungstext der nicht namentlichen abstimmung]) + + Start --> |Ok|Output + Start --> |Fehler|Error +``` + +## In Deep + +```mermaid +flowchart TD + Start([Start mit '´plenaarsaalprotokoll-url, titel, dokumentennummer]) + RequestDb([Datenbankabfrage: Plenarsaalprotokoll 'url']) + AssistantExists{Assistent existiert?} + CreateAssistant([Assistent erstellen]) + UploadFile([Datei bei openAI hochladen]) + CreateVectorStore([VectorStore erstellen]) + CheckOpenAI{VectorStore bei openAI verfügbar?} + UseExistingVectorStore([Bestehenden VectorStore verwenden]) + CreateThread([Neuen Thread erstellen]) + StartThread([Thread mit Assistant starten]) + SaveEntry([Assistent und VectorStore in Datenbank speichern]) + GetMessages([Antwort abrufen]) + FilterMessage([Antwort zwischen ### herausfiltern]) + DeleteThread([Thread löschen]) + ReturnMessage([Antwort zurückgeben]) + Error([Fehler: Kein Plenarsaalprotokoll vorhanden]) + ReturnError([Fehlerantwort]) + + Start --> RequestDb + + RequestDb -->|Eintrag vorhanden| AssistantExists + RequestDb -->|Kein Eintrag| Error + + Error --> ReturnError + + AssistantExists --> |Ja| CheckOpenAI + AssistantExists --> |Nein| CreateAssistant + + CreateAssistant --> CreateThread + + CheckOpenAI -->|Ja| UseExistingVectorStore + CheckOpenAI -->|Nein| UploadFile + + UseExistingVectorStore --> CreateThread + UploadFile --> CreateVectorStore + + CreateVectorStore --> CreateThread + + + CreateThread --> StartThread + StartThread --> SaveEntry + SaveEntry --> GetMessages + GetMessages --> FilterMessage + FilterMessage --> DeleteThread + DeleteThread --> ReturnMessage +``` diff --git a/services/non-named-votes-ai/package.json b/services/non-named-votes-ai/package.json new file mode 100644 index 000000000..146789eda --- /dev/null +++ b/services/non-named-votes-ai/package.json @@ -0,0 +1,30 @@ +{ + "name": "non-named-votes-ai", + "version": "1.0.0", + "description": "", + "main": "build/index.js", + "scripts": { + "dev": "tsx --env-file .env --watch src/index.ts", + "build": "tsup-node", + "start": "node build/index.js" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@democracy-deutschland/bundestagio-common": "workspace:*", + "express": "^4.19.2", + "express-rate-limit": "^7.4.0", + "helmet": "^7.1.0", + "openai": "^4.55.1", + "pino": "^9.3.2" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.1.0", + "tsconfig": "workspace:*", + "tsup": "catalog:", + "tsup-config": "workspace:*", + "tsx": "^4.11.0" + } +} diff --git a/services/non-named-votes-ai/src/authMiddleware.ts b/services/non-named-votes-ai/src/authMiddleware.ts new file mode 100644 index 000000000..b6f6af838 --- /dev/null +++ b/services/non-named-votes-ai/src/authMiddleware.ts @@ -0,0 +1,31 @@ +import { Request, Response, NextFunction } from 'express'; +import crypto from 'crypto'; + +export const authMiddleware = (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers.authorization; + + if (!authHeader) { + return res.status(401).send('Authorization header missing'); + } + + const token = authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).send('Token missing'); + } + + const secret = process.env.SECRET; + + if (!secret) { + return res.status(500).send('Server configuration error'); + } + + const tokenBuffer = Buffer.from(token); + const secretBuffer = Buffer.from(secret); + + if (tokenBuffer.length !== secretBuffer.length || !crypto.timingSafeEqual(tokenBuffer, secretBuffer)) { + return res.status(401).send('Unauthorized'); + } + + return next(); +}; diff --git a/services/non-named-votes-ai/src/beschlusstextController.ts b/services/non-named-votes-ai/src/beschlusstextController.ts new file mode 100644 index 000000000..a2601b6ef --- /dev/null +++ b/services/non-named-votes-ai/src/beschlusstextController.ts @@ -0,0 +1,65 @@ +import { Request, Response } from 'express'; +import { NonNamedVotesAiModel } from '@democracy-deutschland/bundestagio-common'; +import { + createThread, + ensureAssistantId, + ensureFile, + ensureVectorStore, + filterMessage, + getMessageList, + runThread, + saveToDatabase, +} from './utils'; +import { log } from './logger'; + +export const getBeschlusstext = async (req: Request, res: Response) => { + const { pdfUrl, title, drucksachen } = req.query as { pdfUrl: string; title: string; drucksachen?: string[] }; + log.info('getBeschlusstext start'); + log.debug(`getBeschlusstext: ${pdfUrl}, ${title}, ${drucksachen}`); + + if (!pdfUrl || typeof pdfUrl !== 'string') { + return res.status(400).json({ message: 'pdfUrl muss angegeben werden' }); + } + + if (!title || typeof title !== 'string') { + return res.status(400).json({ message: 'title muss angegeben werden' }); + } + + let databaseEntry = await NonNamedVotesAiModel.findOne({ pdfUrl: pdfUrl }); + log.debug({ databaseEntry }); + + // Ensure OpenAi Objects exist + const file = await ensureFile({ pdfUrl: pdfUrl as string, file_id: databaseEntry?.fileId }); + log.debug({ file }); + databaseEntry = await saveToDatabase({ pdfUrl, file, databaseEntry }); + const vectorStore = await ensureVectorStore({ file_id: file.id, vector_store_id: databaseEntry?.vectorStoreId }); + log.debug({ vectorStore }); + databaseEntry = await saveToDatabase({ pdfUrl, file, vectorStore, databaseEntry }); + const assistant = await ensureAssistantId({ + vector_store_id: vectorStore.id, + assistant_id: databaseEntry?.assistantId, + }); + log.debug({ assistant }); + databaseEntry = await saveToDatabase({ pdfUrl, file, vectorStore, assistant, databaseEntry }); + const thread = await createThread({ title, drucksachen }); + log.debug({ thread }); + databaseEntry = await saveToDatabase({ pdfUrl, file, vectorStore, assistant, thread, databaseEntry }); + + // Run + const run = await runThread({ assistant_id: assistant.id, threadId: thread.id }); + log.debug({ run }); + const messages = await getMessageList(thread.id, run.id); + log.debug({ messages }); + const message = await filterMessage(messages); + log.debug({ message }); + + const beschlusstext = { + pdfUrl, + text: message, + }; + log.debug({ beschlusstext }); + + log.info('getBeschlusstext end'); + log.debug(`getBeschlusstext: ${pdfUrl}, ${title}, ${drucksachen} -> ${beschlusstext.text}`); + return res.json(beschlusstext); +}; diff --git a/services/non-named-votes-ai/src/index.ts b/services/non-named-votes-ai/src/index.ts new file mode 100644 index 000000000..c5e679eb6 --- /dev/null +++ b/services/non-named-votes-ai/src/index.ts @@ -0,0 +1,30 @@ +import express from 'express'; +import { authMiddleware } from './authMiddleware'; +import { getBeschlusstext } from './beschlusstextController'; +import { mongoConnect } from '@democracy-deutschland/bundestagio-common'; +import { log } from './logger'; +import helmet from 'helmet'; +import { limiter } from './rateLimiter'; + +(async () => { + await mongoConnect(process.env.DB_URL!); + + const app = express(); + + // Disable x-powered-by header to make it harder for attackers + app.disable('x-powered-by'); + + // Helmet verwenden, um HTTP-Header zu sichern + app.use(helmet()); + + // apply rate limiter to all requests + app.use(limiter); + + app.get('/beschlusstext', authMiddleware, getBeschlusstext); + + const PORT = process.env.PORT || 3005; + app.listen(PORT, () => { + log.info(`Server is running`); + log.debug(`Server is running on port ${PORT}`); + }); +})(); diff --git a/services/non-named-votes-ai/src/logger.ts b/services/non-named-votes-ai/src/logger.ts new file mode 100644 index 000000000..6eda4095d --- /dev/null +++ b/services/non-named-votes-ai/src/logger.ts @@ -0,0 +1,13 @@ +import pino from 'pino'; + +const log = pino({ + level: process.env.PINO_LOG_LEVEL || 'info', + transport: { + target: 'pino-pretty', + options: { + colorize: true, + }, + }, +}); + +export { log }; diff --git a/services/non-named-votes-ai/src/rateLimiter.ts b/services/non-named-votes-ai/src/rateLimiter.ts new file mode 100644 index 000000000..f286ee424 --- /dev/null +++ b/services/non-named-votes-ai/src/rateLimiter.ts @@ -0,0 +1,6 @@ +// set up rate limiter: maximum of five requests per minute +import RateLimit from 'express-rate-limit'; +export const limiter = RateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // max 100 requests per windowMs +}); diff --git a/services/non-named-votes-ai/src/test.ts b/services/non-named-votes-ai/src/test.ts new file mode 100644 index 000000000..49917d021 --- /dev/null +++ b/services/non-named-votes-ai/src/test.ts @@ -0,0 +1,24 @@ +import { log } from './logger'; +import { createThread, ensureAssistantId, ensureFile, filterMessage, getMessageList, runThread } from './utils'; + +let assistantId = 'asst_lK6r9ApD1ZYNvqwEOkleXmfd'; +let fileId = 'file-pEo6HmgAFFGlMvmMKLYeSiEF'; +const vector_store_ids = ['vs_i8KmJXDEtEdUyomai5oCavfz']; + +export const getBeschlusstext = async (pdfUrl: string, title: string, drucksachen: string[]) => { + const assistant = await ensureAssistantId({ vector_store_id: vector_store_ids[0], assistant_id: assistantId }); + assistantId = assistant.id; + + fileId = await ensureFile({ pdfUrl, file_id: fileId }).then((file) => file.id); + + const thread = await createThread({ title, drucksachen }); + log.debug('thread id', thread.id); + + log.debug(thread.tool_resources?.file_search); + + const run = await runThread({ threadId: thread.id, assistant_id: assistantId }); + + const messages = await getMessageList(thread.id, run.id); + + log.debug(filterMessage(messages)); +}; diff --git a/services/non-named-votes-ai/src/utils.ts b/services/non-named-votes-ai/src/utils.ts new file mode 100644 index 000000000..0340546a6 --- /dev/null +++ b/services/non-named-votes-ai/src/utils.ts @@ -0,0 +1,198 @@ +import OpenAI from 'openai'; +import { Assistant } from 'openai/resources/beta/assistants'; +import { MessagesPage } from 'openai/resources/beta/threads/messages'; +import { VectorStore } from 'openai/resources/beta/vector-stores/vector-stores'; +import { log } from './logger'; +import { INonNamedVotesAi, NonNamedVotesAiModel } from '@democracy-deutschland/bundestagio-common'; +import { ObjectId } from 'mongoose'; +import { FileObject } from 'openai/resources'; +import { Thread } from 'openai/resources/beta/threads/threads'; + +const openai = new OpenAI(); +const name = process.env.NAME ?? 'Non-Named Votes AI'; + +export const retriveAssistant = async (assistantId: string) => await openai.beta.assistants.retrieve(assistantId); +export const createAssistant = async ({ vector_store_id }: { vector_store_id: string }) => + await openai.beta.assistants.create({ + name, + instructions: ` + Du erhältst vom User einen Titel und eine Dokumentennummer zu einer Abstimmung aus dem Plenarsaalprotokoll des Deutschen Bundestages. Deine Aufgabe ist es, den exakten Wortlaut des Textabschnitts zur Abstimmung, inklusive der namentlichen Nennung der Parteien und ihres Abstimmungsverhaltens, wie er im Protokoll steht, wiederzugeben. Füge keine zusätzlichen Informationen oder Erklärungen hinzu. Gib den Textabschnitt wortwörtlich und vollständig so zurück, wie er im Dokument erscheint. + Setze am anfang des Textes ein "#####" und am Ende ein "#####" um den Textabschnitt zu kennzeichnen. + `, + model: 'gpt-4o', + tools: [{ type: 'file_search' }], + tool_resources: { file_search: { vector_store_ids: [vector_store_id] } }, + temperature: 0, + }); +export const ensureAssistantId = async ({ + vector_store_id, + assistant_id, +}: { + vector_store_id: string; + assistant_id?: string; +}) => { + let assistant: Assistant | undefined = undefined; + if (assistant_id) { + assistant = await retriveAssistant(assistant_id); + } + + if (!assistant) { + assistant = await createAssistant({ vector_store_id }); + } + return assistant; +}; + +export const retriveFile = async (fileId: string) => await openai.files.retrieve(fileId); +export const createFile = async ({ pdfUrl }: { pdfUrl: string }) => + await openai.files.create({ + file: await validateAndFetchFile(pdfUrl), + purpose: 'assistants', + }); +export const ensureFile = async ({ pdfUrl, file_id }: { pdfUrl: string; file_id?: string }) => { + let file = undefined; + if (file_id) { + file = await retriveFile(file_id); + } + + if (!file) { + file = await createFile({ pdfUrl }); + } + + return file; +}; + +export const retriveVectorStore = async (vectorStoreId: string) => + await openai.beta.vectorStores.retrieve(vectorStoreId); +export const createVectorStore = async ({ file_ids }: { file_ids: string[] }) => + await openai.beta.vectorStores.create({ + file_ids, + }); +export const ensureVectorStore = async ({ + file_id, + vector_store_id, +}: { + file_id: string; + vector_store_id?: string; +}) => { + let vectorStore: VectorStore | undefined = undefined; + if (vector_store_id) { + vectorStore = await retriveVectorStore(vector_store_id); + } + + if (!vectorStore) { + vectorStore = await createVectorStore({ file_ids: [file_id] }); + } + + return vectorStore; +}; + +export const createThread = async ({ title, drucksachen }: { title: string; drucksachen?: string[] }) => + await openai.beta.threads.create({ + messages: [ + { + role: 'user', + content: `Titel: "${title}" + Drucksachen: ${drucksachen?.join(', ')}`, + // Attach the new file to the message. + // attachments: [{ file_id: fileId, tools: [{ type: 'file_search' }] }], + }, + ], + }); +export const runThread = async ({ threadId, assistant_id }: { threadId: string; assistant_id: string }) => + await openai.beta.threads.runs.createAndPoll(threadId, { + assistant_id, + }); + +export const getMessageList = async (threadId: string, runId: string) => { + return await openai.beta.threads.messages.list(threadId, { + run_id: runId, + }); +}; + +export const extractBetweenHashesFromBeschluss = (text: string): string | null => { + const regex = /#####([\s\S]*?)#####/; + const match = text.match(regex); + return match ? match[1].trim() : null; +}; + +export const filterMessage = (messages: MessagesPage) => { + const message = messages.data.pop()!; + if (message.content[0].type === 'text') { + const { text } = message.content[0]; + + return extractBetweenHashesFromBeschluss(text.value); + return text.value; + } + throw new Error('No text message found'); +}; + +// Überprüft, ob die Domain in der Whitelist enthalten ist +const isValidDomain = (url: string): boolean => { + const allowedDomains = process.env.ALLOWED_DOMAINS?.split(',') ?? []; + const parsedUrl = new URL(url); + return allowedDomains.includes(parsedUrl.origin); +}; + +// Desinfiziert die URL und stellt sicher, dass sie sicher ist +const sanitizeUrl = (url: string): string => { + // Führen Sie hier notwendige Desinfektions- und Formatierungsprozesse durch + // Zum Beispiel: Entfernen von fragwürdigen Query-Parametern oder Formatierungsproblemen + return url.trim(); // Beispiel: einfaches Trimmen von Leerzeichen +}; + +// Ruft die Datei von einer sicheren URL ab +const fetchSecureFile = async (url: string) => { + const sanitizedUrl = sanitizeUrl(url); + if (!isValidDomain(sanitizedUrl)) { + throw new Error('Domain ist nicht erlaubt'); + } + const response = await fetch(sanitizedUrl); + return response; +}; + +// Hauptfunktion, die die Validierung und den Abruf kombiniert +export const validateAndFetchFile = async (pdfUrl: string) => { + try { + const file = await fetchSecureFile(pdfUrl); + return file; + } catch (error) { + log.error(`Fehler beim Abrufen der Datei:${(error as Error).message}`); + throw error; + } +}; + +export const saveToDatabase = async ({ + pdfUrl, + file, + vectorStore, + assistant, + thread, + databaseEntry, +}: { + pdfUrl: string; + file?: FileObject; + vectorStore?: VectorStore; + assistant?: Assistant; + thread?: Thread; + databaseEntry: + | (INonNamedVotesAi & { + _id: ObjectId; + }) + | null; +}) => { + if (!databaseEntry) { + return await new NonNamedVotesAiModel({ + pdfUrl, + fileId: file?.id, + vectorStoreId: vectorStore?.id, + assistantId: assistant?.id, + threadId: thread?.id, + }).save(); + } + if (pdfUrl) databaseEntry.pdfUrl = pdfUrl; + if (file) databaseEntry.fileId = file.id; + if (vectorStore) databaseEntry.vectorStoreId = vectorStore.id; + if (assistant) databaseEntry.assistantId = assistant.id; + if (thread) databaseEntry.threadId = thread.id; + return await databaseEntry.save(); +}; diff --git a/services/non-named-votes-ai/tsconfig.json b/services/non-named-votes-ai/tsconfig.json new file mode 100644 index 000000000..6b7962de0 --- /dev/null +++ b/services/non-named-votes-ai/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "tsconfig/base.json" +} diff --git a/services/non-named-votes-ai/tsup.config.ts b/services/non-named-votes-ai/tsup.config.ts new file mode 100644 index 000000000..af5ec3392 --- /dev/null +++ b/services/non-named-votes-ai/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { tsupConfig } from 'tsup-config'; + +export default defineConfig({ + ...tsupConfig, +});