first commit
This commit is contained in:
parent
cb7314e386
commit
300a5205df
@ -17,9 +17,9 @@ export default tseslint.config(
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
},
|
||||
sourceType: 'commonjs',
|
||||
sourceType: 'module', // Changé de 'commonjs' à 'module'
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
project: './tsconfig.json', // Ajout du chemin vers tsconfig
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
@ -28,7 +28,13 @@ export default tseslint.config(
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn'
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
'prettier/prettier': ['error', {
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
}],
|
||||
},
|
||||
},
|
||||
);
|
||||
708
package-lock.json
generated
708
package-lock.json
generated
@ -9,10 +9,18 @@
|
||||
"version": "0.0.1",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@nestjs/bull": "^11.0.4",
|
||||
"@nestjs/cache-manager": "^3.0.1",
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/event-emitter": "^3.0.1",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/schedule": "^6.0.1",
|
||||
"@nestjs/swagger": "^11.2.1",
|
||||
"@prisma/client": "^6.17.1",
|
||||
"cache-manager-redis-store": "^3.0.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
@ -713,6 +721,16 @@
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/@cacheable/utils": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.1.0.tgz",
|
||||
"integrity": "sha512-ZdxfOiaarMqMj+H7qwlt5EBKWaeGihSYVHdQv5lUsbn8MJJOTW82OIwirQ39U5tMZkNvy3bQE+ryzC+xTAb9/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"keyv": "^5.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||
@ -1341,6 +1359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz",
|
||||
"integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
@ -2045,6 +2070,13 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@keyv/serialize": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz",
|
||||
"integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@lukeed/csprng": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
|
||||
@ -2054,6 +2086,96 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/tsdoc": {
|
||||
"version": "0.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
|
||||
"integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
|
||||
"integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
|
||||
"integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
|
||||
"integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
||||
@ -2067,6 +2189,47 @@
|
||||
"@tybys/wasm-util": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/bull": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/bull/-/bull-11.0.4.tgz",
|
||||
"integrity": "sha512-QVz2PR/rJF/isy7otVnMTSqLf/O71p9Ka7lBZt9Gm+NQFv8fcH2L11GL7TA0whyCcw/kAX5iRepUXz/wed4JoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nestjs/bull-shared": "^11.0.4",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
|
||||
"bull": "^3.3 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/bull-shared": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.4.tgz",
|
||||
"integrity": "sha512-VBJcDHSAzxQnpcDfA0kt9MTGUD1XZzfByV70su0W0eDCQ9aqIEBlzWRW21tv9FG9dIut22ysgDidshdjlnczLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/cache-manager": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-3.0.1.tgz",
|
||||
"integrity": "sha512-4UxTnR0fsmKL5YDalU2eLFVnL+OBebWUpX+hEduKGncrVKH4PPNoiRn1kXyOCjmzb0UvWgqubpssNouc8e0MCw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0",
|
||||
"cache-manager": ">=6",
|
||||
"keyv": ">=5",
|
||||
"rxjs": "^7.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/cli": {
|
||||
"version": "11.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.10.tgz",
|
||||
@ -2329,6 +2492,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/config": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.2.tgz",
|
||||
"integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "16.4.7",
|
||||
"dotenv-expand": "12.0.1",
|
||||
"lodash": "4.17.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"rxjs": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/config/node_modules/dotenv": {
|
||||
"version": "16.4.7",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
|
||||
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/core": {
|
||||
"version": "11.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.7.tgz",
|
||||
@ -2370,6 +2560,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/event-emitter": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz",
|
||||
"integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eventemitter2": "6.4.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/mapped-types": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz",
|
||||
"integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"class-transformer": "^0.4.0 || ^0.5.0",
|
||||
"class-validator": "^0.13.0 || ^0.14.0",
|
||||
"reflect-metadata": "^0.1.12 || ^0.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"class-transformer": {
|
||||
"optional": true
|
||||
},
|
||||
"class-validator": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.7.tgz",
|
||||
@ -2391,6 +2614,19 @@
|
||||
"@nestjs/core": "^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schedule": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.1.tgz",
|
||||
"integrity": "sha512-v3yO6cSPAoBSSyH67HWnXHzuhPhSNZhRmLY38JvCt2sqY8sPMOODpcU1D79iUMFf7k16DaMEbL4Mgx61ZhiC8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron": "4.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schematics": {
|
||||
"version": "11.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz",
|
||||
@ -2489,6 +2725,39 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/swagger": {
|
||||
"version": "11.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.1.tgz",
|
||||
"integrity": "sha512-1MS7xf0pzc1mofG53xrrtrurnziafPUHkqzRm4YUVPA/egeiMaSerQBD/feiAeQ2BnX0WiLsTX4HQFO0icvOjQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/tsdoc": "0.15.1",
|
||||
"@nestjs/mapped-types": "2.1.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"path-to-regexp": "8.3.0",
|
||||
"swagger-ui-dist": "5.29.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fastify/static": "^8.0.0",
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"class-transformer": "*",
|
||||
"class-validator": "*",
|
||||
"reflect-metadata": "^0.1.12 || ^0.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@fastify/static": {
|
||||
"optional": true
|
||||
},
|
||||
"class-transformer": {
|
||||
"optional": true
|
||||
},
|
||||
"class-validator": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/testing": {
|
||||
"version": "11.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.7.tgz",
|
||||
@ -2703,6 +2972,78 @@
|
||||
"@prisma/debug": "6.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
|
||||
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/client": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.2",
|
||||
"generic-pool": "3.9.0",
|
||||
"yallist": "4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/client/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@redis/graph": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
|
||||
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/json": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
|
||||
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/search": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
|
||||
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/time-series": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
|
||||
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@scarf/scarf": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
||||
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.34.41",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
||||
@ -2979,6 +3320,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz",
|
||||
"integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/methods": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
|
||||
@ -3081,6 +3428,12 @@
|
||||
"@types/superagent": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/validator": {
|
||||
"version": "13.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz",
|
||||
"integrity": "sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
|
||||
@ -4047,7 +4400,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/array-timsort": {
|
||||
@ -4364,6 +4716,25 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bull": {
|
||||
"version": "4.16.5",
|
||||
"resolved": "https://registry.npmjs.org/bull/-/bull-4.16.5.tgz",
|
||||
"integrity": "sha512-lDsx2BzkKe7gkCYiT5Acj02DpTwDznl/VNN7Psn7M3USPG7Vs/BaClZJJTAG+ufAR9++N1/NiUTdaFBWDIl5TQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cron-parser": "^4.9.0",
|
||||
"get-port": "^5.1.1",
|
||||
"ioredis": "^5.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"msgpackr": "^1.11.2",
|
||||
"semver": "^7.5.2",
|
||||
"uuid": "^8.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@ -4413,6 +4784,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cache-manager": {
|
||||
"version": "7.2.4",
|
||||
"resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.4.tgz",
|
||||
"integrity": "sha512-skmhkqXjPCBmrb70ctEx4zwFk7vb0RdFXlVGYWnFZ8pKvkzdFrFFKSJ1IaKduGfkryHOJvb7q2PkGmonmL+UGw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@cacheable/utils": "^2.1.0",
|
||||
"keyv": "^5.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/cache-manager-redis-store": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cache-manager-redis-store/-/cache-manager-redis-store-3.0.1.tgz",
|
||||
"integrity": "sha512-o560kw+dFqusC9lQJhcm6L2F2fMKobJ5af+FoR2PdnMVdpQ3f3Bz6qzvObTGyvoazQJxjQNWgMQeChP4vRTuXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"redis": "^4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
@ -4576,6 +4970,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/class-validator": {
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz",
|
||||
"integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/validator": "^13.11.8",
|
||||
"libphonenumber-js": "^1.11.1",
|
||||
"validator": "^13.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
@ -4694,6 +5099,15 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -4925,6 +5339,32 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cron": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-4.3.3.tgz",
|
||||
"integrity": "sha512-B/CJj5yL3sjtlun6RtYHvoSB26EmQ2NUmhq9ZiJSyKIM4K/fqfh9aelDFlIayD2YMeFZqWLi9hHV+c+pq2Djkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/luxon": "~3.7.0",
|
||||
"luxon": "~3.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||
"integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"luxon": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@ -5029,6 +5469,16 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@ -5045,6 +5495,17 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-newline": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
|
||||
@ -5080,7 +5541,6 @@
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@ -5089,6 +5549,21 @@
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz",
|
||||
"integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@ -5503,6 +5978,12 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter2": {
|
||||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
|
||||
"integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
@ -5860,6 +6341,16 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/flat-cache/node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||
@ -6041,6 +6532,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/generic-pool": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
||||
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@ -6095,6 +6595,19 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-port": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz",
|
||||
"integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
@ -6473,6 +6986,31 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz",
|
||||
"integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "1.4.0",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
@ -7483,7 +8021,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@ -7567,13 +8104,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
||||
"dev": true,
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.3.tgz",
|
||||
"integrity": "sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
"@keyv/serialize": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/leven": {
|
||||
@ -7600,6 +8137,12 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/libphonenumber-js": {
|
||||
"version": "1.12.24",
|
||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.24.tgz",
|
||||
"integrity": "sha512-l5IlyL9AONj4voSd7q9xkuQOL4u8Ty44puTic7J88CmdXkxfGsRfoVLXHCxppwehgpb/Chdb80FFehHqjN3ItQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
@ -7660,9 +8203,22 @@
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
@ -7704,6 +8260,15 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
@ -7938,6 +8503,39 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/msgpackr": {
|
||||
"version": "1.11.5",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz",
|
||||
"integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"optionalDependencies": {
|
||||
"msgpackr-extract": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/msgpackr-extract": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
|
||||
"integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build-optional-packages": "5.2.2"
|
||||
},
|
||||
"bin": {
|
||||
"download-msgpackr-prebuilds": "bin/download-prebuilds.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
|
||||
@ -8072,6 +8670,22 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-gyp-build-optional-packages": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
|
||||
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-gyp-build-optional-packages": "bin.js",
|
||||
"node-gyp-build-optional-packages-optional": "optional.js",
|
||||
"node-gyp-build-optional-packages-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/node-int64": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||
@ -8810,6 +9424,46 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/redis": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz",
|
||||
"integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"./packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@redis/bloom": "1.2.0",
|
||||
"@redis/client": "1.6.1",
|
||||
"@redis/graph": "1.1.1",
|
||||
"@redis/json": "1.0.7",
|
||||
"@redis/search": "1.2.0",
|
||||
"@redis/time-series": "1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"redis-errors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||
@ -8999,7 +9653,6 @@
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -9240,6 +9893,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
@ -9517,6 +10177,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-dist": {
|
||||
"version": "5.29.4",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.4.tgz",
|
||||
"integrity": "sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scarf/scarf": "=1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-observable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||
@ -10256,6 +10925,16 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
@ -10278,6 +10957,15 @@
|
||||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.15",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
||||
@ -20,10 +20,18 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/bull": "^11.0.4",
|
||||
"@nestjs/cache-manager": "^3.0.1",
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/event-emitter": "^3.0.1",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/schedule": "^6.0.1",
|
||||
"@nestjs/swagger": "^11.2.1",
|
||||
"@prisma/client": "^6.17.1",
|
||||
"cache-manager-redis-store": "^3.0.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
38
prisma/migrations/20251021230409_init/migration.sql
Normal file
38
prisma/migrations/20251021230409_init/migration.sql
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `country` to the `Partner` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `passwordHash` to the `Partner` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Partner" ADD COLUMN "companyInfo" JSONB,
|
||||
ADD COLUMN "country" TEXT NOT NULL,
|
||||
ADD COLUMN "keysRotatedAt" TIMESTAMP(3),
|
||||
ADD COLUMN "passwordHash" TEXT NOT NULL,
|
||||
ALTER COLUMN "status" SET DEFAULT 'PENDING';
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AuthSession" (
|
||||
"id" TEXT NOT NULL,
|
||||
"sessionId" TEXT NOT NULL,
|
||||
"partnerId" TEXT NOT NULL,
|
||||
"userId" TEXT,
|
||||
"msisdn" TEXT NOT NULL,
|
||||
"operator" TEXT NOT NULL,
|
||||
"country" TEXT NOT NULL,
|
||||
"authMethod" TEXT NOT NULL,
|
||||
"challengeId" TEXT,
|
||||
"status" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "AuthSession_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "AuthSession_sessionId_key" ON "AuthSession"("sessionId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "AuthSession" ADD CONSTRAINT "AuthSession_partnerId_fkey" FOREIGN KEY ("partnerId") REFERENCES "Partner"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@ -39,22 +39,7 @@ enum SubscriptionStatus {
|
||||
FAILED
|
||||
}
|
||||
|
||||
model Partner {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
email String @unique
|
||||
apiKey String @unique
|
||||
secretKey String
|
||||
status String
|
||||
callbacks Json?
|
||||
metadata Json?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
users User[]
|
||||
subscriptions Subscription[]
|
||||
payments Payment[]
|
||||
}
|
||||
|
||||
model Operator {
|
||||
id String @id @default(cuid())
|
||||
@ -174,3 +159,43 @@ model Webhook {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Partner {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
email String @unique
|
||||
passwordHash String
|
||||
apiKey String @unique
|
||||
secretKey String
|
||||
status String @default("PENDING")
|
||||
companyInfo Json?
|
||||
callbacks Json?
|
||||
country String
|
||||
metadata Json?
|
||||
keysRotatedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
users User[]
|
||||
subscriptions Subscription[]
|
||||
payments Payment[]
|
||||
authSessions AuthSession[]
|
||||
}
|
||||
|
||||
model AuthSession {
|
||||
id String @id @default(cuid())
|
||||
sessionId String @unique
|
||||
partnerId String
|
||||
userId String?
|
||||
msisdn String
|
||||
operator String
|
||||
country String
|
||||
authMethod String
|
||||
challengeId String?
|
||||
status String
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
partner Partner @relation(fields: [partnerId], references: [id])
|
||||
}
|
||||
@ -30,7 +30,7 @@ export interface OperatorConfig {
|
||||
|
||||
export const operatorsConfig = (): Record<string, OperatorConfig> => ({
|
||||
ORANGE_CIV: {
|
||||
name: 'Orange Côte d\'Ivoire',
|
||||
name: 'Orange Côte d Ivoire',
|
||||
baseUrl: process.env.ORANGE_CIV_BASE_URL || 'https://api.bizao.com',
|
||||
authType: 'OTP',
|
||||
endpoints: {
|
||||
|
||||
@ -7,7 +7,7 @@ async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
// Global prefix
|
||||
app.setGlobalPrefix('api/v2');
|
||||
app.setGlobalPrefix('api/v1');
|
||||
|
||||
// Validation
|
||||
app.useGlobalPipes(
|
||||
|
||||
31
src/modules/auth/auth.module.ts
Normal file
31
src/modules/auth/auth.module.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { JwtStrategy } from './strategies/jwt.strategy';
|
||||
import { ApiKeyStrategy } from './strategies/api-key.strategy';
|
||||
import { PrismaService } from '../../shared/services/prisma.service';
|
||||
import { OperatorsModule } from '../operators/operators.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
PassportModule.register({ defaultStrategy: 'jwt' }),
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
secret: configService.get<string>('app.jwtSecret'),
|
||||
signOptions: {
|
||||
expiresIn: configService.get<string>('app.jwtExpiresIn'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
OperatorsModule,
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, JwtStrategy, ApiKeyStrategy, PrismaService],
|
||||
exports: [AuthService, JwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
245
src/modules/auth/auth.service.ts
Normal file
245
src/modules/auth/auth.service.ts
Normal file
@ -0,0 +1,245 @@
|
||||
import {
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { PrismaService } from '../../shared/services/prisma.service';
|
||||
import { OperatorsService } from '../operators/operators.service';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { AuthInitDto, AuthValidateDto, LoginDto } from './dto/auth.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly operatorsService: OperatorsService,
|
||||
) {}
|
||||
|
||||
async initializeUserAuth(partnerId: string, dto: AuthInitDto) {
|
||||
// Vérifier le partenaire
|
||||
const partner = await this.prisma.partner.findUnique({
|
||||
where: { id: partnerId },
|
||||
});
|
||||
|
||||
if (!partner || partner.status !== 'ACTIVE') {
|
||||
throw new UnauthorizedException('Invalid partner');
|
||||
}
|
||||
|
||||
// Déterminer l'opérateur basé sur le numéro
|
||||
const operator = this.detectOperator(dto.msisdn, dto.country);
|
||||
|
||||
// Obtenir l'adaptateur approprié
|
||||
const adapter = this.operatorsService.getAdapter(operator, dto.country);
|
||||
|
||||
// Initialiser l'authentification avec l'opérateur
|
||||
const authResponse = await adapter.initializeAuth({
|
||||
msisdn: dto.msisdn,
|
||||
country: dto.country,
|
||||
metadata: dto.metadata,
|
||||
});
|
||||
|
||||
// Créer une session temporaire
|
||||
const session = await this.prisma.authSession.create({
|
||||
data: {
|
||||
sessionId: authResponse.sessionId,
|
||||
partnerId: partnerId,
|
||||
msisdn: dto.msisdn,
|
||||
operator: operator,
|
||||
country: dto.country,
|
||||
authMethod: dto.authMethod,
|
||||
challengeId: authResponse.challengeId,
|
||||
status: 'PENDING',
|
||||
expiresAt: authResponse.expiresAt,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
sessionId: session.sessionId,
|
||||
authMethod: dto.authMethod,
|
||||
status: 'PENDING',
|
||||
redirectUrl: authResponse.redirectUrl,
|
||||
challengeId: authResponse.challengeId,
|
||||
expiresAt: authResponse.expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
async validateUserAuth(dto: AuthValidateDto) {
|
||||
// Récupérer la session
|
||||
const session = await this.prisma.authSession.findUnique({
|
||||
where: { sessionId: dto.sessionId },
|
||||
});
|
||||
|
||||
if (!session) {
|
||||
throw new BadRequestException('Invalid session');
|
||||
}
|
||||
|
||||
if (session.status !== 'PENDING') {
|
||||
throw new BadRequestException('Session already processed');
|
||||
}
|
||||
|
||||
if (new Date() > session.expiresAt) {
|
||||
throw new BadRequestException('Session expired');
|
||||
}
|
||||
|
||||
// Obtenir l'adaptateur
|
||||
const adapter = this.operatorsService.getAdapter(
|
||||
session.operator,
|
||||
session.country,
|
||||
);
|
||||
|
||||
// Valider avec l'opérateur
|
||||
const validationResponse = await adapter.validateAuth({
|
||||
challengeId: session.challengeId,
|
||||
otpCode: dto.otpCode,
|
||||
msisdn: session.msisdn,
|
||||
country: session.country,
|
||||
});
|
||||
|
||||
if (!validationResponse.success) {
|
||||
await this.prisma.authSession.update({
|
||||
where: { id: session.id },
|
||||
data: { status: 'FAILED' },
|
||||
});
|
||||
throw new UnauthorizedException('Authentication failed');
|
||||
}
|
||||
|
||||
// Créer ou mettre à jour l'utilisateur
|
||||
const user = await this.prisma.user.upsert({
|
||||
where: { msisdn: session.msisdn },
|
||||
update: {
|
||||
userToken: validationResponse.userToken,
|
||||
userAlias: validationResponse.userAlias,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
create: {
|
||||
msisdn: session.msisdn,
|
||||
userToken: validationResponse.userToken,
|
||||
userAlias: validationResponse.userAlias,
|
||||
operatorId: await this.getOperatorId(session.operator, session.country),
|
||||
partnerId: session.partnerId,
|
||||
country: session.country,
|
||||
},
|
||||
});
|
||||
|
||||
// Mettre à jour la session
|
||||
await this.prisma.authSession.update({
|
||||
where: { id: session.id },
|
||||
data: {
|
||||
status: 'SUCCESS',
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Créer un JWT pour le partenaire
|
||||
const payload = {
|
||||
userId: user.id,
|
||||
partnerId: session.partnerId,
|
||||
msisdn: user.msisdn,
|
||||
operator: session.operator,
|
||||
};
|
||||
|
||||
return {
|
||||
success: true,
|
||||
accessToken: this.jwtService.sign(payload),
|
||||
userToken: validationResponse.userToken,
|
||||
userAlias: validationResponse.userAlias,
|
||||
msisdn: session.msisdn,
|
||||
operator: session.operator,
|
||||
country: session.country,
|
||||
expiresAt: validationResponse.expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
async loginPartner(dto: LoginDto) {
|
||||
const partner = await this.prisma.partner.findUnique({
|
||||
where: { email: dto.email },
|
||||
});
|
||||
|
||||
if (!partner) {
|
||||
throw new UnauthorizedException('Invalid credentials');
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compare(
|
||||
dto.password,
|
||||
partner.passwordHash,
|
||||
);
|
||||
|
||||
if (!isPasswordValid) {
|
||||
throw new UnauthorizedException('Invalid credentials');
|
||||
}
|
||||
|
||||
const payload = {
|
||||
partnerId: partner.id,
|
||||
email: partner.email,
|
||||
type: 'partner',
|
||||
};
|
||||
|
||||
return {
|
||||
accessToken: this.jwtService.sign(payload),
|
||||
partner: {
|
||||
id: partner.id,
|
||||
name: partner.name,
|
||||
email: partner.email,
|
||||
status: partner.status,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private detectOperator(msisdn: string, country: string): string {
|
||||
// Logique pour détecter l'opérateur basé sur le préfixe
|
||||
const prefixMap = {
|
||||
CI: {
|
||||
'07': 'ORANGE',
|
||||
'08': 'ORANGE',
|
||||
'09': 'ORANGE',
|
||||
'04': 'MTN',
|
||||
'05': 'MTN',
|
||||
'06': 'MTN',
|
||||
'01': 'MOOV',
|
||||
},
|
||||
SN: {
|
||||
'77': 'ORANGE',
|
||||
'78': 'ORANGE',
|
||||
'76': 'FREE',
|
||||
'70': 'EXPRESSO',
|
||||
},
|
||||
// Ajouter d'autres pays
|
||||
};
|
||||
|
||||
const countryPrefixes = prefixMap[country];
|
||||
if (!countryPrefixes) {
|
||||
throw new BadRequestException(`Country ${country} not supported`);
|
||||
}
|
||||
|
||||
const prefix = msisdn.substring(0, 2);
|
||||
const operator = countryPrefixes[prefix];
|
||||
|
||||
if (!operator) {
|
||||
throw new BadRequestException(`Cannot detect operator for ${msisdn}`);
|
||||
}
|
||||
|
||||
return operator;
|
||||
}
|
||||
|
||||
private async getOperatorId(
|
||||
operatorCode: string,
|
||||
country: string,
|
||||
): Promise<string> {
|
||||
const operator = await this.prisma.operator.findFirst({
|
||||
where: {
|
||||
code: operatorCode as any,
|
||||
country: country,
|
||||
},
|
||||
});
|
||||
|
||||
if (!operator) {
|
||||
throw new BadRequestException(
|
||||
`Operator ${operatorCode} not found in ${country}`,
|
||||
);
|
||||
}
|
||||
|
||||
return operator.id;
|
||||
}
|
||||
}
|
||||
50
src/modules/auth/dto/auth.dto.ts
Normal file
50
src/modules/auth/dto/auth.dto.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { IsString, IsEnum, IsOptional, IsMobilePhone } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class AuthInitDto {
|
||||
@ApiProperty()
|
||||
@IsMobilePhone()
|
||||
msisdn: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
country: string;
|
||||
|
||||
@ApiProperty({ enum: ['OTP_SMS', 'REDIRECT_3G', 'SMS_MO', 'USSD'] })
|
||||
@IsEnum(['OTP_SMS', 'REDIRECT_3G', 'SMS_MO', 'USSD'])
|
||||
authMethod: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
redirectUrl?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export class AuthValidateDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
sessionId: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
otpCode?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
challengeResponse?: string;
|
||||
}
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
email: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
password: string;
|
||||
}
|
||||
23
src/modules/auth/strategies/jwt.strategy.ts
Normal file
23
src/modules/auth/strategies/jwt.strategy.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private configService: ConfigService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('app.jwtSecret'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
return {
|
||||
userId: payload.userId,
|
||||
partnerId: payload.partnerId,
|
||||
email: payload.email,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -14,11 +14,11 @@ export class OperatorAdapterFactory {
|
||||
const key = `${operator}_${country}`.toUpperCase();
|
||||
|
||||
const adapterMap = {
|
||||
'ORANGE_CI': this.orangeAdapter,
|
||||
'ORANGE_SN': this.orangeAdapter,
|
||||
'ORANGE_CM': this.orangeAdapter,
|
||||
'MTN_CI': this.mtnAdapter,
|
||||
'MTN_CM': this.mtnAdapter,
|
||||
ORANGE_CI: this.orangeAdapter,
|
||||
ORANGE_SN: this.orangeAdapter,
|
||||
ORANGE_CM: this.orangeAdapter,
|
||||
MTN_CI: this.mtnAdapter,
|
||||
MTN_CM: this.mtnAdapter,
|
||||
// Ajouter d'autres mappings
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,9 @@ export interface IOperatorAdapter {
|
||||
charge(params: ChargeParams): Promise<ChargeResponse>;
|
||||
refund(params: RefundParams): Promise<RefundResponse>;
|
||||
sendSms(params: SmsParams): Promise<SmsResponse>;
|
||||
createSubscription?(params: SubscriptionParams): Promise<SubscriptionResponse>;
|
||||
createSubscription?(
|
||||
params: SubscriptionParams,
|
||||
): Promise<SubscriptionResponse>;
|
||||
cancelSubscription?(subscriptionId: string): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@ -123,8 +123,8 @@ export class OrangeAdapter implements IOperatorAdapter {
|
||||
);
|
||||
|
||||
const result = response.data.challenge.result;
|
||||
const userToken = result.find(r => r.type === 'OrangeApiToken')?.value;
|
||||
const userAlias = result.find(r => r.type === 'ise2')?.value;
|
||||
const userToken = result.find((r) => r.type === 'OrangeApiToken')?.value;
|
||||
const userAlias = result.find((r) => r.type === 'ise2')?.value;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@ -5,19 +5,25 @@ export class OrangeTransformer {
|
||||
transformChargeResponse(bizaoResponse: any): any {
|
||||
return {
|
||||
paymentId: bizaoResponse.amountTransaction?.serverReferenceCode,
|
||||
status: this.mapStatus(bizaoResponse.amountTransaction?.transactionOperationStatus),
|
||||
status: this.mapStatus(
|
||||
bizaoResponse.amountTransaction?.transactionOperationStatus,
|
||||
),
|
||||
operatorReference: bizaoResponse.amountTransaction?.serverReferenceCode,
|
||||
amount: parseFloat(bizaoResponse.amountTransaction?.paymentAmount?.totalAmountCharged),
|
||||
currency: bizaoResponse.amountTransaction?.paymentAmount?.chargingInformation?.currency,
|
||||
amount: parseFloat(
|
||||
bizaoResponse.amountTransaction?.paymentAmount?.totalAmountCharged,
|
||||
),
|
||||
currency:
|
||||
bizaoResponse.amountTransaction?.paymentAmount?.chargingInformation
|
||||
?.currency,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
private mapStatus(bizaoStatus: string): string {
|
||||
const statusMap = {
|
||||
'Charged': 'SUCCESS',
|
||||
'Failed': 'FAILED',
|
||||
'Pending': 'PENDING',
|
||||
Charged: 'SUCCESS',
|
||||
Failed: 'FAILED',
|
||||
Pending: 'PENDING',
|
||||
};
|
||||
return statusMap[bizaoStatus] || 'PENDING';
|
||||
}
|
||||
|
||||
76
src/modules/partners/dto/partner.dto.ts
Normal file
76
src/modules/partners/dto/partner.dto.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import {
|
||||
IsString,
|
||||
IsEmail,
|
||||
IsOptional,
|
||||
IsObject,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreatePartnerDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
password: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
country: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
companyInfo?: {
|
||||
legalName: string;
|
||||
taxId: string;
|
||||
address: string;
|
||||
phone?: string;
|
||||
website?: string;
|
||||
};
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export class UpdateCallbacksDto {
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
headerEnrichment?: {
|
||||
url: string;
|
||||
method: string;
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
subscription?: {
|
||||
onCreate?: string;
|
||||
onRenew?: string;
|
||||
onCancel?: string;
|
||||
onExpire?: string;
|
||||
};
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
payment?: {
|
||||
onSuccess?: string;
|
||||
onFailure?: string;
|
||||
onRefund?: string;
|
||||
};
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
authentication?: {
|
||||
onSuccess?: string;
|
||||
onFailure?: string;
|
||||
};
|
||||
}
|
||||
11
src/modules/partners/partners.module.ts
Normal file
11
src/modules/partners/partners.module.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PartnersController } from './partners.controller';
|
||||
import { PartnersService } from './partners.service';
|
||||
import { PrismaService } from '../../shared/services/prisma.service';
|
||||
|
||||
@Module({
|
||||
controllers: [PartnersController],
|
||||
providers: [PartnersService, PrismaService],
|
||||
exports: [PartnersService],
|
||||
})
|
||||
export class PartnersModule {}
|
||||
184
src/modules/partners/partners.service.ts
Normal file
184
src/modules/partners/partners.service.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import {
|
||||
Injectable,
|
||||
ConflictException,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../../shared/services/prisma.service';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import * as crypto from 'crypto';
|
||||
import { CreatePartnerDto, UpdateCallbacksDto } from './dto/partner.dto';
|
||||
|
||||
@Injectable()
|
||||
export class PartnersService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async register(dto: CreatePartnerDto) {
|
||||
// Vérifier si l'email existe déjà
|
||||
const existingPartner = await this.prisma.partner.findUnique({
|
||||
where: { email: dto.email },
|
||||
});
|
||||
|
||||
if (existingPartner) {
|
||||
throw new ConflictException('Email already registered');
|
||||
}
|
||||
|
||||
// Générer les clés API
|
||||
const apiKey = this.generateApiKey();
|
||||
const secretKey = this.generateSecretKey();
|
||||
|
||||
// Hasher le mot de passe
|
||||
const passwordHash = await bcrypt.hash(dto.password, 10);
|
||||
|
||||
// Créer le partenaire
|
||||
const partner = await this.prisma.partner.create({
|
||||
data: {
|
||||
name: dto.name,
|
||||
email: dto.email,
|
||||
passwordHash: passwordHash,
|
||||
apiKey: apiKey,
|
||||
secretKey: secretKey,
|
||||
status: 'PENDING',
|
||||
companyInfo: dto.companyInfo,
|
||||
country: dto.country,
|
||||
metadata: dto.metadata,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
partnerId: partner.id,
|
||||
apiKey: partner.apiKey,
|
||||
secretKey: partner.secretKey,
|
||||
status: partner.status,
|
||||
message: 'Partner registered successfully. Awaiting approval.',
|
||||
};
|
||||
}
|
||||
|
||||
async updateCallbacks(partnerId: string, dto: UpdateCallbacksDto) {
|
||||
const partner = await this.prisma.partner.findUnique({
|
||||
where: { id: partnerId },
|
||||
});
|
||||
|
||||
if (!partner) {
|
||||
throw new NotFoundException('Partner not found');
|
||||
}
|
||||
|
||||
const updatedPartner = await this.prisma.partner.update({
|
||||
where: { id: partnerId },
|
||||
data: {
|
||||
callbacks: dto,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
partnerId: updatedPartner.id,
|
||||
callbacks: updatedPartner.callbacks,
|
||||
};
|
||||
}
|
||||
|
||||
async getPartner(partnerId: string) {
|
||||
const partner = await this.prisma.partner.findUnique({
|
||||
where: { id: partnerId },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
status: true,
|
||||
callbacks: true,
|
||||
companyInfo: true,
|
||||
createdAt: true,
|
||||
_count: {
|
||||
select: {
|
||||
users: true,
|
||||
subscriptions: true,
|
||||
payments: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!partner) {
|
||||
throw new NotFoundException('Partner not found');
|
||||
}
|
||||
|
||||
return partner;
|
||||
}
|
||||
|
||||
async getPartnerStats(partnerId: string) {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const [totalUsers, activeSubscriptions, todayPayments, monthRevenue] =
|
||||
await Promise.all([
|
||||
this.prisma.user.count({
|
||||
where: { partnerId },
|
||||
}),
|
||||
this.prisma.subscription.count({
|
||||
where: {
|
||||
partnerId,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
}),
|
||||
this.prisma.payment.count({
|
||||
where: {
|
||||
partnerId,
|
||||
createdAt: { gte: today },
|
||||
},
|
||||
}),
|
||||
this.prisma.payment.aggregate({
|
||||
where: {
|
||||
partnerId,
|
||||
status: 'SUCCESS',
|
||||
createdAt: {
|
||||
gte: new Date(today.getFullYear(), today.getMonth(), 1),
|
||||
},
|
||||
},
|
||||
_sum: {
|
||||
amount: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
totalUsers,
|
||||
activeSubscriptions,
|
||||
todayPayments,
|
||||
monthRevenue: monthRevenue._sum.amount || 0,
|
||||
};
|
||||
}
|
||||
|
||||
async regenerateKeys(partnerId: string) {
|
||||
const partner = await this.prisma.partner.findUnique({
|
||||
where: { id: partnerId },
|
||||
});
|
||||
|
||||
if (!partner) {
|
||||
throw new NotFoundException('Partner not found');
|
||||
}
|
||||
|
||||
const newApiKey = this.generateApiKey();
|
||||
const newSecretKey = this.generateSecretKey();
|
||||
|
||||
const updatedPartner = await this.prisma.partner.update({
|
||||
where: { id: partnerId },
|
||||
data: {
|
||||
apiKey: newApiKey,
|
||||
secretKey: newSecretKey,
|
||||
keysRotatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
apiKey: updatedPartner.apiKey,
|
||||
secretKey: updatedPartner.secretKey,
|
||||
message: 'Keys regenerated successfully',
|
||||
};
|
||||
}
|
||||
|
||||
private generateApiKey(): string {
|
||||
return `pk_${crypto.randomBytes(32).toString('hex')}`;
|
||||
}
|
||||
|
||||
private generateSecretKey(): string {
|
||||
return `sk_${crypto.randomBytes(32).toString('hex')}`;
|
||||
}
|
||||
}
|
||||
24
src/modules/payments/payments.module.ts
Normal file
24
src/modules/payments/payments.module.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { PaymentsController } from './payments.controller';
|
||||
import { PaymentsService } from './payments.service';
|
||||
import { PaymentProcessor } from './processors/payment.processor';
|
||||
import { WebhookService } from './services/webhook.service';
|
||||
import { PrismaService } from '../../shared/services/prisma.service';
|
||||
import { OperatorsModule } from '../operators/operators.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
BullModule.registerQueue({
|
||||
name: 'payments',
|
||||
}),
|
||||
BullModule.registerQueue({
|
||||
name: 'webhooks',
|
||||
}),
|
||||
OperatorsModule,
|
||||
],
|
||||
controllers: [PaymentsController],
|
||||
providers: [PaymentsService, PaymentProcessor, WebhookService, PrismaService],
|
||||
exports: [PaymentsService],
|
||||
})
|
||||
export class PaymentsModule {}
|
||||
@ -59,9 +59,10 @@ export class PaymentsService {
|
||||
const updatedPayment = await this.prisma.payment.update({
|
||||
where: { id: payment.id },
|
||||
data: {
|
||||
status: result.status === 'SUCCESS'
|
||||
? PaymentStatus.SUCCESS
|
||||
: PaymentStatus.FAILED,
|
||||
status:
|
||||
result.status === 'SUCCESS'
|
||||
? PaymentStatus.SUCCESS
|
||||
: PaymentStatus.FAILED,
|
||||
operatorReference: result.operatorReference,
|
||||
completedAt: new Date(),
|
||||
},
|
||||
|
||||
49
src/modules/payments/processors/payment.processor.ts
Normal file
49
src/modules/payments/processors/payment.processor.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { Process, Processor } from '@nestjs/bull';
|
||||
import { Job } from 'bull';
|
||||
import { PaymentsService } from '../payments.service';
|
||||
import { WebhookService } from '../services/webhook.service';
|
||||
|
||||
@Processor('payments')
|
||||
export class PaymentProcessor {
|
||||
constructor(
|
||||
private readonly paymentsService: PaymentsService,
|
||||
private readonly webhookService: WebhookService,
|
||||
) {}
|
||||
|
||||
@Process('process-payment')
|
||||
async handlePayment(job: Job) {
|
||||
const { paymentId } = job.data;
|
||||
|
||||
try {
|
||||
// Traiter le paiement
|
||||
const result = await this.paymentsService.processPayment(paymentId);
|
||||
|
||||
// Envoyer le webhook
|
||||
if (result.callbackUrl) {
|
||||
await this.webhookService.send({
|
||||
url: result.callbackUrl,
|
||||
event:
|
||||
result.status === 'SUCCESS' ? 'PAYMENT_SUCCESS' : 'PAYMENT_FAILED',
|
||||
payload: result,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`Payment processing failed for ${paymentId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@Process('retry-payment')
|
||||
async handleRetry(job: Job) {
|
||||
const { paymentId, attempt } = job.data;
|
||||
|
||||
try {
|
||||
return await this.paymentsService.retryPayment(paymentId, attempt);
|
||||
} catch (error) {
|
||||
console.error(`Payment retry failed for ${paymentId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,7 +136,10 @@ export class SubscriptionsService {
|
||||
await this.handlePaymentFailure(subscription.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to renew subscription ${subscription.id}:`, error);
|
||||
console.error(
|
||||
`Failed to renew subscription ${subscription.id}:`,
|
||||
error,
|
||||
);
|
||||
await this.handlePaymentFailure(subscription.id);
|
||||
}
|
||||
}
|
||||
@ -166,9 +169,13 @@ export class SubscriptionsService {
|
||||
const now = new Date();
|
||||
switch (trialPeriod.unit) {
|
||||
case 'DAYS':
|
||||
return new Date(now.getTime() + trialPeriod.duration * 24 * 60 * 60 * 1000);
|
||||
return new Date(
|
||||
now.getTime() + trialPeriod.duration * 24 * 60 * 60 * 1000,
|
||||
);
|
||||
case 'WEEKS':
|
||||
return new Date(now.getTime() + trialPeriod.duration * 7 * 24 * 60 * 60 * 1000);
|
||||
return new Date(
|
||||
now.getTime() + trialPeriod.duration * 7 * 24 * 60 * 60 * 1000,
|
||||
);
|
||||
case 'MONTHS':
|
||||
return new Date(now.setMonth(now.getMonth() + trialPeriod.duration));
|
||||
default:
|
||||
|
||||
@ -2,7 +2,10 @@ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||
export class PrismaService
|
||||
extends PrismaClient
|
||||
implements OnModuleInit, OnModuleDestroy
|
||||
{
|
||||
constructor() {
|
||||
super({
|
||||
log: ['query', 'info', 'warn', 'error'],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user