chore: qaq

This commit is contained in:
Innei
2021-07-31 20:17:27 +08:00
parent d13829bcaa
commit 02f8004145
31 changed files with 563 additions and 68 deletions

4
global.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare global {
export type KV<T = any> = Record<string, T>
}
export {}

24
jest.config.js Normal file
View File

@@ -0,0 +1,24 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { pathsToModuleNameMapper } = require('ts-jest/utils')
// In the following statement, replace `./tsconfig` with the path to your `tsconfig` file
// which contains the path mapping (ie the `compilerOptions.paths` option):
const { compilerOptions } = require('./tsconfig.json')
module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: '.',
testRegex: '.*\\.spec\\.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
collectCoverageFrom: ['**/*.(t|j)s'],
coverageDirectory: '../coverage',
testEnvironment: 'node',
moduleNameMapper: {
...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
'^src/(.*)$': '<rootDir>/src/$1',
'^test/(.*)$': '<rootDir>/test/$1',
src: '<rootDir>/src',
'^test$': '<rootDir>/test',
},
}

View File

@@ -38,12 +38,15 @@
},
"dependencies": {
"@nestjs/common": "^8.0.4",
"@nestjs/config": "^1.0.1",
"@nestjs/core": "^8.0.4",
"@nestjs/passport": "^8.0.0",
"@nestjs/platform-fastify": "^8.0.4",
"@nestjs/swagger": "^5.0.8",
"@typegoose/auto-increment": "^0.6.0",
"@typegoose/typegoose": "8.0.0",
"@types/bcrypt": "^5.0.0",
"argv": "^0.0.2",
"bcrypt": "^5.0.1",
"chalk": "^4.1.1",
"class-transformer": "^0.4.0",
@@ -53,13 +56,15 @@
"lint-staged": "^11.1.0",
"mongoose": "^5.13.3",
"mongoose-lean-virtuals": "^0.8.0",
"nestjs-typegoose": "^7.1.38",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"snakecase-keys": "^4.0.2"
"snakecase-keys": "^4.0.2",
"zx": "^2.0.0"
},
"devDependencies": {
"@nestjs/cli": "^8.0.2",
@@ -72,7 +77,6 @@
"@typescript-eslint/eslint-plugin": "4.28.4",
"@typescript-eslint/parser": "4.28.4",
"cross-env": "^7.0.3",
"dotenv": "^10.0.0",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
@@ -84,22 +88,5 @@
"ts-node": "^10.1.0",
"tsconfig-paths": "^3.10.1",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}

132
pnpm-lock.yaml generated
View File

@@ -3,7 +3,9 @@ lockfileVersion: 5.3
specifiers:
'@nestjs/cli': ^8.0.2
'@nestjs/common': ^8.0.4
'@nestjs/config': ^1.0.1
'@nestjs/core': ^8.0.4
'@nestjs/passport': ^8.0.0
'@nestjs/platform-fastify': ^8.0.4
'@nestjs/schematics': ^8.0.2
'@nestjs/swagger': ^5.0.8
@@ -17,12 +19,12 @@ specifiers:
'@types/supertest': ^2.0.11
'@typescript-eslint/eslint-plugin': 4.28.4
'@typescript-eslint/parser': 4.28.4
argv: ^0.0.2
bcrypt: ^5.0.1
chalk: ^4.1.1
class-transformer: ^0.4.0
class-validator: ^0.13.1
cross-env: ^7.0.3
dotenv: ^10.0.0
eslint: ^7.31.0
eslint-config-prettier: ^8.3.0
eslint-plugin-prettier: ^3.4.0
@@ -32,6 +34,7 @@ specifiers:
lint-staged: ^11.1.0
mongoose: ^5.13.3
mongoose-lean-virtuals: ^0.8.0
nestjs-typegoose: ^7.1.38
passport: ^0.4.1
passport-jwt: ^4.0.0
passport-local: ^1.0.0
@@ -46,15 +49,19 @@ specifiers:
ts-node: ^10.1.0
tsconfig-paths: ^3.10.1
typescript: ^4.3.5
zx: ^2.0.0
dependencies:
'@nestjs/common': 8.0.4_e911b02ebc86a0e92191a7b133b2fcd0
'@nestjs/config': 1.0.1_e4988ec8350f50541cb1b028c87076a8
'@nestjs/core': 8.0.4_e4988ec8350f50541cb1b028c87076a8
'@nestjs/passport': 8.0.0_8ffbbd1dfbd3e60a0a9ef125c15ec754
'@nestjs/platform-fastify': 8.0.4_dbb23fa745e40b87eda38a58a66b6bd5
'@nestjs/swagger': 5.0.8_00ec01bd1a1595869e3c10a21b92c5e7
'@typegoose/auto-increment': 0.6.0_mongoose@5.13.3
'@typegoose/typegoose': 8.0.0_mongoose@5.13.3
'@types/bcrypt': 5.0.0
argv: 0.0.2
bcrypt: 5.0.1
chalk: 4.1.1
class-transformer: 0.4.0
@@ -64,6 +71,7 @@ dependencies:
lint-staged: 11.1.0
mongoose: 5.13.3
mongoose-lean-virtuals: 0.8.0_mongoose@5.13.3
nestjs-typegoose: 7.1.38_ad6502a9e63d94834e95766efca460e3
passport: 0.4.1
passport-jwt: 4.0.0
passport-local: 1.0.0
@@ -71,6 +79,7 @@ dependencies:
rimraf: 3.0.2
rxjs: 7.2.0
snakecase-keys: 4.0.2
zx: 2.0.0
devDependencies:
'@nestjs/cli': 8.0.2
@@ -83,7 +92,6 @@ devDependencies:
'@typescript-eslint/eslint-plugin': 4.28.4_b1648df9f9ba40bdeef3710a5a5af353
'@typescript-eslint/parser': 4.28.4_eslint@7.31.0+typescript@4.3.5
cross-env: 7.0.3
dotenv: 10.0.0
eslint: 7.31.0
eslint-config-prettier: 8.3.0_eslint@7.31.0
eslint-plugin-prettier: 3.4.0_19f511d6aa08b367b6cb59e8f50291ca
@@ -829,6 +837,24 @@ packages:
- debug
dev: false
/@nestjs/config/1.0.1_e4988ec8350f50541cb1b028c87076a8:
resolution: {integrity: sha512-azMl4uYlFIhYsywFxPJT81RxF3Pnn0TZW3EEmr0Wa0Wex8R2xpvBNrCcrOgW3TB1xGMP7eqBrlfsVh5ZP82szg==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0
reflect-metadata: ^0.1.13
rxjs: ^6.0.0 || ^7.2.0
dependencies:
'@nestjs/common': 8.0.4_e911b02ebc86a0e92191a7b133b2fcd0
dotenv: 10.0.0
dotenv-expand: 5.1.0
lodash.get: 4.4.2
lodash.has: 4.5.2
lodash.set: 4.3.2
reflect-metadata: 0.1.13
rxjs: 7.2.0
uuid: 8.3.2
dev: false
/@nestjs/core/8.0.4_e4988ec8350f50541cb1b028c87076a8:
resolution: {integrity: sha512-1clCaGtVZTE7/tEvVj1FLwgFTHIlaCvwfPSMgxhGalTrx+cM8MoqEGLWklqS5bS+YfQscI1+Gn3OLdWlRcGbRQ==}
requiresBuild: true
@@ -873,6 +899,16 @@ packages:
reflect-metadata: 0.1.13
dev: false
/@nestjs/passport/8.0.0_8ffbbd1dfbd3e60a0a9ef125c15ec754:
resolution: {integrity: sha512-pIuOA+dEgOW0hg/qVV9uegitUHN1dKY8nEZhOtwj9FErJfCVZpQce8OH06n7yngqGTzvkkjjr/TzfuA1o8/A5A==}
peerDependencies:
'@nestjs/common': ^6.0.0 || ^7.0.0 || ^8.0.0
passport: ^0.4.0
dependencies:
'@nestjs/common': 8.0.4_e911b02ebc86a0e92191a7b133b2fcd0
passport: 0.4.1
dev: false
/@nestjs/platform-fastify/8.0.4_dbb23fa745e40b87eda38a58a66b6bd5:
resolution: {integrity: sha512-fseMiMf2eDYdREFRzZnVwIPrhvVHG42UnsX5FSqQlWfECe5NZ7ErOnKh537QgPS+VOQhuhsyiQkArPWrBDbNSg==}
peerDependencies:
@@ -1133,6 +1169,12 @@ packages:
'@types/serve-static': 1.13.10
dev: true
/@types/fs-extra/9.0.12:
resolution: {integrity: sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==}
dependencies:
'@types/node': 16.4.8
dev: false
/@types/graceful-fs/4.1.5:
resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==}
dependencies:
@@ -1170,6 +1212,10 @@ packages:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
dev: true
/@types/minimist/1.2.2:
resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
dev: false
/@types/mongodb/3.6.20:
resolution: {integrity: sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==}
dependencies:
@@ -1177,6 +1223,13 @@ packages:
'@types/node': 16.4.1
dev: false
/@types/node-fetch/2.5.12:
resolution: {integrity: sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==}
dependencies:
'@types/node': 16.4.8
form-data: 3.0.1
dev: false
/@types/node/15.14.2:
resolution: {integrity: sha512-dvMUE/m2LbXPwlvVuzCyslTEtQ2ZwuuFClDrOQ6mp2CenCg971719PTILZ4I6bTP27xfFFc+o7x2TkLuun/MPw==}
dev: false
@@ -1193,6 +1246,10 @@ packages:
resolution: {integrity: sha512-FKyawK3o5KL16AwbeFajen8G4K3mmqUrQsehn5wNKs8IzlKHE8TfnSmILXVMVziAEcnB23u1RCFU1NT6hSyr7Q==}
dev: false
/@types/node/16.4.8:
resolution: {integrity: sha512-VL7RZyCpfYEmbyd3/Eq5RNYhZt7yoL1JThZQ3KzimzhLya2Qa86U1ZZmioNWAAjiz99z1ED1xF9NUV2srvfVrA==}
dev: false
/@types/parse-json/4.0.0:
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
@@ -1670,6 +1727,11 @@ packages:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: false
/argv/0.0.2:
resolution: {integrity: sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=}
engines: {node: '>=0.6.10'}
dev: false
/array-union/2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
@@ -1689,7 +1751,6 @@ packages:
/asynckit/0.4.0:
resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=}
dev: true
/at-least-node/1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
@@ -2094,7 +2155,6 @@ packages:
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: true
/commander/2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@@ -2271,7 +2331,6 @@ packages:
/delayed-stream/1.0.0:
resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=}
engines: {node: '>=0.4.0'}
dev: true
/delegates/1.0.0:
resolution: {integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=}
@@ -2345,10 +2404,14 @@ packages:
tslib: 2.3.0
dev: false
/dotenv-expand/5.1.0:
resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==}
dev: false
/dotenv/10.0.0:
resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==}
engines: {node: '>=10'}
dev: true
dev: false
/ecdsa-sig-formatter/1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
@@ -2922,7 +2985,6 @@ packages:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.31
dev: true
/formidable/1.2.2:
resolution: {integrity: sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==}
@@ -2945,7 +3007,6 @@ packages:
graceful-fs: 4.2.6
jsonfile: 6.1.0
universalify: 2.0.0
dev: true
/fs-extra/9.1.0:
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
@@ -3082,7 +3143,6 @@ packages:
/graceful-fs/4.2.6:
resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==}
dev: true
/has-bigints/1.0.1:
resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==}
@@ -3305,6 +3365,10 @@ packages:
ci-info: 3.2.0
dev: true
/is-class/0.0.9:
resolution: {integrity: sha512-kUfRnejcRC9YLgblxoJ76dp9gZ3vMKTrDS5I6z2UVMOsHHSImNKCJocjQTkZr38PwiSZ9LVklaHEENaVYeFTXg==}
dev: false
/is-core-module/2.5.0:
resolution: {integrity: sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==}
dependencies:
@@ -4070,7 +4134,6 @@ packages:
universalify: 2.0.0
optionalDependencies:
graceful-fs: 4.2.6
dev: true
/jsonwebtoken/8.5.1:
resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==}
@@ -4205,6 +4268,14 @@ packages:
resolution: {integrity: sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=}
dev: true
/lodash.get/4.4.2:
resolution: {integrity: sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=}
dev: false
/lodash.has/4.5.2:
resolution: {integrity: sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=}
dev: false
/lodash.includes/4.3.0:
resolution: {integrity: sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=}
dev: false
@@ -4237,6 +4308,10 @@ packages:
resolution: {integrity: sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=}
dev: false
/lodash.set/4.3.2:
resolution: {integrity: sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=}
dev: false
/lodash.toarray/4.4.0:
resolution: {integrity: sha1-JMS/zWsvuji/0FlNsRedjptlZWE=}
dev: true
@@ -4358,14 +4433,12 @@ packages:
/mime-db/1.48.0:
resolution: {integrity: sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==}
engines: {node: '>= 0.6'}
dev: true
/mime-types/2.1.31:
resolution: {integrity: sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.48.0
dev: true
/mime/1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
@@ -4390,7 +4463,6 @@ packages:
/minimist/1.2.5:
resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==}
dev: true
/minipass/3.1.3:
resolution: {integrity: sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==}
@@ -4537,6 +4609,23 @@ packages:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: true
/nestjs-typegoose/7.1.38_ad6502a9e63d94834e95766efca460e3:
resolution: {integrity: sha512-hYgDliG2IS5CJkzRsTbbfwZk+IP0jEyCaxcRqqXvxZaP5miiH9UvGm3K0hg8X5DuVkmsgTzW3+MxOXmcLm9BOA==}
engines: {node: '>=8.10.0'}
peerDependencies:
'@nestjs/common': ^6.10.1 || ^7.0.0
'@nestjs/core': ^6.10.1 || ^7.0.0
'@typegoose/typegoose': ^6.2.1 || ^7.0.0
mongoose: ^5.10.6
dependencies:
'@nestjs/common': 8.0.4_e911b02ebc86a0e92191a7b133b2fcd0
'@nestjs/core': 8.0.4_e4988ec8350f50541cb1b028c87076a8
'@typegoose/typegoose': 8.0.0_mongoose@5.13.3
is-class: 0.0.9
mongoose: 5.13.3
reflect-metadata: 0.1.13
dev: false
/no-case/3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
dependencies:
@@ -5886,7 +5975,6 @@ packages:
/universalify/2.0.0:
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
engines: {node: '>= 10.0.0'}
dev: true
/uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -6158,3 +6246,19 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
dev: true
/zx/2.0.0:
resolution: {integrity: sha512-OF8YvqseMMmtDaASqO+8+0/tJZvykLK0hX9YBAaRO9l7Hc+YjNKjpgJTjrmncgEURoyDr9Ln4r/qBtEuDNZstg==}
engines: {node: '>= 14.8.0'}
hasBin: true
dependencies:
'@types/fs-extra': 9.0.12
'@types/minimist': 1.2.2
'@types/node': 16.4.8
'@types/node-fetch': 2.5.12
chalk: 4.1.1
fs-extra: 10.0.0
minimist: 1.2.5
node-fetch: 2.6.1
which: 2.0.2
dev: false

26
src/app.config.ts Normal file
View File

@@ -0,0 +1,26 @@
import argv from 'argv'
export const CROSS_DOMAIN = {
allowedOrigins: [
'innei.ren',
'shizuri.net',
'localhost:9528',
'localhost:2323',
'127.0.0.1',
'mbp.cc',
'local.innei.test',
'22333322.xyz',
],
allowedReferer: 'innei.ren',
}
export const MONGO_DB = {
uri: `mongodb://127.0.0.1:${argv.dbport || '27017'}/mx-space`,
}
export const REDIS = {
host: argv.redis_host || 'localhost',
port: argv.redis_port || 6379,
password: (argv.redis_password || null) as string,
ttl: null,
defaultCacheTTL: 60 * 60 * 24,
}

View File

@@ -1,6 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing'
import { AppController } from './app.controller'
import { AppService } from './app.service'
describe('AppController', () => {
let appController: AppController
@@ -8,15 +7,19 @@ describe('AppController', () => {
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile()
appController = app.get<AppController>(AppController)
})
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!')
it('should return has schema object', async () => {
const obj = await appController.appInfo()
expect(Object.keys(obj)).toStrictEqual(['name', 'version', 'hash'])
})
it('should return pong', () => {
expect(appController.ping()).toBe('pong')
})
})
})

View File

@@ -1,12 +1,30 @@
import { Controller, Get } from '@nestjs/common'
import { AppService } from './app.service'
import PKG from '../package.json'
// import { $ } from 'zx'
import { execSync } from 'child_process'
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello()
async appInfo(): Promise<IAppInfo> {
const cmd = `git log --pretty=oneline | head -n 1 | cut -d' ' -f1`
const hash = execSync(cmd, { encoding: 'utf-8' }).split('\n')[0]
// const hash = await $`git log --pretty=oneline | head -n 1 | cut -d' ' -f1`
return {
// hash: hash.stdout,
name: PKG.name,
version: PKG.version,
hash,
}
}
@Get('/ping')
ping(): 'pong' {
return 'pong'
}
}
interface IAppInfo {
version: string
hash: string
name: string
}

View File

@@ -1,12 +1,30 @@
import { Module } from '@nestjs/common'
import { Module, OnModuleInit } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { InitModule } from './modules/init/init.module'
import { UserModule } from './modules/user/user.module'
import { HelperModule } from './modules/helper/helper.module'
import { PostModule } from './modules/post/post.module';
import { CategoryModule } from './modules/category/category.module';
@Module({
imports: [InitModule, UserModule],
imports: [
InitModule,
UserModule,
ConfigModule.forRoot({
envFilePath: [
'.env.development.local',
'.env.development',
'.env.production.local',
'.env.production',
'.env',
],
isGlobal: true,
}),
HelperModule,
PostModule,
CategoryModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

View File

@@ -1,8 +0,0 @@
import { Injectable } from '@nestjs/common'
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!'
}
}

View File

@@ -0,0 +1,16 @@
import { applyDecorators, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger'
import { isDev } from '~/utils'
export function Auth() {
const decorators = []
if (!isDev) {
decorators.push(UseGuards(AuthGuard('jwt')))
}
decorators.push(
ApiBearerAuth(),
ApiUnauthorizedResponse({ description: 'Unauthorized' }),
)
return applyDecorators(...decorators)
}

View File

@@ -8,7 +8,7 @@
*/
import { homedir } from 'os'
import { join } from 'path'
import { isDev } from 'src/utils'
import { isDev } from '~/utils'
export const HOME = homedir()

View File

@@ -1,14 +1,15 @@
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { NestFastifyApplication } from '@nestjs/platform-fastify'
import { fastifyApp } from './core/adapt/fastify'
import { fastifyApp } from './common/adapt/fastify'
import { isDev } from './utils'
import { Logger } from '@nestjs/common'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import { CROSS_DOMAIN } from './app.config'
// const PORT = parseInt(process.env.PORT) || 2333
const PORT = 2333
const APIVersion = 1
const Origin = process.env.ORIGIN || ''
const Origin = CROSS_DOMAIN.allowedOrigins
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
@@ -16,7 +17,7 @@ async function bootstrap() {
fastifyApp,
)
const hosts = Origin.split(',').map((host) => new RegExp(host, 'i'))
const hosts = Origin.map((host) => new RegExp(host, 'i'))
app.enableCors({
origin: (origin, callback) => {
@@ -47,6 +48,7 @@ async function bootstrap() {
if (isDev) {
Logger.debug(`http://localhost:${PORT}/api-docs`)
}
Logger.log('Server is up.')
})
}

View File

@@ -0,0 +1,22 @@
import { DocumentType, index, modelOptions, prop } from '@typegoose/typegoose'
import { BaseModel } from '~/shared/base.model'
export type CategoryDocument = DocumentType<CategoryModel>
export enum CategoryType {
Category,
Tag,
}
@index({ slug: -1 })
@modelOptions({ options: { customName: 'Category' } })
export class CategoryModel extends BaseModel {
@prop({ unique: true, trim: true, required: true })
name!: string
@prop({ default: CategoryType.Category })
type?: CategoryType
@prop({ unique: true, required: true })
slug!: string
}

View File

@@ -0,0 +1,10 @@
import { forwardRef, Module } from '@nestjs/common'
import { PostModule } from '../post/post.module'
import { CategoryService } from './category.service'
@Module({
imports: [forwardRef(() => PostModule)],
providers: [CategoryService],
exports: [CategoryService],
})
export class CategoryModule {}

View File

@@ -0,0 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing'
import { getFakeCategoryModel, getFakePostModel } from 'test/db-model.mock'
import { PostModule } from '../post/post.module'
import { CategoryService } from './category.service'
describe('CategoryService', () => {
let service: CategoryService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [getFakePostModel(), getFakeCategoryModel(), CategoryService],
imports: [PostModule],
}).compile()
service = module.get<CategoryService>(CategoryService)
})
it('should be defined', () => {
expect(service).toBeDefined()
})
})

View File

@@ -0,0 +1,19 @@
import { forwardRef, Inject, Injectable } from '@nestjs/common'
import { ReturnModelType } from '@typegoose/typegoose'
import { InjectModel } from 'nestjs-typegoose'
import { PostService } from '../post/post.service'
import { CategoryModel } from './category.model'
@Injectable()
export class CategoryService {
constructor(
@InjectModel(CategoryModel)
private readonly categoryModel: ReturnModelType<typeof CategoryModel>,
private postService: PostService,
) {}
findCategoryById(categoryId: string) {
return this.categoryModel.findById(categoryId)
}
}

View File

@@ -0,0 +1,27 @@
import { Global, Module } from '@nestjs/common'
import { TypegooseModule } from 'nestjs-typegoose'
import { MONGO_DB } from '~/app.config'
import { CategoryModel } from '../category/category.model'
import { PostModel } from '../post/post.model'
import { UserModel } from '../user/user.model'
const models = TypegooseModule.forFeature([UserModel, PostModel, CategoryModel])
@Module({
imports: [
TypegooseModule.forRootAsync({
useFactory: () => ({
uri: MONGO_DB.uri,
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true,
autoIndex: true,
}),
}),
models,
],
exports: [models],
})
@Global()
export class DbModule {}

View File

@@ -0,0 +1,5 @@
import { Module } from '@nestjs/common'
import { DbModule } from './db.module'
@Module({ imports: [DbModule] })
export class HelperModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { PostController } from './post.controller';
describe('PostController', () => {
let controller: PostController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [PostController],
}).compile();
controller = module.get<PostController>(PostController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,4 @@
import { Controller } from '@nestjs/common'
@Controller('posts')
export class PostController {}

View File

@@ -0,0 +1,44 @@
import { index, modelOptions, prop, Ref, Severity } from '@typegoose/typegoose'
import { Schema } from 'mongoose'
import { CountMixed as Count, WriteBaseModel } from '~/shared/base.model'
import { CategoryModel as Category } from '../category/category.model'
@index({ slug: 1 })
@index({ modified: -1 })
@index({ text: 'text' })
@modelOptions({ options: { customName: 'Post', allowMixed: Severity.ALLOW } })
export class PostModel extends WriteBaseModel {
@prop({ trim: true, unique: true, required: true })
slug!: string
@prop()
summary?: string
@prop({ ref: () => Category, required: true })
categoryId: Ref<Category>
@prop({
ref: () => Category,
foreignField: '_id',
localField: 'categoryId',
justOne: true,
})
public category: Ref<Category>
@prop({ default: false })
hide?: boolean
@prop({ default: true })
copyright?: boolean
@prop({
type: String,
})
tags?: string[]
@prop({ type: Count, default: { read: 0, like: 0 }, _id: false })
count?: Count
@prop({ type: Schema.Types.Mixed })
options?: Record<any, any>
}

View File

@@ -0,0 +1,12 @@
import { forwardRef, Module } from '@nestjs/common'
import { CategoryModule } from '../category/category.module'
import { PostController } from './post.controller'
import { PostService } from './post.service'
@Module({
controllers: [PostController],
providers: [PostService],
imports: [forwardRef(() => CategoryModule)],
exports: [PostService],
})
export class PostModule {}

View File

@@ -0,0 +1,22 @@
import { forwardRef } from '@nestjs/common'
import { Test, TestingModule } from '@nestjs/testing'
import { getFakeCategoryModel, getFakePostModel } from 'test/db-model.mock'
import { CategoryModule } from '../category/category.module'
import { PostService } from './post.service'
describe('PostService', () => {
let service: PostService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [getFakePostModel(), getFakeCategoryModel(), PostService],
imports: [forwardRef(() => CategoryModule)],
}).compile()
service = module.get<PostService>(PostService)
})
it('should be defined', () => {
expect(service).toBeDefined()
})
})

View File

@@ -0,0 +1,33 @@
import {
forwardRef,
Inject,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common'
import { ReturnModelType } from '@typegoose/typegoose'
import { InjectModel } from 'nestjs-typegoose'
import { CategoryService } from '../category/category.service'
import { PostModel } from './post.model'
@Injectable()
export class PostService {
constructor(
@InjectModel(PostModel)
private readonly postModel: ReturnModelType<typeof PostModel>,
@Inject(forwardRef(() => CategoryService))
private categoryService: CategoryService,
) {}
async create(post: Partial<PostModel>) {
const { categoryId } = post
const category = await this.categoryService.findCategoryById(
categoryId as any as string,
)
if (!category) {
throw new UnprocessableEntityException('分类丢失了 ಠ_ಠ')
}
const res = this.postModel.create(post)
return res
}
}

View File

@@ -1,7 +1,13 @@
import { DocumentType, modelOptions, prop } from '@typegoose/typegoose'
import {
DocumentType,
modelOptions,
prop,
Severity,
} from '@typegoose/typegoose'
import { Schema } from 'mongoose'
import { BaseModel } from 'src/shared/base.module'
import { hashSync } from 'bcrypt'
import { BaseModel } from 'src/shared/base.model'
export type UserDocument = DocumentType<UserModel>
export class OAuthModel {
@@ -25,7 +31,7 @@ export class TokenModel {
name: string
}
@modelOptions({ options: { customName: 'User' } })
@modelOptions({ options: { customName: 'User', allowMixed: Severity.ALLOW } })
export class UserModel extends BaseModel {
@prop({ required: true, unique: true, trim: true })
username!: string

View File

@@ -1,12 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing'
import { getModelToken } from 'nestjs-typegoose'
import { UserService } from './user.service'
const fakeModel = jest.fn()
describe('UserService', () => {
let service: UserService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UserService],
providers: [
UserService,
{
provide: getModelToken('UserModel'),
useValue: fakeModel,
},
],
}).compile()
service = module.get<UserService>(UserService)

View File

@@ -1,4 +1,12 @@
import { Injectable } from '@nestjs/common'
import { ReturnModelType } from '@typegoose/typegoose'
import { InjectModel } from 'nestjs-typegoose'
import { UserModel } from './user.model'
@Injectable()
export class UserService {}
export class UserService {
constructor(
@InjectModel(UserModel)
private userModel: ReturnModelType<typeof UserModel>,
) {}
}

View File

@@ -1,4 +1,4 @@
import { prop } from '@typegoose/typegoose'
import { modelOptions, prop } from '@typegoose/typegoose'
import { IsNotEmpty, IsString } from 'class-validator'
export class BaseModel {
created?: Date
@@ -48,3 +48,15 @@ export abstract class WriteBaseModel extends BaseCommentIndexModel {
@prop({ default: () => new Date() })
modified: Date
}
@modelOptions({
schemaOptions: { id: false, _id: false },
options: { customName: 'count' },
})
export class CountMixed {
@prop({ default: 0 })
read?: number
@prop({ default: 0 })
like?: number
}

20
test/db-model.mock.ts Normal file
View File

@@ -0,0 +1,20 @@
import { Provider } from '@nestjs/common'
import { getModelToken } from 'nestjs-typegoose'
const getFakeModel = () => jest.fn()
export const getFakePostModel = (): Provider<any> => {
const model = getFakeModel()
return {
useValue: model,
provide: getModelToken('PostModel'),
}
}
export const getFakeCategoryModel = (): Provider<any> => {
const model = getFakeModel()
return {
useValue: model,
provide: getModelToken('CategoryModel'),
}
}

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"module": "commonjs",
"module": "CommonJS",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
@@ -10,8 +10,17 @@
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"baseUrl": ".",
"incremental": true,
"skipLibCheck": true
"resolveJsonModule": true,
"skipLibCheck": true,
"paths": {
"~": [
"./src"
],
"~/*": [
"./src/*"
]
}
}
}