Skip to content

Commit ef9fdc1

Browse files
committed
feat: add swagger v2 ui
1 parent c5f9dd4 commit ef9fdc1

File tree

5 files changed

+58
-58
lines changed

5 files changed

+58
-58
lines changed

pnpm-lock.yaml

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"@nestjs/jwt": "^6.1.1",
7272
"@nestjs/passport": "^6.1.1",
7373
"@nestjs/platform-express": "^6.11.5",
74-
"@nestjs/swagger": "^4.7.15",
74+
"@nestjs/swagger": "^4.8.2",
7575
"@nestjs/typeorm": "^6.2.0",
7676
"@types/express-serve-static-core": "^4.19.5",
7777
"ali-oss": "^6.5.1",
@@ -106,7 +106,8 @@
106106
"open": "^8.2.1",
107107
"express": "^4.18.2",
108108
"@nestjs/cli": "^6.9.0",
109-
"pm2": "^5.2.0"
109+
"pm2": "^5.2.0",
110+
"swagger-themes": "^1.4.3"
110111
},
111112
"devDependencies": {
112113
"@nestjs/schematics": "^6.7.0",

server/public/favicon.png

32.3 KB
Loading

server/public/swagger/custom.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.swagger-ui div.topbar {
2+
display: none !important;
3+
padding: 0 !important;
4+
}
5+
6+
7+
.swagger-ui .scheme-container {
8+
display: none !important;
9+
margin: 0 !important;
10+
padding: 0 !important;
11+
}

server/src/starter.ts

Lines changed: 33 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,12 @@ import * as express from 'express';
66
import * as rateLimit from 'express-rate-limit';
77
import * as helmet from 'helmet';
88
import { join } from 'path';
9+
import { SwaggerTheme, SwaggerThemeNameEnum } from 'swagger-themes';
910

1011
import { AppModule } from './app.module';
1112
import { HttpExceptionFilter } from './filters/http-exception.filter';
1213
import { TransformInterceptor } from './interceptors/transform.interceptor';
1314

14-
// 端口检测函数
15-
const checkPortAvailability = (port: number): Promise<boolean> => {
16-
return new Promise((resolve) => {
17-
const net = require('net');
18-
const tester = net.createServer()
19-
.once('error', () => resolve(false))
20-
.once('listening', () => tester.once('close', () => resolve(true)).close())
21-
.listen(port);
22-
});
23-
};
24-
25-
// 查找可用端口
26-
const findAvailableApplicationPort = async (startPort: number, maxAttempts = 10): Promise<number> => {
27-
for (let i = 0; i < maxAttempts; i++) {
28-
const portToTry = startPort + i;
29-
if (await checkPortAvailability(portToTry)) {
30-
return portToTry;
31-
}
32-
}
33-
throw new Error(`No available ports found in range ${startPort}-${startPort + maxAttempts - 1}`);
34-
};
35-
36-
// 等待端口可用
37-
const waitForPort = async (port: number, timeout = 30000): Promise<boolean> => {
38-
const startTime = Date.now();
39-
40-
while (Date.now() - startTime < timeout) {
41-
if (await checkPortAvailability(port)) {
42-
return true;
43-
}
44-
await new Promise(resolve => setTimeout(resolve, 500));
45-
}
46-
47-
return false;
48-
};
49-
5015
export async function bootstrap() {
5116
try {
5217
const app = await NestFactory.create(AppModule);
@@ -72,38 +37,52 @@ export async function bootstrap() {
7237
app.use(bodyParser.json({ limit: '10mb' }));
7338
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }));
7439

40+
// 增强版 Swagger 配置
7541
const swaggerConfig = new DocumentBuilder()
76-
.setTitle('ReactPress Open Api')
77-
.setDescription('ReactPress Open Api Document')
78-
.setVersion('1.0')
42+
.setTitle('ReactPress API Documentation')
43+
.setDescription('Comprehensive API documentation for ReactPress - A modern content management system built with NestJS')
44+
.setVersion('2.0')
45+
.setContact('ReactPress Team', 'https://github.com/fecommunity/reactpress', '[email protected]')
46+
.setLicense('MIT', 'https://github.com/fecommunity/reactpress/blob/main/LICENSE')
47+
.addServer(configService.get('SERVER_SITE_URL', 'http://localhost:3002'), 'API Server')
7948
.build();
49+
8050
const document = SwaggerModule.createDocument(app, swaggerConfig);
81-
SwaggerModule.setup('api', app, document);
51+
52+
// 使用 swagger-themes 提供专业主题
53+
const theme = new SwaggerTheme();
54+
55+
// 自定义 Swagger 设置
56+
const options = {
57+
customCss: theme.getBuffer(SwaggerThemeNameEnum.MATERIAL), // 应用主题
58+
customSiteTitle: 'ReactPress API Documentation',
59+
customfavIcon: '/public/favicon.png',
60+
swaggerOptions: {
61+
docExpansion: 'list',
62+
filter: true,
63+
showRequestDuration: true,
64+
persistAuthorization: true, // 保持授权数据
65+
displayOperationId: true,
66+
operationsSorter: 'method', // 按方法排序
67+
tagsSorter: 'alpha', // 按字母顺序排序标签
68+
},
69+
customCssUrl: '/public/swagger/custom.css', // 额外的自定义CSS
70+
};
71+
72+
// 设置 Swagger UI
73+
SwaggerModule.setup('api', app, document, options);
8274

8375
const configuredPort = configService.get('SERVER_PORT', 3002);
8476

85-
// 确保端口可用
86-
const isPortAvailable = await checkPortAvailability(configuredPort);
87-
if (!isPortAvailable) {
88-
console.warn(`[ReactPress] Port ${configuredPort} is not available, waiting for it to be released...`);
89-
const portReleased = await waitForPort(configuredPort);
90-
91-
if (!portReleased) {
92-
console.error(`[ReactPress] Port ${configuredPort} is still occupied after waiting`);
93-
process.exit(1);
94-
}
95-
}
96-
9777
await app.listen(configuredPort);
9878
console.log(`[ReactPress] Application started on http://localhost:${configuredPort}`);
99-
console.log(`[ReactPress] Please visit http://localhost:${configuredPort}/api manually`);
79+
console.log(`[ReactPress] API Documentation available at http://localhost:${configuredPort}/api/docs`);
10080

10181
return app;
10282

10383
} catch (error) {
10484
console.error('[ReactPress] Failed to start application:', error);
10585

106-
// 提供友好的错误信息
10786
if (error.code === 'EADDRINUSE') {
10887
console.error('[ReactPress] Port is already in use. Please check for other running instances.');
10988
console.error('[ReactPress] You can change the port in your .env file or terminate the conflicting process.');
@@ -113,7 +92,6 @@ export async function bootstrap() {
11392
}
11493
}
11594

116-
// 添加进程信号处理
11795
process.on('SIGINT', () => {
11896
console.log('\n[ReactPress] Application shutting down gracefully...');
11997
process.exit(0);

0 commit comments

Comments
 (0)