Skip to content

Commit 58f9312

Browse files
committed
feat: add reactpress toolkit
1 parent 6782216 commit 58f9312

39 files changed

+2769
-13
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"precommit": "lint-staged",
3838
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
3939
"publish": "node scripts/publish-packages.js",
40-
"release": "node scripts/build-packages.js --publish"
40+
"release": "node scripts/build-packages.js --publish",
41+
"generate:api-types": "node scripts/generate-api-types.js"
4142
},
4243
"dependencies": {
4344
"chalk": "^4.1.2",

pnpm-lock.yaml

Lines changed: 323 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ packages:
77
- 'server'
88
# 文档
99
- 'docs'
10+
# 工具包
11+
- 'toolkit'

server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
"pm2:restart": "pm2 restart reactpress-server",
5858
"pm2:delete": "pm2 delete reactpress-server",
5959
"pm2:logs": "pm2 logs reactpress-server",
60-
"pm2:status": "pm2 status reactpress-server"
60+
"pm2:status": "pm2 status reactpress-server",
61+
"generate:swagger": "ts-node src/generate-swagger.ts"
6162
},
6263
"dependencies": {
6364
"@fecommunity/reactpress-config": "workspace:*",

server/src/generate-swagger.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { NestFactory } from '@nestjs/core';
2+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
3+
import { existsSync, unlinkSync, writeFileSync } from 'fs';
4+
import { join } from 'path';
5+
import { AppModule } from './app.module';
6+
7+
async function generateSwaggerJson() {
8+
let app;
9+
try {
10+
console.log('🚀 Starting Swagger JSON generation...');
11+
12+
// 创建应用实例,但不初始化数据库连接
13+
app = await NestFactory.createApplicationContext(AppModule, {
14+
logger: ['error', 'warn'], // 只记录错误和警告
15+
});
16+
17+
// 获取配置服务
18+
const configService = app.get('ConfigService');
19+
const apiPrefix = configService.get('SERVER_API_PREFIX', '/api');
20+
21+
console.log('📝 Configuring Swagger documentation...');
22+
23+
// 增强版 Swagger 配置
24+
const swaggerConfig = new DocumentBuilder()
25+
.setTitle('ReactPress API Documentation')
26+
.setDescription('Comprehensive API documentation for ReactPress - A modern content management system built with NestJS')
27+
.setVersion('2.0')
28+
.setContact('ReactPress Team', 'https://github.com/fecommunity/reactpress', '[email protected]')
29+
.setLicense('MIT', 'https://github.com/fecommunity/reactpress/blob/main/LICENSE')
30+
.addServer(configService.get('SERVER_SITE_URL', 'http://localhost:3002'), 'API Server')
31+
.build();
32+
33+
// 创建 Swagger 文档
34+
console.log('🔨 Generating Swagger document...');
35+
const document = SwaggerModule.createDocument(app, swaggerConfig);
36+
37+
// 写入文件
38+
const outputPath = join(__dirname, '../public/swagger.json');
39+
40+
// 确保 public 目录存在
41+
const publicDir = join(__dirname, '../public');
42+
if (!existsSync(publicDir)) {
43+
console.log('📁 Creating public directory...');
44+
require('fs').mkdirSync(publicDir, { recursive: true });
45+
}
46+
47+
// 如果文件已经存在,就覆盖掉
48+
if (existsSync(outputPath)) {
49+
console.log('🗑️ Removing existing swagger.json file...');
50+
unlinkSync(outputPath);
51+
}
52+
53+
console.log('💾 Writing Swagger JSON to file...');
54+
writeFileSync(outputPath, JSON.stringify(document, null, 2));
55+
56+
console.log(`✅ Success! Swagger JSON generated at: ${outputPath}`);
57+
58+
// 直接退出进程,避免关闭应用时的数据库连接错误
59+
console.log('🎉 Swagger generation process completed successfully');
60+
process.exit(0);
61+
62+
} catch (error) {
63+
console.error('❌ Error during Swagger generation:', error.message);
64+
65+
// 检查是否已有现有的 swagger.json 文件
66+
const existingPath = join(__dirname, '../public/swagger.json');
67+
if (existsSync(existingPath)) {
68+
console.log('ℹ️ Using existing swagger.json file');
69+
console.log('🎉 Swagger generation process completed successfully');
70+
process.exit(0);
71+
} else {
72+
console.error('❌ No existing swagger.json file found');
73+
process.exit(1);
74+
}
75+
} finally {
76+
// 不尝试关闭应用,直接退出进程
77+
// 这样可以避免数据库连接池错误
78+
}
79+
}
80+
81+
// 运行生成函数
82+
generateSwaggerJson().catch(e => {
83+
console.error('💥 Unhandled error in generateSwaggerJson:', e.message);
84+
85+
// 检查是否已有现有的 swagger.json 文件
86+
const existingPath = join(__dirname, '../public/swagger.json');
87+
if (existsSync(existingPath)) {
88+
console.log('ℹ️ Using existing swagger.json file');
89+
console.log('🎉 Swagger generation process completed successfully');
90+
process.exit(0);
91+
} else {
92+
console.error('❌ No existing swagger.json file found');
93+
process.exit(1);
94+
}
95+
});

server/src/starter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export async function bootstrap() {
7676

7777
await app.listen(configuredPort);
7878
console.log(`[ReactPress] Application started on http://localhost:${configuredPort}`);
79-
console.log(`[ReactPress] API Documentation available at http://localhost:${configuredPort}/api/docs`);
79+
console.log(`[ReactPress] API Documentation available at http://localhost:${configuredPort}/api`);
8080

8181
return app;
8282

toolkit/demos/simple.ts

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// src/demo.ts
2+
import { api, types, utils } from '../src';
3+
4+
// 使用类型定义
5+
const exampleUser: types.IUser = {
6+
name: '张三',
7+
password: 'hashed_password', // 实际应用中应该是加密后的密码
8+
avatar: 'https://example.com/avatar.jpg',
9+
10+
role: 'user',
11+
status: 'active',
12+
type: 'local',
13+
createAt: new Date().toISOString(),
14+
updateAt: new Date().toISOString()
15+
};
16+
17+
const exampleArticle: types.IArticle = {
18+
id: '1',
19+
title: '示例文章',
20+
cover: 'https://example.com/cover.jpg',
21+
summary: '这是一篇示例文章的摘要',
22+
content: '这是文章的完整内容...',
23+
html: '<p>这是文章的HTML内容...</p>',
24+
toc: '[]',
25+
category: {
26+
id: '1',
27+
label: '技术',
28+
value: 'tech',
29+
articles: [],
30+
createAt: new Date().toISOString(),
31+
updateAt: new Date().toISOString()
32+
},
33+
tags: ['typescript', 'api'],
34+
status: 'published',
35+
views: 0,
36+
likes: 0,
37+
isRecommended: false,
38+
password: '',
39+
needPassword: false,
40+
isCommentable: true,
41+
publishAt: new Date().toISOString(),
42+
createAt: new Date().toISOString(),
43+
updateAt: new Date().toISOString()
44+
};
45+
46+
// API 调用示例
47+
async function demoAPIUsage() {
48+
try {
49+
console.log('🚀 开始 API 演示...');
50+
51+
// 1. 获取所有用户
52+
console.log('📋 获取用户列表...');
53+
const users = await api.UserController_findAll();
54+
console.log('✅ 用户列表:', users);
55+
56+
// 2. 注册新用户
57+
console.log('📋 注册新用户...');
58+
const newUser = await api.UserController_register(exampleUser);
59+
console.log('✅ 新用户:', newUser);
60+
61+
// 3. 获取所有文章
62+
console.log('📋 获取文章列表...');
63+
const articles = await api.ArticleController_findAll();
64+
console.log('✅ 文章列表:', articles);
65+
66+
// 4. 创建新文章
67+
console.log('📋 创建新文章...');
68+
const createdArticle = await api.ArticleController_create(exampleArticle);
69+
console.log('✅ 新文章:', createdArticle);
70+
71+
// 5. 获取特定文章
72+
console.log('📋 获取特定文章...');
73+
const article = await api.ArticleController_findById({ id: '1' });
74+
console.log('✅ 文章详情:', article);
75+
76+
// 6. 更新文章浏览量
77+
console.log('📋 更新文章浏览量...');
78+
const updatedArticle = await api.ArticleController_updateViewsById({ id: '1' });
79+
console.log('✅ 更新后的文章:', updatedArticle);
80+
81+
} catch (error) {
82+
console.error('❌ API 调用失败:', error);
83+
84+
// 使用工具函数处理错误
85+
if (error instanceof utils.ApiError) {
86+
console.error(`错误代码: ${error.code}, 消息: ${error.message}`);
87+
}
88+
}
89+
}
90+
91+
// 使用工具函数示例
92+
function demoUtilsUsage() {
93+
console.log('🛠️ 工具函数演示...');
94+
95+
// 1. 日期格式化
96+
const formattedDate = utils.formatDate(new Date(), 'YYYY年MM月DD日');
97+
console.log('✅ 格式化日期:', formattedDate);
98+
99+
// 2. 深度克隆
100+
const original = { name: '张三', profile: { age: 30 } };
101+
const cloned = utils.deepClone(original);
102+
console.log('✅ 深度克隆:', cloned);
103+
104+
// 3. 防抖函数
105+
const debouncedSearch = utils.debounce((query: string) => {
106+
console.log('🔍 搜索:', query);
107+
}, 300);
108+
109+
// 模拟搜索输入
110+
debouncedSearch('typescript');
111+
debouncedSearch('typescript api');
112+
113+
// 4. 节流函数
114+
const throttledScroll = utils.throttle((position: number) => {
115+
console.log('📜 滚动位置:', position);
116+
}, 1000);
117+
118+
// 模拟滚动事件
119+
throttledScroll(100);
120+
throttledScroll(200);
121+
throttledScroll(300);
122+
}
123+
124+
// 高级用法示例
125+
async function demoAdvancedUsage() {
126+
console.log('🎯 高级用法演示...');
127+
128+
try {
129+
// 1. 使用 HTTP 客户端直接调用 API
130+
console.log('📋 使用 HTTP 客户端直接调用...');
131+
const httpClient = await import('@fecommunity/toolkit/utils').then(m => m.httpClient);
132+
133+
const response = await httpClient.get('/api/user');
134+
console.log('✅ 直接HTTP调用结果:', response);
135+
136+
// 2. 批量操作
137+
console.log('📋 批量获取数据...');
138+
const [users, articles, settings] = await Promise.all([
139+
api.UserController_findAll(),
140+
api.ArticleController_findAll(),
141+
api.SettingController_findAll()
142+
]);
143+
144+
console.log('✅ 批量获取完成:');
145+
console.log(' 用户数量:', users.length);
146+
console.log(' 文章数量:', articles.length);
147+
148+
// 3. 错误处理策略
149+
console.log('📋 错误处理演示...');
150+
try {
151+
// 模拟一个可能失败的请求
152+
await api.ArticleController_findById({ id: 'non-existent-id' });
153+
} catch (error) {
154+
if (utils.isApiError(error)) {
155+
console.log('✅ 错误已正确处理:', error.message);
156+
} else {
157+
console.log('✅ 其他类型错误:', error.message);
158+
}
159+
}
160+
161+
} catch (error) {
162+
console.error('❌ 高级用法演示失败:', error);
163+
}
164+
}
165+
166+
// 完整演示
167+
async function runDemo() {
168+
console.log('🎬 开始 Toolkit 演示\n');
169+
170+
// 演示工具函数
171+
demoUtilsUsage();
172+
173+
// 演示 API 调用
174+
await demoAPIUsage();
175+
176+
// 演示高级用法
177+
await demoAdvancedUsage();
178+
179+
console.log('\n🎉 Toolkit 演示完成!');
180+
}
181+
182+
// 运行演示
183+
runDemo().catch(console.error);

toolkit/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@fecommunity/reactpress-tookit",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {
6+
"generate": "node scripts/generate-swagger.js"
7+
},
8+
"keywords": [],
9+
"author": "",
10+
"license": "ISC",
11+
"description": "",
12+
"dependencies": {
13+
"lodash": "^4.17.21",
14+
"swagger-typescript-api": "^12.0.4",
15+
"axios": "^1.12.2"
16+
}
17+
}

0 commit comments

Comments
 (0)