add swagger

This commit is contained in:
liuyi 2025-06-14 23:32:15 +08:00
parent 5a5306b10d
commit 73e5a897c6
6 changed files with 2335 additions and 1616 deletions

View File

@ -5,6 +5,13 @@
"compilerOptions": { "compilerOptions": {
"deleteOutDir": true, "deleteOutDir": true,
"builder": "swc", "builder": "swc",
"typeCheck": true "typeCheck": true,
"plugins": [{
"name": "@nestjs/swagger",
"options":{
"introspectComments": true,
"controllerKeyOfComment": "summary"
}
}]
} }
} }

View File

@ -22,10 +22,10 @@
}, },
"dependencies": { "dependencies": {
"@fastify/static": "^8.2.0", "@fastify/static": "^8.2.0",
"@nestjs/common": "^10.0.3", "@nestjs/common": "^11.1.3",
"@nestjs/core": "^10.0.3", "@nestjs/core": "^11.1.3",
"@nestjs/platform-fastify": "^11.1.3", "@nestjs/platform-fastify": "^11.1.3",
"@nestjs/swagger": "^7.4.2", "@nestjs/swagger": "^11.2.0",
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"chalk": "^5.4.1", "chalk": "^5.4.1",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
@ -35,50 +35,50 @@
"find-up": "^7.0.0", "find-up": "^7.0.0",
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"meilisearch": "^0.50.0", "meilisearch": "^0.51.0",
"mysql2": "^3.14.1", "mysql2": "^3.14.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.2.2",
"rimraf": "^5.0.1", "rimraf": "^6.0.1",
"rxjs": "^7.8.1", "rxjs": "^7.8.2",
"sanitize-html": "^2.17.0", "sanitize-html": "^2.17.0",
"typeorm": "^0.3.24", "typeorm": "^0.3.24",
"validator": "^13.15.0", "validator": "^13.15.15",
"yaml": "^2.8.0" "yaml": "^2.8.0"
}, },
"devDependencies": { "devDependencies": {
"@faker-js/faker": "^9.8.0", "@faker-js/faker": "^9.8.0",
"@nestjs/cli": "^10.0.3", "@nestjs/cli": "^11.0.7",
"@nestjs/schematics": "^10.0.1", "@nestjs/schematics": "^11.0.5",
"@nestjs/testing": "^10.0.3", "@nestjs/testing": "^11.1.3",
"@swc/cli": "^0.1.62", "@swc/cli": "^0.7.7",
"@swc/core": "^1.3.66", "@swc/core": "^1.12.1",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/jest": "29.5.2", "@types/jest": "29.5.14",
"@types/lodash": "^4.17.16", "@types/lodash": "^4.17.17",
"@types/node": "^20.3.1", "@types/node": "^24.0.1",
"@types/sanitize-html": "^2.16.0", "@types/sanitize-html": "^2.16.0",
"@types/supertest": "^2.0.12", "@types/supertest": "^6.0.3",
"@types/validator": "^13.15.1", "@types/validator": "^13.15.1",
"@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/eslint-plugin": "^8.34.0",
"@typescript-eslint/parser": "^5.60.0", "@typescript-eslint/parser": "^8.34.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.43.0", "eslint": "^9.29.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-airbnb-typescript": "^18.0.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^10.1.5",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^27.2.2", "eslint-plugin-jest": "^28.13.5",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^5.4.1",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^4.1.4",
"jest": "29.5.0", "jest": "30.0.0",
"prettier": "^2.8.8", "prettier": "^3.5.3",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"supertest": "^6.3.3", "supertest": "^7.1.1",
"ts-jest": "29.1.0", "ts-jest": "29.4.0",
"ts-loader": "^9.4.3", "ts-loader": "^9.5.2",
"ts-node": "^10.9.1", "ts-node": "^10.9.2",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "~5.1.3" "typescript": "~5.8.3"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [

File diff suppressed because it is too large Load Diff

View File

@ -11,23 +11,33 @@ import {
SerializeOptions, SerializeOptions,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Depends } from '@/modules/restful/decorators/depend.decorator'; import { Depends } from '@/modules/restful/decorators/depend.decorator';
import { ContentModule } from '../content.module'; import { ContentModule } from '../content.module';
import { CreateCategoryDto, QueryCategoryDto, UpdateCategoryDto } from '../dtos/category.dto'; import { CreateCategoryDto, QueryCategoryDto, UpdateCategoryDto } from '../dtos/category.dto';
import { CategoryService } from '../services'; import { CategoryService } from '../services';
@ApiTags('Category Operate')
@Depends(ContentModule) @Depends(ContentModule)
@Controller('category') @Controller('category')
export class CategoryController { export class CategoryController {
constructor(protected service: CategoryService) {} constructor(protected service: CategoryService) {}
/**
* Search category tree
*/
@Get('tree') @Get('tree')
@SerializeOptions({ groups: ['category-tree'] }) @SerializeOptions({ groups: ['category-tree'] })
async tree() { async tree() {
return this.service.findTrees(); return this.service.findTrees();
} }
/**
*
* @param options
*/
@Get() @Get()
@SerializeOptions({ groups: ['category-list'] }) @SerializeOptions({ groups: ['category-list'] })
async list( async list(
@ -37,6 +47,10 @@ export class CategoryController {
return this.service.paginate(options); return this.service.paginate(options);
} }
/**
*
* @param id
*/
@Get(':id') @Get(':id')
@SerializeOptions({ groups: ['category-detail'] }) @SerializeOptions({ groups: ['category-detail'] })
async detail(@Param('id', new ParseUUIDPipe()) id: string) { async detail(@Param('id', new ParseUUIDPipe()) id: string) {

View File

@ -2,7 +2,7 @@ import { INestApplication, Injectable, Type } from '@nestjs/common';
import { RouterModule } from '@nestjs/core'; import { RouterModule } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { omit, trim } from 'lodash'; import { isNil, omit, trim } from 'lodash';
import { BaseRestful } from './base'; import { BaseRestful } from './base';
import { import {
@ -119,7 +119,10 @@ export class Restful extends BaseRestful {
this._docs.default = this.getDocOption(this._default, defaultVersion, true); this._docs.default = this.getDocOption(this._default, defaultVersion, true);
} }
async factoryDocs<T extends INestApplication>(container: T) { async factoryDocs<T extends INestApplication>(
container: T,
metadata?: () => Promise<RecordAny>,
) {
const docs = Object.values(this._docs) const docs = Object.values(this._docs)
.map((doc) => [doc.default, ...Object.values(doc.routes ?? [])]) .map((doc) => [doc.default, ...Object.values(doc.routes ?? [])])
.reduce((o, n) => [...o, ...n], []) .reduce((o, n) => [...o, ...n], [])
@ -146,6 +149,10 @@ export class Restful extends BaseRestful {
} }
builder.setVersion(version); builder.setVersion(version);
if (!isNil(metadata)) {
await SwaggerModule.loadPluginMetadata(metadata);
}
const document = SwaggerModule.createDocument(container, builder.build(), { const document = SwaggerModule.createDocument(container, builder.build(), {
include: include.length > 0 ? include : [() => undefined as any], include: include.length > 0 ? include : [() => undefined as any],
ignoreGlobalPrefix: true, ignoreGlobalPrefix: true,

View File

@ -2,6 +2,9 @@ import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { existsSync } from 'fs-extra';
import { isNil } from 'lodash';
import * as configs from './config'; import * as configs from './config';
import { ContentModule } from './modules/content/content.module'; import { ContentModule } from './modules/content/content.module';
import { CreateOptions } from './modules/core/types'; import { CreateOptions } from './modules/core/types';
@ -9,6 +12,8 @@ import { DatabaseModule } from './modules/database/database.module';
import { MeiliModule } from './modules/meilisearch/meili.module'; import { MeiliModule } from './modules/meilisearch/meili.module';
import { Restful } from './modules/restful/restful'; import { Restful } from './modules/restful/restful';
import { RestfulModule } from './modules/restful/restful.module'; import { RestfulModule } from './modules/restful/restful.module';
import { ApiConfig } from './modules/restful/types';
import { join } from 'path';
export const createOptions: CreateOptions = { export const createOptions: CreateOptions = {
config: { factories: configs as any, storage: { enable: true } }, config: { factories: configs as any, storage: { enable: true } },
@ -28,8 +33,18 @@ export const createOptions: CreateOptions = {
logger: ['error', 'warn'], logger: ['error', 'warn'],
}, },
); );
if (!isNil(await configure.get<ApiConfig>('api', null))) {
const restful = container.get(Restful); const restful = container.get(Restful);
await restful.factoryDocs(container); let metadata: () => Promise<RecordAny>;
if (existsSync(join(__dirname, 'metadata.js'))) {
metadata = (await import(join(__dirname, 'metadata.js'))).default;
}
if (existsSync(join(__dirname, 'metadata.ts'))) {
metadata = (await import(join(__dirname, 'metadata.ts'))).default;
}
await restful.factoryDocs(container, metadata);
}
return container; return container;
}, },
}; };