feat: init category module
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,3 +33,4 @@ lerna-debug.log*
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
patch/dist
|
||||
|
||||
36
bin/patch.js
Normal file
36
bin/patch.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// @ts-check
|
||||
const inquirer = require('inquirer')
|
||||
const chalk = require('chalk')
|
||||
const prompt = inquirer.createPromptModule()
|
||||
const package = require('../package.json')
|
||||
const { execSync } = require('child_process')
|
||||
const { resolve } = require('path')
|
||||
const { readdirSync } = require('fs')
|
||||
const PATCH_DIR = resolve(process.cwd(), './patch')
|
||||
|
||||
async function bootstarp() {
|
||||
console.log(chalk.yellowBright('mx-space server patch center'))
|
||||
|
||||
console.log(chalk.yellow(`current version: ${package.version}`))
|
||||
|
||||
const patchFiles = readdirSync(PATCH_DIR).filter(
|
||||
(file) => file.startsWith('v') && file.endsWith('.ts'),
|
||||
)
|
||||
|
||||
prompt({
|
||||
type: 'list',
|
||||
name: 'version',
|
||||
message: 'Select version you want to patch.',
|
||||
choices: patchFiles.map((f) => f.replace(/\.ts$/, '')),
|
||||
}).then(({ version }) => {
|
||||
execSync('yarn run build', {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
|
||||
const patchPath = resolve('dist/patch/', version + '.js')
|
||||
console.log(chalk.green('starting patch... ' + patchPath))
|
||||
execSync(`node ${patchPath}`, { encoding: 'utf8' })
|
||||
})
|
||||
}
|
||||
|
||||
bootstarp()
|
||||
13
package.json
13
package.json
@@ -35,7 +35,8 @@
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"patch": "node bin/patch.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^8.0.6",
|
||||
@@ -54,15 +55,16 @@
|
||||
"bcrypt": "^5.0.1",
|
||||
"cache-manager": "3.4.4",
|
||||
"cache-manager-ioredis": "^2.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"chalk": "*",
|
||||
"class-transformer": "^0.4.0",
|
||||
"class-validator": "^0.13.1",
|
||||
"dayjs": "^1.10.6",
|
||||
"ejs": "^3.1.6",
|
||||
"fastify-swagger": "^4.9.0",
|
||||
"image-size": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mongoose": "^5.13.7",
|
||||
"lodash": "*",
|
||||
"mongoose": "*",
|
||||
"dotenv": "*",
|
||||
"mongoose-lean-virtuals": "^0.8.0",
|
||||
"mongoose-paginate-v2": "^1.4.2",
|
||||
"nanoid": "^3.1.25",
|
||||
@@ -74,7 +76,8 @@
|
||||
"redis": "3.1.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.3.0",
|
||||
"snakecase-keys": "^4.0.2"
|
||||
"snakecase-keys": "^4.0.2",
|
||||
"inquirer": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@innei-util/eslint-config-ts": "^0.2.3",
|
||||
|
||||
39
patch/bootstrap.ts
Normal file
39
patch/bootstrap.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { getModelForClass, mongoose } from '@typegoose/typegoose'
|
||||
import { config } from 'dotenv'
|
||||
import { ConnectionBase } from 'mongoose'
|
||||
import * as APP from '../src/app.config'
|
||||
import { CategoryModel } from '../src/modules/category/category.model'
|
||||
import { NoteModel } from '../src/modules/note/note.model'
|
||||
import { PostModel } from '../src/modules/post/post.model'
|
||||
const env = config().parsed || {}
|
||||
const url = APP.MONGO_DB.uri
|
||||
|
||||
const opt = {
|
||||
useCreateIndex: true,
|
||||
useFindAndModify: false,
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
autoIndex: true,
|
||||
}
|
||||
mongoose.connect(url, opt)
|
||||
const post = getModelForClass(PostModel)
|
||||
const note = getModelForClass(NoteModel)
|
||||
const category = getModelForClass(CategoryModel)
|
||||
|
||||
const Config = {
|
||||
env,
|
||||
db: (mongoose.connection as any).client.db('mx-space-next') as ConnectionBase,
|
||||
models: {
|
||||
post,
|
||||
note,
|
||||
category,
|
||||
},
|
||||
}
|
||||
async function bootstrap(cb: (config: typeof Config) => any) {
|
||||
await cb.call(this, Config)
|
||||
|
||||
mongoose.disconnect()
|
||||
process.exit()
|
||||
}
|
||||
|
||||
export { bootstrap as patch }
|
||||
11
patch/global.d.ts
vendored
Normal file
11
patch/global.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ModelType } from '@typegoose/typegoose/lib/types'
|
||||
import { Document, PaginateModel } from 'mongoose'
|
||||
/// <reference types="../global" />
|
||||
declare global {
|
||||
export type KV<T = any> = Record<string, T>
|
||||
|
||||
// @ts-ignore
|
||||
export type MongooseModel<T> = ModelType<T> & PaginateModel<T & Document>
|
||||
}
|
||||
|
||||
export {}
|
||||
1
patch/index.ts
Normal file
1
patch/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './bootstrap'
|
||||
34
patch/tsconfig.json
Normal file
34
patch/tsconfig.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"declaration": false,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": false,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": ".",
|
||||
"noImplicitAny": false,
|
||||
"incremental": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"~": [
|
||||
"../src"
|
||||
],
|
||||
"~/*": [
|
||||
"../src/*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"../src"
|
||||
],
|
||||
"exclude": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
17
patch/v2.0.0-alpha.1.ts
Normal file
17
patch/v2.0.0-alpha.1.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// patch for version lower than v2.0.0-alpha.1
|
||||
|
||||
import { patch } from './bootstrap'
|
||||
|
||||
patch(async ({ models: { note, post, category } }) => {
|
||||
await Promise.all([
|
||||
[note, post].map((model) => {
|
||||
return model.updateMany(
|
||||
{},
|
||||
{
|
||||
$unset: ['options'],
|
||||
},
|
||||
)
|
||||
}),
|
||||
category.aggregate([{ $unset: 'count' }]),
|
||||
])
|
||||
})
|
||||
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@@ -34,22 +34,24 @@ specifiers:
|
||||
bcrypt: ^5.0.1
|
||||
cache-manager: 3.4.4
|
||||
cache-manager-ioredis: ^2.1.0
|
||||
chalk: ^4.1.2
|
||||
chalk: '*'
|
||||
class-transformer: ^0.4.0
|
||||
class-validator: ^0.13.1
|
||||
cross-env: ^7.0.3
|
||||
dayjs: ^1.10.6
|
||||
dotenv: '*'
|
||||
ejs: ^3.1.6
|
||||
eslint: ^7.32.0
|
||||
fastify: '*'
|
||||
fastify-swagger: ^4.9.0
|
||||
husky: ^7.0.1
|
||||
image-size: ^1.0.0
|
||||
inquirer: '*'
|
||||
ioredis: '*'
|
||||
jest: 27.1.0
|
||||
lint-staged: ^11.1.2
|
||||
lodash: ^4.17.21
|
||||
mongoose: ^5.13.7
|
||||
lodash: '*'
|
||||
mongoose: '*'
|
||||
mongoose-lean-virtuals: ^0.8.0
|
||||
mongoose-paginate-v2: ^1.4.2
|
||||
nanoid: ^3.1.25
|
||||
@@ -96,9 +98,11 @@ dependencies:
|
||||
class-transformer: 0.4.0
|
||||
class-validator: 0.13.1
|
||||
dayjs: 1.10.6
|
||||
dotenv: 10.0.0
|
||||
ejs: 3.1.6
|
||||
fastify-swagger: 4.9.1
|
||||
image-size: 1.0.0
|
||||
inquirer: 8.1.1
|
||||
lodash: 4.17.21
|
||||
mongoose: 5.13.8
|
||||
mongoose-lean-virtuals: 0.8.0_mongoose@5.13.8
|
||||
@@ -2068,7 +2072,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
type-fest: 0.21.3
|
||||
dev: true
|
||||
|
||||
/ansi-regex/2.1.1:
|
||||
resolution: {integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8=}
|
||||
@@ -2083,7 +2086,6 @@ packages:
|
||||
/ansi-regex/5.0.0:
|
||||
resolution: {integrity: sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/ansi-styles/3.2.1:
|
||||
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
||||
@@ -2325,7 +2327,6 @@ packages:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.0
|
||||
dev: true
|
||||
|
||||
/bluebird/3.5.1:
|
||||
resolution: {integrity: sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==}
|
||||
@@ -2473,7 +2474,6 @@ packages:
|
||||
|
||||
/chardet/0.7.0:
|
||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||
dev: true
|
||||
|
||||
/chokidar/3.5.2:
|
||||
resolution: {integrity: sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==}
|
||||
@@ -2530,12 +2530,10 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
restore-cursor: 3.1.0
|
||||
dev: true
|
||||
|
||||
/cli-spinners/2.6.0:
|
||||
resolution: {integrity: sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/cli-table3/0.5.1:
|
||||
resolution: {integrity: sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==}
|
||||
@@ -2558,7 +2556,6 @@ packages:
|
||||
/cli-width/3.0.0:
|
||||
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
|
||||
engines: {node: '>= 10'}
|
||||
dev: true
|
||||
|
||||
/cliui/7.0.4:
|
||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
||||
@@ -2571,7 +2568,6 @@ packages:
|
||||
/clone/1.0.4:
|
||||
resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: true
|
||||
|
||||
/cluster-key-slot/1.1.0:
|
||||
resolution: {integrity: sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==}
|
||||
@@ -2799,7 +2795,6 @@ packages:
|
||||
resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=}
|
||||
dependencies:
|
||||
clone: 1.0.4
|
||||
dev: true
|
||||
|
||||
/define-properties/1.1.3:
|
||||
resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==}
|
||||
@@ -2921,7 +2916,6 @@ packages:
|
||||
|
||||
/emoji-regex/8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: true
|
||||
|
||||
/encodeurl/1.0.2:
|
||||
resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=}
|
||||
@@ -3259,7 +3253,6 @@ packages:
|
||||
chardet: 0.7.0
|
||||
iconv-lite: 0.4.24
|
||||
tmp: 0.0.33
|
||||
dev: true
|
||||
|
||||
/fast-decode-uri-component/1.0.1:
|
||||
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
||||
@@ -3385,7 +3378,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
dev: true
|
||||
|
||||
/file-entry-cache/6.0.1:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
@@ -3752,7 +3744,6 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: true
|
||||
|
||||
/ieee754/1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
@@ -3853,7 +3844,6 @@ packages:
|
||||
string-width: 4.2.2
|
||||
strip-ansi: 6.0.0
|
||||
through: 2.3.8
|
||||
dev: true
|
||||
|
||||
/internal-slot/1.0.3:
|
||||
resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
|
||||
@@ -3965,7 +3955,6 @@ packages:
|
||||
/is-fullwidth-code-point/3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-function/1.0.2:
|
||||
resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==}
|
||||
@@ -3986,7 +3975,6 @@ packages:
|
||||
/is-interactive/1.0.0:
|
||||
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-negative-zero/2.0.1:
|
||||
resolution: {integrity: sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==}
|
||||
@@ -4053,7 +4041,6 @@ packages:
|
||||
/is-unicode-supported/0.1.0:
|
||||
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/isarray/1.0.0:
|
||||
resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=}
|
||||
@@ -4939,7 +4926,6 @@ packages:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
dev: true
|
||||
|
||||
/log-update/4.0.0:
|
||||
resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
|
||||
@@ -5068,7 +5054,6 @@ packages:
|
||||
/mimic-fn/2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/min-document/2.19.0:
|
||||
resolution: {integrity: sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=}
|
||||
@@ -5232,7 +5217,6 @@ packages:
|
||||
|
||||
/mute-stream/0.0.8:
|
||||
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
|
||||
dev: true
|
||||
|
||||
/nanoid/3.1.25:
|
||||
resolution: {integrity: sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==}
|
||||
@@ -5407,7 +5391,6 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
mimic-fn: 2.1.0
|
||||
dev: true
|
||||
|
||||
/openapi-types/9.3.0:
|
||||
resolution: {integrity: sha512-sR23YjmuwDSMsQVZDHbV9mPgi0RyniQlqR0AQxTC2/F3cpSjRFMH3CFPjoWvNqhC4OxPkDYNb2l8Mc1Me6D/KQ==}
|
||||
@@ -5474,7 +5457,6 @@ packages:
|
||||
log-symbols: 4.1.0
|
||||
strip-ansi: 6.0.0
|
||||
wcwidth: 1.0.1
|
||||
dev: true
|
||||
|
||||
/os-name/4.0.1:
|
||||
resolution: {integrity: sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==}
|
||||
@@ -5487,7 +5469,6 @@ packages:
|
||||
/os-tmpdir/1.0.2:
|
||||
resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/p-each-series/2.2.0:
|
||||
resolution: {integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==}
|
||||
@@ -5950,7 +5931,6 @@ packages:
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.3
|
||||
dev: true
|
||||
|
||||
/ret/0.2.2:
|
||||
resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==}
|
||||
@@ -5972,7 +5952,6 @@ packages:
|
||||
/run-async/2.4.1:
|
||||
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/run-parallel/1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
@@ -5990,7 +5969,6 @@ packages:
|
||||
engines: {npm: '>=2.0.0'}
|
||||
dependencies:
|
||||
tslib: 1.14.1
|
||||
dev: true
|
||||
|
||||
/rxjs/7.3.0:
|
||||
resolution: {integrity: sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==}
|
||||
@@ -6011,7 +5989,6 @@ packages:
|
||||
|
||||
/safer-buffer/2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
dev: true
|
||||
|
||||
/saslprep/1.0.3:
|
||||
resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==}
|
||||
@@ -6335,7 +6312,6 @@ packages:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.0
|
||||
dev: true
|
||||
|
||||
/string.prototype.trimend/1.0.4:
|
||||
resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==}
|
||||
@@ -6390,7 +6366,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.0
|
||||
dev: true
|
||||
|
||||
/strip-bom/3.0.0:
|
||||
resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=}
|
||||
@@ -6578,7 +6553,6 @@ packages:
|
||||
|
||||
/through/2.3.8:
|
||||
resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=}
|
||||
dev: true
|
||||
|
||||
/timm/1.7.1:
|
||||
resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==}
|
||||
@@ -6597,7 +6571,6 @@ packages:
|
||||
engines: {node: '>=0.6.0'}
|
||||
dependencies:
|
||||
os-tmpdir: 1.0.2
|
||||
dev: true
|
||||
|
||||
/tmpl/1.0.4:
|
||||
resolution: {integrity: sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=}
|
||||
@@ -6745,7 +6718,6 @@ packages:
|
||||
|
||||
/tslib/1.14.1:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
dev: true
|
||||
|
||||
/tslib/2.1.0:
|
||||
resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==}
|
||||
@@ -6795,7 +6767,6 @@ packages:
|
||||
/type-fest/0.21.3:
|
||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/typedarray-to-buffer/3.1.5:
|
||||
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
|
||||
@@ -6913,7 +6884,6 @@ packages:
|
||||
resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=}
|
||||
dependencies:
|
||||
defaults: 1.0.3
|
||||
dev: true
|
||||
|
||||
/webidl-conversions/5.0.0:
|
||||
resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
|
||||
|
||||
54
src/modules/category/category.controller.ts
Normal file
54
src/modules/category/category.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Controller, forwardRef, Get, Inject, Query } from '@nestjs/common'
|
||||
import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||
import { PostService } from '../post/post.service'
|
||||
import { CategoryType, MultiCategoriesQueryDto } from './category.dto'
|
||||
import { CategoryService } from './category.service'
|
||||
|
||||
@Controller({ path: 'categories' })
|
||||
@ApiName
|
||||
export class CategoryController {
|
||||
constructor(
|
||||
private readonly categoryService: CategoryService,
|
||||
@Inject(forwardRef(() => PostService))
|
||||
private readonly postService: PostService,
|
||||
) {}
|
||||
|
||||
@Get('/')
|
||||
async getCategories(@Query() query: MultiCategoriesQueryDto) {
|
||||
const { ids, joint, type = CategoryType.Category } = query // categories is category's mongo id
|
||||
if (ids) {
|
||||
return joint
|
||||
? await Promise.all(
|
||||
ids.map(async (id) => {
|
||||
return await this.postService.model.find(
|
||||
{ categoryId: id },
|
||||
{
|
||||
select: 'title slug _id categoryId created modified',
|
||||
sort: { created: -1 },
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
: await Promise.all(
|
||||
ids.map(async (id) => {
|
||||
const posts = await this.postService.model.find(
|
||||
{ categoryId: id },
|
||||
{
|
||||
select: 'title slug _id created modified',
|
||||
sort: { created: -1 },
|
||||
},
|
||||
)
|
||||
const category = await this.categoryService.model
|
||||
.findById(id)
|
||||
.lean()
|
||||
return {
|
||||
category: { ...category, children: posts },
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
return type === CategoryType.Category
|
||||
? await this.categoryService.model.find({ type }).lean()
|
||||
: await this.categoryService.getPostTagsSum()
|
||||
}
|
||||
}
|
||||
86
src/modules/category/category.dto.ts
Normal file
86
src/modules/category/category.dto.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { UnprocessableEntityException } from '@nestjs/common'
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { Transform } from 'class-transformer'
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsMongoId,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator'
|
||||
import { uniq } from 'lodash'
|
||||
import { IsBooleanOrString } from '~/utils/validator/isBooleanOrString'
|
||||
|
||||
export enum CategoryType {
|
||||
Category,
|
||||
Tag,
|
||||
}
|
||||
|
||||
export class CategoryDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty()
|
||||
name: string
|
||||
|
||||
@IsEnum(CategoryType)
|
||||
@IsOptional()
|
||||
@ApiProperty({ enum: [0, 1] })
|
||||
type?: CategoryType
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsOptional()
|
||||
slug?: string
|
||||
}
|
||||
|
||||
export class SlugOrIdDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty()
|
||||
query?: string
|
||||
}
|
||||
|
||||
export class MultiQueryTagAndCategoryDto {
|
||||
@IsOptional()
|
||||
@Transform(({ value: val }) => {
|
||||
if (val === '1' || val === 'true') {
|
||||
return true
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
})
|
||||
@IsBooleanOrString()
|
||||
tag?: boolean | string
|
||||
}
|
||||
export class MultiCategoriesQueryDto {
|
||||
@IsOptional()
|
||||
@IsMongoId({
|
||||
each: true,
|
||||
message: '多分类查询使用逗号分隔, 应为 mongoID',
|
||||
})
|
||||
@Transform(({ value: v }) => uniq(v.split(',')))
|
||||
ids?: Array<string>
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform((b) => Boolean(b))
|
||||
@ApiProperty({ enum: [1, 0] })
|
||||
joint?: boolean
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value: v }: { value: string }) => {
|
||||
if (typeof v !== 'string') {
|
||||
throw new UnprocessableEntityException('type must be a string')
|
||||
}
|
||||
switch (v.toLowerCase()) {
|
||||
case 'category':
|
||||
return CategoryType.Category
|
||||
case 'tag':
|
||||
return CategoryType.Tag
|
||||
default:
|
||||
return CategoryType.Category
|
||||
}
|
||||
})
|
||||
type: CategoryType
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { forwardRef, Module } from '@nestjs/common'
|
||||
import { PostModule } from '../post/post.module'
|
||||
import { CategoryController } from './category.controller'
|
||||
import { CategoryService } from './category.service'
|
||||
|
||||
@Module({
|
||||
providers: [CategoryService],
|
||||
exports: [CategoryService],
|
||||
controllers: [CategoryController],
|
||||
imports: [forwardRef(() => PostModule)],
|
||||
})
|
||||
export class CategoryModule {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
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()
|
||||
@@ -8,6 +9,8 @@ export class CategoryService {
|
||||
constructor(
|
||||
@InjectModel(CategoryModel)
|
||||
private readonly categoryModel: ReturnModelType<typeof CategoryModel>,
|
||||
@Inject(forwardRef(() => PostService))
|
||||
private readonly postService: PostService,
|
||||
) {}
|
||||
|
||||
findCategoryById(categoryId: string) {
|
||||
@@ -17,4 +20,22 @@ export class CategoryService {
|
||||
get model() {
|
||||
return this.categoryModel
|
||||
}
|
||||
|
||||
async getPostTagsSum() {
|
||||
const data = await this.postService.model.aggregate([
|
||||
{ $project: { tags: 1 } },
|
||||
{
|
||||
$unwind: '$tags',
|
||||
},
|
||||
{ $group: { _id: '$tags', count: { $sum: 1 } } },
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
name: '$_id',
|
||||
count: 1,
|
||||
},
|
||||
},
|
||||
])
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
import { Injectable, Logger } from '@nestjs/common'
|
||||
import chalk from 'chalk'
|
||||
import { mkdirSync } from 'fs'
|
||||
import { DATA_DIR, LOGGER_DIR, TEMP_DIR } from 'src/constants/path.constant'
|
||||
import { DATA_DIR, LOGGER_DIR, TEMP_DIR } from '~/constants/path.constant'
|
||||
|
||||
@Injectable()
|
||||
export class InitService {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { forwardRef, Module } from '@nestjs/common'
|
||||
import { CategoryModule } from '../category/category.module'
|
||||
import { PostController } from './post.controller'
|
||||
import { PostService } from './post.service'
|
||||
|
||||
@Module({
|
||||
imports: [CategoryModule],
|
||||
imports: [forwardRef(() => CategoryModule)],
|
||||
controllers: [PostController],
|
||||
providers: [PostService],
|
||||
exports: [PostService],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing'
|
||||
import { getFakeCategoryModel } from 'test/db-model.mock'
|
||||
import { CategoryService } from '../category/category.service'
|
||||
import { getFakeCategoryModel } from '~/../test/db-model.mock'
|
||||
import { DbModule } from '../../processors/database/database.module'
|
||||
import { CategoryService } from '../category/category.service'
|
||||
import { PostService } from './post.service'
|
||||
|
||||
describe('PostService', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing'
|
||||
import { INestApplication } from '@nestjs/common'
|
||||
import * as request from 'supertest'
|
||||
import { Test, TestingModule } from '@nestjs/testing'
|
||||
import request from 'supertest'
|
||||
import { AppModule } from './../src/app.module'
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
|
||||
Reference in New Issue
Block a user