refactor: config service
This commit is contained in:
@@ -64,6 +64,7 @@
|
|||||||
"@algolia/client-search": "*",
|
"@algolia/client-search": "*",
|
||||||
"@nestjs/common": "8.2.5",
|
"@nestjs/common": "8.2.5",
|
||||||
"@nestjs/core": "8.2.5",
|
"@nestjs/core": "8.2.5",
|
||||||
|
"@nestjs/event-emitter": "1.0.0",
|
||||||
"@nestjs/graphql": "9.1.2",
|
"@nestjs/graphql": "9.1.2",
|
||||||
"@nestjs/jwt": "8.0.0",
|
"@nestjs/jwt": "8.0.0",
|
||||||
"@nestjs/mapped-types": "*",
|
"@nestjs/mapped-types": "*",
|
||||||
@@ -148,9 +149,9 @@
|
|||||||
"ioredis": "4.28.3",
|
"ioredis": "4.28.3",
|
||||||
"jest": "27.4.7",
|
"jest": "27.4.7",
|
||||||
"lint-staged": "12.1.7",
|
"lint-staged": "12.1.7",
|
||||||
"mockingoose": "2.15.2",
|
|
||||||
"mongodb-memory-server": "8.1.0",
|
"mongodb-memory-server": "8.1.0",
|
||||||
"prettier": "2.5.1",
|
"prettier": "2.5.1",
|
||||||
|
"redis-memory-server": "0.5.0",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"run-script-webpack-plugin": "0.0.11",
|
"run-script-webpack-plugin": "0.0.11",
|
||||||
"semver": "*",
|
"semver": "*",
|
||||||
|
|||||||
112
pnpm-lock.yaml
generated
112
pnpm-lock.yaml
generated
@@ -11,6 +11,7 @@ specifiers:
|
|||||||
'@nestjs/cli': 8.1.8
|
'@nestjs/cli': 8.1.8
|
||||||
'@nestjs/common': 8.2.5
|
'@nestjs/common': 8.2.5
|
||||||
'@nestjs/core': 8.2.5
|
'@nestjs/core': 8.2.5
|
||||||
|
'@nestjs/event-emitter': 1.0.0
|
||||||
'@nestjs/graphql': 9.1.2
|
'@nestjs/graphql': 9.1.2
|
||||||
'@nestjs/jwt': 8.0.0
|
'@nestjs/jwt': 8.0.0
|
||||||
'@nestjs/mapped-types': '*'
|
'@nestjs/mapped-types': '*'
|
||||||
@@ -70,9 +71,8 @@ specifiers:
|
|||||||
jszip: 3.7.1
|
jszip: 3.7.1
|
||||||
lint-staged: 12.1.7
|
lint-staged: 12.1.7
|
||||||
lodash: '*'
|
lodash: '*'
|
||||||
marked: 4.0.10
|
marked: 4.0.9
|
||||||
mkdirp: '*'
|
mkdirp: '*'
|
||||||
mockingoose: 2.15.2
|
|
||||||
mongodb-memory-server: 8.1.0
|
mongodb-memory-server: 8.1.0
|
||||||
mongoose: '*'
|
mongoose: '*'
|
||||||
mongoose-lean-id: 0.3.0
|
mongoose-lean-id: 0.3.0
|
||||||
@@ -87,6 +87,7 @@ specifiers:
|
|||||||
passport-jwt: 4.0.0
|
passport-jwt: 4.0.0
|
||||||
pluralize: '*'
|
pluralize: '*'
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
|
redis-memory-server: 0.5.0
|
||||||
reflect-metadata: 0.1.13
|
reflect-metadata: 0.1.13
|
||||||
rimraf: 3.0.2
|
rimraf: 3.0.2
|
||||||
run-script-webpack-plugin: 0.0.11
|
run-script-webpack-plugin: 0.0.11
|
||||||
@@ -109,6 +110,7 @@ dependencies:
|
|||||||
'@algolia/client-search': 4.12.0
|
'@algolia/client-search': 4.12.0
|
||||||
'@nestjs/common': 8.2.5_17d6ce38cc74803538e351a8f2ca91ba
|
'@nestjs/common': 8.2.5_17d6ce38cc74803538e351a8f2ca91ba
|
||||||
'@nestjs/core': 8.2.5_b47d24345f8c2ad123c7afb4f52057f0
|
'@nestjs/core': 8.2.5_b47d24345f8c2ad123c7afb4f52057f0
|
||||||
|
'@nestjs/event-emitter': 1.0.0_8b618827ffa1784f2e7b042a97d87b6c
|
||||||
'@nestjs/graphql': 9.1.2_4aece87669e37e111f6a4461a47eae19
|
'@nestjs/graphql': 9.1.2_4aece87669e37e111f6a4461a47eae19
|
||||||
'@nestjs/jwt': 8.0.0_@nestjs+common@8.2.5
|
'@nestjs/jwt': 8.0.0_@nestjs+common@8.2.5
|
||||||
'@nestjs/mapped-types': 1.0.1_156c8e2af2c7dad431aaf14c3b14437b
|
'@nestjs/mapped-types': 1.0.1_156c8e2af2c7dad431aaf14c3b14437b
|
||||||
@@ -145,7 +147,7 @@ dependencies:
|
|||||||
js-yaml: 4.1.0
|
js-yaml: 4.1.0
|
||||||
jszip: 3.7.1
|
jszip: 3.7.1
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
marked: 4.0.10
|
marked: 4.0.9
|
||||||
mkdirp: 1.0.4
|
mkdirp: 1.0.4
|
||||||
mongoose: 6.1.6
|
mongoose: 6.1.6
|
||||||
mongoose-lean-id: 0.3.0_mongoose@6.1.6
|
mongoose-lean-id: 0.3.0_mongoose@6.1.6
|
||||||
@@ -193,9 +195,9 @@ devDependencies:
|
|||||||
ioredis: 4.28.3
|
ioredis: 4.28.3
|
||||||
jest: 27.4.7_ts-node@10.4.0
|
jest: 27.4.7_ts-node@10.4.0
|
||||||
lint-staged: 12.1.7
|
lint-staged: 12.1.7
|
||||||
mockingoose: 2.15.2_mongoose@6.1.6
|
|
||||||
mongodb-memory-server: 8.1.0
|
mongodb-memory-server: 8.1.0
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
|
redis-memory-server: 0.5.0
|
||||||
rimraf: 3.0.2
|
rimraf: 3.0.2
|
||||||
run-script-webpack-plugin: 0.0.11
|
run-script-webpack-plugin: 0.0.11
|
||||||
semver: 7.3.5
|
semver: 7.3.5
|
||||||
@@ -1420,6 +1422,19 @@ packages:
|
|||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@nestjs/event-emitter/1.0.0_8b618827ffa1784f2e7b042a97d87b6c:
|
||||||
|
resolution: {integrity: sha512-dRAou6G89KKYI2iyYfqSVGE6ZTC4WmHkQkFfgh88GLQg8dBqRk92ZY8CRtL2SK32SSelh9bwEDNQn9561uoypA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^7.0.0 || ^8.0.0
|
||||||
|
'@nestjs/core': ^7.0.0 || ^8.0.0
|
||||||
|
reflect-metadata: ^0.1.12
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 8.2.5_17d6ce38cc74803538e351a8f2ca91ba
|
||||||
|
'@nestjs/core': 8.2.5_b47d24345f8c2ad123c7afb4f52057f0
|
||||||
|
eventemitter2: 6.4.4
|
||||||
|
reflect-metadata: 0.1.13
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@nestjs/graphql/9.1.2_4aece87669e37e111f6a4461a47eae19:
|
/@nestjs/graphql/9.1.2_4aece87669e37e111f6a4461a47eae19:
|
||||||
resolution: {integrity: sha512-ncxmkKmrswnHJ+jLgc6tbkRET6HAyC3gK6WKt1CdPLjvMnXVpFVpyQMFSenNXxAndInw9IgaiSyiJFdRYZVD/w==}
|
resolution: {integrity: sha512-ncxmkKmrswnHJ+jLgc6tbkRET6HAyC3gK6WKt1CdPLjvMnXVpFVpyQMFSenNXxAndInw9IgaiSyiJFdRYZVD/w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2137,6 +2152,14 @@ packages:
|
|||||||
'@types/yargs-parser': 20.2.1
|
'@types/yargs-parser': 20.2.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/yauzl/2.9.2:
|
||||||
|
resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==}
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 16.11.19
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin/4.33.0_3289a875d95a672b97ebf589745c66ef:
|
/@typescript-eslint/eslint-plugin/4.33.0_3289a875d95a672b97ebf589745c66ef:
|
||||||
resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==}
|
resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==}
|
||||||
engines: {node: ^10.12.0 || >=12.0.0}
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
@@ -3269,7 +3292,6 @@ packages:
|
|||||||
/chownr/2.0.0:
|
/chownr/2.0.0:
|
||||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/chrome-trace-event/1.0.3:
|
/chrome-trace-event/1.0.3:
|
||||||
resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
|
resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
|
||||||
@@ -4221,6 +4243,10 @@ packages:
|
|||||||
through: 2.3.8
|
through: 2.3.8
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/eventemitter2/6.4.4:
|
||||||
|
resolution: {integrity: sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/eventemitter3/3.1.2:
|
/eventemitter3/3.1.2:
|
||||||
resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==}
|
resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -4291,6 +4317,20 @@ packages:
|
|||||||
iconv-lite: 0.4.24
|
iconv-lite: 0.4.24
|
||||||
tmp: 0.0.33
|
tmp: 0.0.33
|
||||||
|
|
||||||
|
/extract-zip/2.0.1:
|
||||||
|
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
|
||||||
|
engines: {node: '>= 10.17.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.3
|
||||||
|
get-stream: 5.2.0
|
||||||
|
yauzl: 2.10.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/yauzl': 2.9.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
/extsprintf/1.3.0:
|
/extsprintf/1.3.0:
|
||||||
resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=}
|
resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=}
|
||||||
engines: {'0': node >=0.6.0}
|
engines: {'0': node >=0.6.0}
|
||||||
@@ -4515,6 +4555,10 @@ packages:
|
|||||||
semver-store: 0.3.0
|
semver-store: 0.3.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/find-package-json/1.2.0:
|
||||||
|
resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/find-up/2.1.0:
|
/find-up/2.1.0:
|
||||||
resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=}
|
resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -4657,7 +4701,6 @@ packages:
|
|||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass: 3.1.3
|
minipass: 3.1.3
|
||||||
dev: false
|
|
||||||
|
|
||||||
/fs-monkey/1.0.3:
|
/fs-monkey/1.0.3:
|
||||||
resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==}
|
resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==}
|
||||||
@@ -6178,9 +6221,19 @@ packages:
|
|||||||
p-locate: 4.1.0
|
p-locate: 4.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lockfile/1.0.4:
|
||||||
|
resolution: {integrity: sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==}
|
||||||
|
dependencies:
|
||||||
|
signal-exit: 3.0.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/lodash.defaults/4.2.0:
|
/lodash.defaults/4.2.0:
|
||||||
resolution: {integrity: sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=}
|
resolution: {integrity: sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=}
|
||||||
|
|
||||||
|
/lodash.defaultsdeep/4.6.1:
|
||||||
|
resolution: {integrity: sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/lodash.flatten/4.4.0:
|
/lodash.flatten/4.4.0:
|
||||||
resolution: {integrity: sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=}
|
resolution: {integrity: sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=}
|
||||||
|
|
||||||
@@ -6317,8 +6370,8 @@ packages:
|
|||||||
resolution: {integrity: sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=}
|
resolution: {integrity: sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/marked/4.0.10:
|
/marked/4.0.9:
|
||||||
resolution: {integrity: sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==}
|
resolution: {integrity: sha512-HmoFvQwFLxNESeGupeOC+6CLb5WzcCWQmqvVetsErmrI3vrZ6gBumty5IP0ynLPR0zYSoVY7ITC1GffsYIGkog==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
dev: false
|
||||||
@@ -6408,7 +6461,6 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/minizlib/2.1.2:
|
/minizlib/2.1.2:
|
||||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||||
@@ -6416,7 +6468,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minipass: 3.1.3
|
minipass: 3.1.3
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mkdirp/0.5.5:
|
/mkdirp/0.5.5:
|
||||||
resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==}
|
resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==}
|
||||||
@@ -6429,16 +6480,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mockingoose/2.15.2_mongoose@6.1.6:
|
|
||||||
resolution: {integrity: sha512-50mtbAk29Go5hdhzqTmjmE67Z/cB0yPz45u2jrHoGm4nkYnnBq224viWgyKwnxzWw8birnqn98viM2cRBTnJvw==}
|
|
||||||
engines: {node: '>=6.4.0'}
|
|
||||||
peerDependencies:
|
|
||||||
mongoose: '>=4.9.10'
|
|
||||||
dependencies:
|
|
||||||
mongoose: 6.1.6
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/moment-timezone/0.5.33:
|
/moment-timezone/0.5.33:
|
||||||
resolution: {integrity: sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==}
|
resolution: {integrity: sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==}
|
||||||
@@ -7292,6 +7333,31 @@ packages:
|
|||||||
resolution: {integrity: sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=}
|
resolution: {integrity: sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
/redis-memory-server/0.5.0:
|
||||||
|
resolution: {integrity: sha512-MoqPXeB9pqUa/Klry6HZw94aaOSE8CAB+1Zo1hXvGu/b+k/u79e2u1/CVGsqJ0xJMSkQMPa9duzzdmXll0PCSg==}
|
||||||
|
engines: {node: '>=10.15.0'}
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
camelcase: 6.3.0
|
||||||
|
cross-spawn: 7.0.3
|
||||||
|
debug: 4.3.3
|
||||||
|
extract-zip: 2.0.1
|
||||||
|
find-cache-dir: 3.3.2
|
||||||
|
find-package-json: 1.2.0
|
||||||
|
get-port: 5.1.1
|
||||||
|
https-proxy-agent: 5.0.0
|
||||||
|
lockfile: 1.0.4
|
||||||
|
lodash.defaultsdeep: 4.6.1
|
||||||
|
mkdirp: 1.0.4
|
||||||
|
rimraf: 3.0.2
|
||||||
|
semver: 7.3.5
|
||||||
|
tar: 6.1.11
|
||||||
|
tmp: 0.2.1
|
||||||
|
uuid: 8.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
/redis-parser/3.0.0:
|
/redis-parser/3.0.0:
|
||||||
resolution: {integrity: sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=}
|
resolution: {integrity: sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -8035,7 +8101,6 @@ packages:
|
|||||||
minizlib: 2.1.2
|
minizlib: 2.1.2
|
||||||
mkdirp: 1.0.4
|
mkdirp: 1.0.4
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/terminal-link/2.1.1:
|
/terminal-link/2.1.1:
|
||||||
resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
|
resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
|
||||||
@@ -8469,6 +8534,11 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/uuid/8.3.0:
|
||||||
|
resolution: {integrity: sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/uuid/8.3.2:
|
/uuid/8.3.2:
|
||||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import { SpiderGuard } from './common/guard/spider.guard'
|
|||||||
import { LoggingInterceptor } from './common/interceptors/logging.interceptor'
|
import { LoggingInterceptor } from './common/interceptors/logging.interceptor'
|
||||||
import { MyLogger } from './processors/logger/logger.service'
|
import { MyLogger } from './processors/logger/logger.service'
|
||||||
|
|
||||||
console.log('ENV:', process.env.NODE_ENV)
|
|
||||||
|
|
||||||
const Origin = CROSS_DOMAIN.allowedOrigins
|
const Origin = CROSS_DOMAIN.allowedOrigins
|
||||||
|
|
||||||
declare const module: any
|
declare const module: any
|
||||||
@@ -70,6 +68,7 @@ async function bootstrap() {
|
|||||||
|
|
||||||
await app.listen(+PORT, '0.0.0.0', async (err, address) => {
|
await app.listen(+PORT, '0.0.0.0', async (err, address) => {
|
||||||
app.useLogger(app.get(MyLogger))
|
app.useLogger(app.get(MyLogger))
|
||||||
|
consola.info('ENV:', process.env.NODE_ENV)
|
||||||
const url = await app.getUrl()
|
const url = await app.getUrl()
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
consola.debug(`OpenApi: ${url}/api-docs`)
|
consola.debug(`OpenApi: ${url}/api-docs`)
|
||||||
|
|||||||
4
src/constants/event.constant.ts
Normal file
4
src/constants/event.constant.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum EventBusEvents {
|
||||||
|
EmailInit = 'email.init',
|
||||||
|
PushSearch = 'search.push',
|
||||||
|
}
|
||||||
@@ -1,19 +1,42 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common'
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
|
Logger,
|
||||||
|
ValidationPipe,
|
||||||
|
} from '@nestjs/common'
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter'
|
||||||
import { DocumentType, ReturnModelType } from '@typegoose/typegoose'
|
import { DocumentType, ReturnModelType } from '@typegoose/typegoose'
|
||||||
import { BeAnObject } from '@typegoose/typegoose/lib/types'
|
import { BeAnObject } from '@typegoose/typegoose/lib/types'
|
||||||
|
import camelcaseKeys from 'camelcase-keys'
|
||||||
|
import { ClassConstructor, plainToClass } from 'class-transformer'
|
||||||
|
import { validateSync, ValidatorOptions } from 'class-validator'
|
||||||
import { cloneDeep, mergeWith } from 'lodash'
|
import { cloneDeep, mergeWith } from 'lodash'
|
||||||
import { LeanDocument } from 'mongoose'
|
import { LeanDocument } from 'mongoose'
|
||||||
import { InjectModel } from 'nestjs-typegoose'
|
import { InjectModel } from 'nestjs-typegoose'
|
||||||
import { API_VERSION } from '~/app.config'
|
import { API_VERSION } from '~/app.config'
|
||||||
import { RedisKeys } from '~/constants/cache.constant'
|
import { RedisKeys } from '~/constants/cache.constant'
|
||||||
|
import { EventBusEvents } from '~/constants/event.constant'
|
||||||
import { CacheService } from '~/processors/cache/cache.service'
|
import { CacheService } from '~/processors/cache/cache.service'
|
||||||
import { sleep } from '~/utils/index.util'
|
import { sleep } from '~/utils/index.util'
|
||||||
import { getRedisKey } from '~/utils/redis.util'
|
import { getRedisKey } from '~/utils/redis.util'
|
||||||
|
import * as optionDtos from '../configs/configs.dto'
|
||||||
import { UserModel } from '../user/user.model'
|
import { UserModel } from '../user/user.model'
|
||||||
import { UserService } from '../user/user.service'
|
import { UserService } from '../user/user.service'
|
||||||
import { BackupOptionsDto, MailOptionsDto } from './configs.dto'
|
import {
|
||||||
|
AlgoliaSearchOptionsDto,
|
||||||
|
BackupOptionsDto,
|
||||||
|
MailOptionsDto,
|
||||||
|
} from './configs.dto'
|
||||||
import { IConfig } from './configs.interface'
|
import { IConfig } from './configs.interface'
|
||||||
import { OptionModel } from './configs.model'
|
import { OptionModel } from './configs.model'
|
||||||
|
const map: Record<string, any> = Object.entries(optionDtos).reduce(
|
||||||
|
(obj, [key, value]) => ({
|
||||||
|
...obj,
|
||||||
|
[`${key.charAt(0).toLowerCase() + key.slice(1).replace(/Dto$/, '')}`]:
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
const generateDefaultConfig: () => IConfig = () => ({
|
const generateDefaultConfig: () => IConfig = () => ({
|
||||||
seo: {
|
seo: {
|
||||||
@@ -50,6 +73,8 @@ export class ConfigsService {
|
|||||||
private readonly optionModel: ReturnModelType<typeof OptionModel>,
|
private readonly optionModel: ReturnModelType<typeof OptionModel>,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly redis: CacheService,
|
private readonly redis: CacheService,
|
||||||
|
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) {
|
) {
|
||||||
this.configInit().then(() => {
|
this.configInit().then(() => {
|
||||||
this.logger.log('Config 已经加载完毕!')
|
this.logger.log('Config 已经加载完毕!')
|
||||||
@@ -131,7 +156,10 @@ export class ConfigsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async patch<T extends keyof IConfig>(key: T, data: IConfig[T]) {
|
public async patch<T extends keyof IConfig>(
|
||||||
|
key: T,
|
||||||
|
data: Partial<IConfig[T]>,
|
||||||
|
): Promise<IConfig[T]> {
|
||||||
const config = await this.getConfig()
|
const config = await this.getConfig()
|
||||||
const updatedConfigRow = await this.optionModel
|
const updatedConfigRow = await this.optionModel
|
||||||
.findOneAndUpdate(
|
.findOneAndUpdate(
|
||||||
@@ -155,6 +183,60 @@ export class ConfigsService {
|
|||||||
return newData
|
return newData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validOptions: ValidatorOptions = {
|
||||||
|
whitelist: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
}
|
||||||
|
validate = new ValidationPipe(this.validOptions)
|
||||||
|
async patchAndValid<T extends keyof IConfig>(
|
||||||
|
key: T,
|
||||||
|
value: Partial<IConfig[T]>,
|
||||||
|
) {
|
||||||
|
value = camelcaseKeys(value, { deep: true }) as any
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'mailOptions': {
|
||||||
|
const option = await this.patch(
|
||||||
|
'mailOptions',
|
||||||
|
this.validWithDto(MailOptionsDto, value),
|
||||||
|
)
|
||||||
|
if (option.enable) {
|
||||||
|
this.eventEmitter.emit(EventBusEvents.EmailInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'algoliaSearchOptions': {
|
||||||
|
const option = await this.patch(
|
||||||
|
'algoliaSearchOptions',
|
||||||
|
this.validWithDto(AlgoliaSearchOptionsDto, value),
|
||||||
|
)
|
||||||
|
if (option.enable) {
|
||||||
|
this.eventEmitter.emit(EventBusEvents.PushSearch)
|
||||||
|
}
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
const dto = map[key]
|
||||||
|
if (!dto) {
|
||||||
|
throw new BadRequestException('设置不存在')
|
||||||
|
}
|
||||||
|
return this.patch(key, this.validWithDto(dto, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private validWithDto<T extends object>(dto: ClassConstructor<T>, value: any) {
|
||||||
|
const validModel = plainToClass(dto, value)
|
||||||
|
const errors = validateSync(validModel, this.validOptions)
|
||||||
|
if (errors.length > 0) {
|
||||||
|
const error = this.validate.createExceptionFactory()(errors as any[])
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
return validModel
|
||||||
|
}
|
||||||
|
|
||||||
get getMaster() {
|
get getMaster() {
|
||||||
// HINT: 需要注入 this 的指向
|
// HINT: 需要注入 this 的指向
|
||||||
return this.userService.getMaster.bind(this.userService) as () => Promise<
|
return this.userService.getMaster.bind(this.userService) as () => Promise<
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
import { ApiName } from '~/common/decorator/openapi.decorator'
|
import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||||
import { ConfigsService } from '../configs/configs.service'
|
import { ConfigsService } from '../configs/configs.service'
|
||||||
import { ConfigKeyDto } from '../option/dtos/config.dto'
|
import { ConfigKeyDto } from '../option/dtos/config.dto'
|
||||||
import { OptionService } from '../option/option.service'
|
|
||||||
import { InitService } from './init.service'
|
import { InitService } from './init.service'
|
||||||
|
|
||||||
@Controller({
|
@Controller({
|
||||||
@@ -23,7 +22,7 @@ import { InitService } from './init.service'
|
|||||||
export class InitController {
|
export class InitController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly configs: ConfigsService,
|
private readonly configs: ConfigsService,
|
||||||
private readonly optionService: OptionService,
|
|
||||||
private readonly initService: InitService,
|
private readonly initService: InitService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -55,6 +54,6 @@ export class InitController {
|
|||||||
if (typeof body !== 'object') {
|
if (typeof body !== 'object') {
|
||||||
throw new UnprocessableEntityException('body must be object')
|
throw new UnprocessableEntityException('body must be object')
|
||||||
}
|
}
|
||||||
return this.optionService.patchAndValid(params.key, body)
|
return this.configs.patchAndValid(params.key, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ import { IConfig } from '../configs/configs.interface'
|
|||||||
import { ConfigsService } from '../configs/configs.service'
|
import { ConfigsService } from '../configs/configs.service'
|
||||||
import { ConfigKeyDto } from './dtos/config.dto'
|
import { ConfigKeyDto } from './dtos/config.dto'
|
||||||
import { ReplyEmailBodyDto, ReplyEmailTypeDto } from './dtos/email.dto'
|
import { ReplyEmailBodyDto, ReplyEmailTypeDto } from './dtos/email.dto'
|
||||||
import { OptionService } from './option.service'
|
|
||||||
|
|
||||||
@Controller(['options', 'config', 'setting', 'configs', 'option'])
|
@Controller(['options', 'config', 'setting', 'configs', 'option'])
|
||||||
@ApiTags('Option Routes')
|
@ApiTags('Option Routes')
|
||||||
@Auth()
|
@Auth()
|
||||||
export class OptionController {
|
export class OptionController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly optionService: OptionService,
|
private readonly configsService: ConfigsService,
|
||||||
private readonly configs: ConfigsService,
|
private readonly configs: ConfigsService,
|
||||||
private readonly emailService: EmailService,
|
private readonly emailService: EmailService,
|
||||||
) {}
|
) {}
|
||||||
@@ -57,7 +56,7 @@ export class OptionController {
|
|||||||
if (typeof body !== 'object') {
|
if (typeof body !== 'object') {
|
||||||
throw new UnprocessableEntityException('body must be object')
|
throw new UnprocessableEntityException('body must be object')
|
||||||
}
|
}
|
||||||
return this.optionService.patchAndValid(params.key, body)
|
return this.configsService.patchAndValid(params.key, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/email/template/reply')
|
@Get('/email/template/reply')
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { Module } from '@nestjs/common'
|
import { Module } from '@nestjs/common'
|
||||||
import { GatewayModule } from '~/processors/gateway/gateway.module'
|
import { GatewayModule } from '~/processors/gateway/gateway.module'
|
||||||
import { OptionController } from './option.controller'
|
import { OptionController } from './option.controller'
|
||||||
import { OptionService } from './option.service'
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [GatewayModule],
|
imports: [GatewayModule],
|
||||||
controllers: [OptionController],
|
controllers: [OptionController],
|
||||||
providers: [OptionService],
|
|
||||||
exports: [OptionService],
|
|
||||||
})
|
})
|
||||||
export class OptionModule {}
|
export class OptionModule {}
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
import { BadRequestException, Injectable, ValidationPipe } from '@nestjs/common'
|
|
||||||
import camelcaseKeys from 'camelcase-keys'
|
|
||||||
import { ClassConstructor, plainToClass } from 'class-transformer'
|
|
||||||
import { validateSync, ValidatorOptions } from 'class-validator'
|
|
||||||
import { CronService } from '~/processors/helper/helper.cron.service'
|
|
||||||
import { EmailService } from '~/processors/helper/helper.email.service'
|
|
||||||
import * as optionDtos from '../configs/configs.dto'
|
|
||||||
import { AlgoliaSearchOptionsDto, MailOptionsDto } from '../configs/configs.dto'
|
|
||||||
import { IConfig } from '../configs/configs.interface'
|
|
||||||
import { ConfigsService } from '../configs/configs.service'
|
|
||||||
|
|
||||||
const map: Record<string, any> = Object.entries(optionDtos).reduce(
|
|
||||||
(obj, [key, value]) => ({
|
|
||||||
...obj,
|
|
||||||
[`${key.charAt(0).toLowerCase() + key.slice(1).replace(/Dto$/, '')}`]:
|
|
||||||
value,
|
|
||||||
}),
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class OptionService {
|
|
||||||
constructor(
|
|
||||||
private readonly configs: ConfigsService,
|
|
||||||
private readonly emailService: EmailService,
|
|
||||||
private readonly cronService: CronService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
validOptions: ValidatorOptions = {
|
|
||||||
whitelist: true,
|
|
||||||
forbidNonWhitelisted: true,
|
|
||||||
}
|
|
||||||
validate = new ValidationPipe(this.validOptions)
|
|
||||||
async patchAndValid<T extends keyof IConfig>(key: T, value: IConfig[T]) {
|
|
||||||
value = camelcaseKeys(value, { deep: true }) as any
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case 'mailOptions': {
|
|
||||||
const option = await this.configs.patch(
|
|
||||||
'mailOptions',
|
|
||||||
this.validWithDto(MailOptionsDto, value),
|
|
||||||
)
|
|
||||||
this.emailService.init()
|
|
||||||
|
|
||||||
return option
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'algoliaSearchOptions': {
|
|
||||||
const option = await this.configs.patch(
|
|
||||||
'algoliaSearchOptions',
|
|
||||||
this.validWithDto(AlgoliaSearchOptionsDto, value),
|
|
||||||
)
|
|
||||||
if (option.enable) {
|
|
||||||
this.cronService.pushToAlgoliaSearch()
|
|
||||||
}
|
|
||||||
return option
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const dto = map[key]
|
|
||||||
if (!dto) {
|
|
||||||
throw new BadRequestException('设置不存在')
|
|
||||||
}
|
|
||||||
return this.configs.patch(key, this.validWithDto(dto, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private validWithDto<T extends object>(dto: ClassConstructor<T>, value: any) {
|
|
||||||
const validModel = plainToClass(dto, value)
|
|
||||||
const errors = validateSync(validModel, this.validOptions)
|
|
||||||
if (errors.length > 0) {
|
|
||||||
const error = this.validate.createExceptionFactory()(errors as any[])
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
return validModel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'
|
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter'
|
||||||
import { Cron, CronExpression } from '@nestjs/schedule'
|
import { Cron, CronExpression } from '@nestjs/schedule'
|
||||||
import COS from 'cos-nodejs-sdk-v5'
|
import COS from 'cos-nodejs-sdk-v5'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -8,6 +9,7 @@ import mkdirp from 'mkdirp'
|
|||||||
import { InjectModel } from 'nestjs-typegoose'
|
import { InjectModel } from 'nestjs-typegoose'
|
||||||
import { CronDescription } from '~/common/decorator/cron-description.decorator'
|
import { CronDescription } from '~/common/decorator/cron-description.decorator'
|
||||||
import { RedisItems, RedisKeys } from '~/constants/cache.constant'
|
import { RedisItems, RedisKeys } from '~/constants/cache.constant'
|
||||||
|
import { EventBusEvents } from '~/constants/event.constant'
|
||||||
import {
|
import {
|
||||||
LOCAL_BOT_LIST_DATA_FILE_PATH,
|
LOCAL_BOT_LIST_DATA_FILE_PATH,
|
||||||
TEMP_DIR,
|
TEMP_DIR,
|
||||||
@@ -236,6 +238,7 @@ export class CronService {
|
|||||||
*/
|
*/
|
||||||
@Cron(CronExpression.EVERY_DAY_AT_NOON, { name: 'pushToAlgoliaSearch' })
|
@Cron(CronExpression.EVERY_DAY_AT_NOON, { name: 'pushToAlgoliaSearch' })
|
||||||
@CronDescription('推送到 Algolia Search')
|
@CronDescription('推送到 Algolia Search')
|
||||||
|
@OnEvent(EventBusEvents.PushSearch)
|
||||||
async pushToAlgoliaSearch() {
|
async pushToAlgoliaSearch() {
|
||||||
const configs = await this.configs.waitForConfigReady()
|
const configs = await this.configs.waitForConfigReady()
|
||||||
if (!configs.algoliaSearchOptions.enable) {
|
if (!configs.algoliaSearchOptions.enable) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common'
|
import { Injectable, Logger } from '@nestjs/common'
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter'
|
||||||
import { render } from 'ejs'
|
import { render } from 'ejs'
|
||||||
import { createTransport } from 'nodemailer'
|
import { createTransport } from 'nodemailer'
|
||||||
|
import { EventBusEvents } from '~/constants/event.constant'
|
||||||
import { ConfigsService } from '~/modules/configs/configs.service'
|
import { ConfigsService } from '~/modules/configs/configs.service'
|
||||||
import { LinkModel } from '~/modules/link/link.model'
|
import { LinkModel } from '~/modules/link/link.model'
|
||||||
import { AssetService } from './helper.asset.service'
|
import { AssetService } from './helper.asset.service'
|
||||||
@@ -73,6 +75,7 @@ export class EmailService {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@OnEvent(EventBusEvents.EmailInit)
|
||||||
init() {
|
init() {
|
||||||
this.getConfigFromConfigService()
|
this.getConfigFromConfigService()
|
||||||
.then((config) => {
|
.then((config) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { forwardRef, Global, Module, Provider } from '@nestjs/common'
|
import { forwardRef, Global, Module, Provider } from '@nestjs/common'
|
||||||
|
import { EventEmitterModule } from '@nestjs/event-emitter'
|
||||||
import { ScheduleModule } from '@nestjs/schedule'
|
import { ScheduleModule } from '@nestjs/schedule'
|
||||||
import { AggregateModule } from '~/modules/aggregate/aggregate.module'
|
import { AggregateModule } from '~/modules/aggregate/aggregate.module'
|
||||||
import { BackupModule } from '~/modules/backup/backup.module'
|
import { BackupModule } from '~/modules/backup/backup.module'
|
||||||
@@ -29,6 +30,21 @@ const providers: Provider<any>[] = [
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ScheduleModule.forRoot(),
|
ScheduleModule.forRoot(),
|
||||||
|
EventEmitterModule.forRoot({
|
||||||
|
wildcard: false,
|
||||||
|
// the delimiter used to segment namespaces
|
||||||
|
delimiter: '.',
|
||||||
|
// set this to `true` if you want to emit the newListener event
|
||||||
|
newListener: false,
|
||||||
|
// set this to `true` if you want to emit the removeListener event
|
||||||
|
removeListener: false,
|
||||||
|
// the maximum amount of listeners that can be assigned to an event
|
||||||
|
maxListeners: 10,
|
||||||
|
// show event name in memory leak message when more than maximum amount of listeners is assigned
|
||||||
|
verboseMemoryLeak: isDev,
|
||||||
|
// disable throwing uncaughtException if an error event is emitted and it has no listeners
|
||||||
|
ignoreErrors: false,
|
||||||
|
}),
|
||||||
|
|
||||||
forwardRef(() => AggregateModule),
|
forwardRef(() => AggregateModule),
|
||||||
forwardRef(() => PostModule),
|
forwardRef(() => PostModule),
|
||||||
|
|||||||
53
test/helper/redis-mock.helper.ts
Normal file
53
test/helper/redis-mock.helper.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import IORedis from 'ioredis'
|
||||||
|
import RedisMemoryServer from 'redis-memory-server'
|
||||||
|
import { CacheKeys } from '~/constants/cache.constant'
|
||||||
|
|
||||||
|
export class MockCacheService {
|
||||||
|
private client: IORedis.Redis
|
||||||
|
constructor(port: number, host: string) {
|
||||||
|
this.client = new IORedis(port, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get redisClient() {
|
||||||
|
return this.client
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key) {
|
||||||
|
return this.client.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(key, value: any) {
|
||||||
|
return this.client.set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public getClient() {
|
||||||
|
return this.redisClient
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearAggregateCache() {
|
||||||
|
return Promise.all([
|
||||||
|
this.redisClient.del(CacheKeys.RSS),
|
||||||
|
this.redisClient.del(CacheKeys.RSSXmlCatch),
|
||||||
|
this.redisClient.del(CacheKeys.AggregateCatch),
|
||||||
|
this.redisClient.del(CacheKeys.SiteMapCatch),
|
||||||
|
this.redisClient.del(CacheKeys.SiteMapXmlCatch),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createMockRedis = async () => {
|
||||||
|
const redisServer = new RedisMemoryServer()
|
||||||
|
|
||||||
|
const redisHost = await redisServer.getHost()
|
||||||
|
const redisPort = await redisServer.getPort()
|
||||||
|
|
||||||
|
const service = new MockCacheService(redisPort, redisHost)
|
||||||
|
|
||||||
|
return {
|
||||||
|
service,
|
||||||
|
async close() {
|
||||||
|
await service.getClient().quit()
|
||||||
|
await redisServer.stop()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
132
test/src/modules/configs/configs.service.spec.ts
Normal file
132
test/src/modules/configs/configs.service.spec.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { BadRequestException } from '@nestjs/common'
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter'
|
||||||
|
import { Test } from '@nestjs/testing'
|
||||||
|
import { getModelForClass } from '@typegoose/typegoose'
|
||||||
|
import { getModelToken } from 'nestjs-typegoose'
|
||||||
|
import { dbHelper } from 'test/helper/db-mock.helper'
|
||||||
|
import {
|
||||||
|
createMockRedis,
|
||||||
|
MockCacheService,
|
||||||
|
} from 'test/helper/redis-mock.helper'
|
||||||
|
import { RedisKeys } from '~/constants/cache.constant'
|
||||||
|
import { OptionModel } from '~/modules/configs/configs.model'
|
||||||
|
import { ConfigsService } from '~/modules/configs/configs.service'
|
||||||
|
import { UserService } from '~/modules/user/user.service'
|
||||||
|
import { CacheService } from '~/processors/cache/cache.service'
|
||||||
|
import { getRedisKey } from '~/utils/redis.util'
|
||||||
|
|
||||||
|
describe('Test ConfigsService', () => {
|
||||||
|
let service: ConfigsService
|
||||||
|
let closeRedis: any
|
||||||
|
let redisService: MockCacheService
|
||||||
|
afterAll(async () => {
|
||||||
|
await dbHelper.clear()
|
||||||
|
await dbHelper.close()
|
||||||
|
await closeRedis()
|
||||||
|
})
|
||||||
|
const optionModel = getModelForClass(OptionModel)
|
||||||
|
const mockEmitFn = jest.fn()
|
||||||
|
beforeAll(async () => {
|
||||||
|
const { service: redisService$, close } = await createMockRedis()
|
||||||
|
closeRedis = close
|
||||||
|
redisService = redisService$
|
||||||
|
await dbHelper.connect()
|
||||||
|
|
||||||
|
const moduleRef = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
ConfigsService,
|
||||||
|
{
|
||||||
|
provide: getModelToken(OptionModel.name),
|
||||||
|
useValue: optionModel,
|
||||||
|
},
|
||||||
|
{ provide: UserService, useValue: {} },
|
||||||
|
{
|
||||||
|
provide: CacheService,
|
||||||
|
useValue: redisService$,
|
||||||
|
},
|
||||||
|
{ provide: EventEmitter2, useValue: { emit: mockEmitFn } },
|
||||||
|
],
|
||||||
|
}).compile()
|
||||||
|
|
||||||
|
service = moduleRef.get<ConfigsService>(ConfigsService)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('first get config should equal default config', async () => {
|
||||||
|
const config = await service.getConfig()
|
||||||
|
|
||||||
|
expect(config).toBeDefined()
|
||||||
|
expect(config).toStrictEqual(service.defaultConfig)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('patch config should apply change between db and redis', () => {
|
||||||
|
it('should update config', async () => {
|
||||||
|
const updated = await service.patch('seo', {
|
||||||
|
keywords: ['foo', 'bar'],
|
||||||
|
})
|
||||||
|
expect(updated).toBeDefined()
|
||||||
|
expect(updated).toStrictEqual({
|
||||||
|
...service.defaultConfig.seo,
|
||||||
|
keywords: ['foo', 'bar'],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update redis', async () => {
|
||||||
|
const redis = redisService.getClient()
|
||||||
|
const dataStr = await redis.get(getRedisKey(RedisKeys.ConfigCache))
|
||||||
|
const data = JSON.parse(dataStr)
|
||||||
|
expect(data).toBeDefined()
|
||||||
|
expect(data.seo.keywords).toStrictEqual(['foo', 'bar'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update db', async () => {
|
||||||
|
const seo = (await optionModel.findOne({ name: 'seo' })).value
|
||||||
|
expect(seo).toBeDefined()
|
||||||
|
expect(seo.keywords).toStrictEqual(['foo', 'bar'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get updated config via `get()`', async () => {
|
||||||
|
const seo = await service.get('seo')
|
||||||
|
expect(seo).toBeDefined()
|
||||||
|
expect(seo.keywords).toStrictEqual(['foo', 'bar'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw error if set a wrong type of config value', async () => {
|
||||||
|
await expect(
|
||||||
|
service.patchAndValid('seo', { title: true } as any),
|
||||||
|
).rejects.toThrow(BadRequestException)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should emit event if enable email option and update search', async () => {
|
||||||
|
await service.patchAndValid('mailOptions', { enable: true })
|
||||||
|
expect(mockEmitFn).toBeCalledTimes(1)
|
||||||
|
mockEmitFn.mockClear()
|
||||||
|
|
||||||
|
await service.patchAndValid('mailOptions', { pass: '*' })
|
||||||
|
expect(mockEmitFn).toBeCalledTimes(1)
|
||||||
|
mockEmitFn.mockClear()
|
||||||
|
|
||||||
|
await service.patchAndValid('mailOptions', { pass: '*', enable: false })
|
||||||
|
expect(mockEmitFn).toBeCalledTimes(0)
|
||||||
|
mockEmitFn.mockClear()
|
||||||
|
|
||||||
|
await service.patchAndValid('algoliaSearchOptions', {
|
||||||
|
enable: true,
|
||||||
|
})
|
||||||
|
expect(mockEmitFn).toBeCalledTimes(1)
|
||||||
|
mockEmitFn.mockClear()
|
||||||
|
|
||||||
|
await service.patchAndValid('algoliaSearchOptions', {
|
||||||
|
indexName: 'x',
|
||||||
|
})
|
||||||
|
expect(mockEmitFn).toBeCalledTimes(1)
|
||||||
|
mockEmitFn.mockClear()
|
||||||
|
|
||||||
|
await service.patchAndValid('algoliaSearchOptions', {
|
||||||
|
enable: false,
|
||||||
|
})
|
||||||
|
expect(mockEmitFn).toBeCalledTimes(0)
|
||||||
|
mockEmitFn.mockClear()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -2,19 +2,24 @@ import { NestFastifyApplication } from '@nestjs/platform-fastify'
|
|||||||
import { Test } from '@nestjs/testing'
|
import { Test } from '@nestjs/testing'
|
||||||
import { getModelForClass } from '@typegoose/typegoose'
|
import { getModelForClass } from '@typegoose/typegoose'
|
||||||
import { getModelToken } from 'nestjs-typegoose'
|
import { getModelToken } from 'nestjs-typegoose'
|
||||||
|
import { dbHelper } from 'test/helper/db-mock.helper'
|
||||||
import { setupE2EApp } from 'test/helper/register-app.helper'
|
import { setupE2EApp } from 'test/helper/register-app.helper'
|
||||||
import { firstKeyOfMap } from 'test/helper/utils.helper'
|
|
||||||
import { SnippetController } from '~/modules/snippet/snippet.controller'
|
import { SnippetController } from '~/modules/snippet/snippet.controller'
|
||||||
import { SnippetModel, SnippetType } from '~/modules/snippet/snippet.model'
|
import { SnippetModel, SnippetType } from '~/modules/snippet/snippet.model'
|
||||||
import { SnippetService } from '~/modules/snippet/snippet.service'
|
import { SnippetService } from '~/modules/snippet/snippet.service'
|
||||||
|
|
||||||
const mockingoose = require('mockingoose')
|
describe('test /snippets', () => {
|
||||||
|
|
||||||
describe.only('test /snippets', () => {
|
|
||||||
let app: NestFastifyApplication
|
let app: NestFastifyApplication
|
||||||
const model = getModelForClass(SnippetModel)
|
|
||||||
|
|
||||||
const mockTable = new Map()
|
beforeAll(async () => {
|
||||||
|
await dbHelper.connect()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await dbHelper.clear()
|
||||||
|
await dbHelper.close()
|
||||||
|
})
|
||||||
|
const model = getModelForClass(SnippetModel)
|
||||||
|
|
||||||
const mockPayload1: Partial<SnippetModel> = Object.freeze({
|
const mockPayload1: Partial<SnippetModel> = Object.freeze({
|
||||||
name: 'Snippet_1',
|
name: 'Snippet_1',
|
||||||
@@ -38,27 +43,6 @@ describe.only('test /snippets', () => {
|
|||||||
app = await setupE2EApp(ref)
|
app = await setupE2EApp(ref)
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockingoose(model).toReturn(
|
|
||||||
{
|
|
||||||
...mockPayload1,
|
|
||||||
_id: '61dfc5e1db3c871756fa5f9c',
|
|
||||||
},
|
|
||||||
'findOne',
|
|
||||||
)
|
|
||||||
mockingoose(model).toReturn(
|
|
||||||
{
|
|
||||||
...mockPayload1,
|
|
||||||
_id: '61dfc5e1db3c871756fa5f9c',
|
|
||||||
},
|
|
||||||
'countDocuments',
|
|
||||||
)
|
|
||||||
mockTable.set('61dfc5e1db3c871756fa5f9c', {
|
|
||||||
...mockPayload1,
|
|
||||||
_id: '121212',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('POST /snippets, should 422 with wrong name', async () => {
|
test('POST /snippets, should 422 with wrong name', async () => {
|
||||||
await app
|
await app
|
||||||
.inject({
|
.inject({
|
||||||
@@ -76,6 +60,19 @@ describe.only('test /snippets', () => {
|
|||||||
expect(res.statusCode).toBe(422)
|
expect(res.statusCode).toBe(422)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
let id: string
|
||||||
|
test('POST /snippets, should create successfully', async () => {
|
||||||
|
const res = await app.inject({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/snippets',
|
||||||
|
payload: mockPayload1,
|
||||||
|
})
|
||||||
|
expect(res.statusCode).toBe(201)
|
||||||
|
const data = await res.json()
|
||||||
|
expect(data.name).toEqual(mockPayload1.name)
|
||||||
|
expect(data.id).toBeDefined()
|
||||||
|
id = data.id
|
||||||
|
})
|
||||||
|
|
||||||
test('POST /snippets, re-create same of name should return 400', async () => {
|
test('POST /snippets, re-create same of name should return 400', async () => {
|
||||||
await app
|
await app
|
||||||
@@ -98,7 +95,7 @@ describe.only('test /snippets', () => {
|
|||||||
await app
|
await app
|
||||||
.inject({
|
.inject({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/snippets/' + firstKeyOfMap(mockTable),
|
url: '/snippets/' + id,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const json = res.json()
|
const json = res.json()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { dbHelper } from 'test/helper/db-mock.helper'
|
|||||||
import { SnippetModel, SnippetType } from '~/modules/snippet/snippet.model'
|
import { SnippetModel, SnippetType } from '~/modules/snippet/snippet.model'
|
||||||
import { SnippetService } from '~/modules/snippet/snippet.service'
|
import { SnippetService } from '~/modules/snippet/snippet.service'
|
||||||
|
|
||||||
describe.only('test Snippet Service', () => {
|
describe('test Snippet Service', () => {
|
||||||
let service: SnippetService
|
let service: SnippetService
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user