@fecommunity/reactpress-server 1.0.0-beta.3 → 1.0.0-beta.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -342
- package/bin/reactpress-server.js +162 -274
- package/dist/main.d.ts +5 -0
- package/dist/main.js +296 -39
- package/dist/main.js.map +1 -1
- package/dist/modules/setting/setting.service.js +35 -11
- package/dist/modules/setting/setting.service.js.map +1 -1
- package/dist/public/index.html +439 -0
- package/dist/starter.d.ts +1 -0
- package/dist/starter.js +101 -0
- package/dist/starter.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +559 -52
- package/package.json +31 -24
- package/public/index.html +439 -0
- package/dist/bootstrap.d.ts +0 -10
- package/dist/bootstrap.js +0 -57
- package/dist/bootstrap.js.map +0 -1
package/bin/reactpress-server.js
CHANGED
|
@@ -1,302 +1,190 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* ReactPress Server CLI Entry Point
|
|
5
|
+
* This script allows starting the ReactPress server via npx
|
|
6
|
+
* Supports both regular and PM2 startup modes
|
|
7
|
+
*/
|
|
8
|
+
|
|
5
9
|
const path = require('path');
|
|
6
10
|
const fs = require('fs');
|
|
7
|
-
const
|
|
8
|
-
const inquirer = require('inquirer');
|
|
11
|
+
const { spawn, spawnSync } = require('child_process');
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
// Get command line arguments
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const usePM2 = args.includes('--pm2');
|
|
16
|
+
const showHelp = args.includes('--help') || args.includes('-h');
|
|
14
17
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
.option('-d, --database <type>', 'Database type (mysql|postgres)', 'mysql')
|
|
20
|
-
.option('-p, --port <port>', 'Server port', '3002')
|
|
21
|
-
.option('--skip-db-setup', 'Skip database setup')
|
|
22
|
-
.action(async (options) => {
|
|
23
|
-
console.log(chalk.blue('🚀 Initializing ReactPress Server...\n'));
|
|
24
|
-
|
|
25
|
-
const cwd = process.cwd();
|
|
26
|
-
const envPath = path.join(cwd, '.env');
|
|
27
|
-
|
|
28
|
-
// 检查.env文件是否已存在
|
|
29
|
-
if (fs.existsSync(envPath)) {
|
|
30
|
-
const { overwrite } = await inquirer.prompt([
|
|
31
|
-
{
|
|
32
|
-
type: 'confirm',
|
|
33
|
-
name: 'overwrite',
|
|
34
|
-
message: 'Configuration file (.env) already exists. Overwrite?',
|
|
35
|
-
default: false
|
|
36
|
-
}
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
if (!overwrite) {
|
|
40
|
-
console.log(chalk.yellow('⚠️ Initialization cancelled.'));
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// 收集配置信息
|
|
46
|
-
console.log(chalk.cyan('📋 Please provide the following configuration:\n'));
|
|
47
|
-
|
|
48
|
-
const config = await inquirer.prompt([
|
|
49
|
-
{
|
|
50
|
-
type: 'input',
|
|
51
|
-
name: 'DB_HOST',
|
|
52
|
-
message: 'Database host:',
|
|
53
|
-
default: 'localhost'
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
type: 'input',
|
|
57
|
-
name: 'DB_PORT',
|
|
58
|
-
message: 'Database port:',
|
|
59
|
-
default: options.database === 'postgres' ? '5432' : '3306'
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
type: 'input',
|
|
63
|
-
name: 'DB_USERNAME',
|
|
64
|
-
message: 'Database username:',
|
|
65
|
-
default: 'root'
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
type: 'password',
|
|
69
|
-
name: 'DB_PASSWORD',
|
|
70
|
-
message: 'Database password:',
|
|
71
|
-
mask: '*'
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
type: 'input',
|
|
75
|
-
name: 'DB_DATABASE',
|
|
76
|
-
message: 'Database name:',
|
|
77
|
-
default: 'reactpress'
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
type: 'input',
|
|
81
|
-
name: 'SERVER_PORT',
|
|
82
|
-
message: 'Server port:',
|
|
83
|
-
default: options.port
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
type: 'input',
|
|
87
|
-
name: 'JWT_SECRET',
|
|
88
|
-
message: 'JWT secret (leave empty to generate):',
|
|
89
|
-
default: ''
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: 'input',
|
|
93
|
-
name: 'ADMIN_USER',
|
|
94
|
-
message: 'Admin username:',
|
|
95
|
-
default: 'admin'
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
type: 'password',
|
|
99
|
-
name: 'ADMIN_PASSWD',
|
|
100
|
-
message: 'Admin password:',
|
|
101
|
-
default: 'admin',
|
|
102
|
-
mask: '*'
|
|
103
|
-
}
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
// 如果没有提供JWT密钥则生成一个
|
|
107
|
-
if (!config.JWT_SECRET) {
|
|
108
|
-
config.JWT_SECRET = require('crypto').randomBytes(32).toString('hex');
|
|
109
|
-
console.log(chalk.green('🔐 Generated JWT secret'));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 创建.env文件
|
|
113
|
-
const envContent = `# ReactPress Server Configuration
|
|
114
|
-
# Generated on ${new Date().toISOString()}
|
|
18
|
+
// Show help if requested
|
|
19
|
+
if (showHelp) {
|
|
20
|
+
console.log(`
|
|
21
|
+
ReactPress Server - NestJS-based backend API for ReactPress CMS
|
|
115
22
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
DB_HOST=${config.DB_HOST}
|
|
119
|
-
DB_PORT=${config.DB_PORT}
|
|
120
|
-
DB_USERNAME=${config.DB_USERNAME}
|
|
121
|
-
DB_PASSWORD=${config.DB_PASSWORD}
|
|
122
|
-
DB_DATABASE=${config.DB_DATABASE}
|
|
23
|
+
Usage:
|
|
24
|
+
npx @fecommunity/reactpress-server [options]
|
|
123
25
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
26
|
+
Options:
|
|
27
|
+
--pm2 Start server with PM2 process manager
|
|
28
|
+
--help, -h Show this help message
|
|
127
29
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
30
|
+
Examples:
|
|
31
|
+
npx @fecommunity/reactpress-server # Start server normally
|
|
32
|
+
npx @fecommunity/reactpress-server --pm2 # Start server with PM2
|
|
33
|
+
npx @fecommunity/reactpress-server --help # Show this help message
|
|
34
|
+
`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
132
37
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
console.log(chalk.green('✅ Configuration saved to .env file'));
|
|
139
|
-
|
|
140
|
-
if (!options.skipDbSetup) {
|
|
141
|
-
console.log(chalk.yellow('\n🗄️ Setting up database...'));
|
|
142
|
-
console.log(chalk.gray(`Please ensure your ${options.database} server is running and create the database:`));
|
|
143
|
-
console.log(chalk.cyan(`CREATE DATABASE ${config.DB_DATABASE};`));
|
|
144
|
-
console.log(chalk.gray('Then run: npx @reactpress/server start'));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
console.log(chalk.green('\n🎉 ReactPress Server initialized successfully!'));
|
|
148
|
-
console.log(chalk.cyan('\nNext steps:'));
|
|
149
|
-
console.log(chalk.gray('1. Start the server: npx @fecommunity/reactpress-server start'));
|
|
150
|
-
console.log(chalk.gray('2. Visit API: http://localhost:' + config.SERVER_PORT + '/api'));
|
|
151
|
-
console.log(chalk.gray('3. API docs: http://localhost:' + config.SERVER_PORT + '/api (Swagger UI)'));
|
|
152
|
-
});
|
|
38
|
+
// Get the directory where this script is located
|
|
39
|
+
const binDir = __dirname;
|
|
40
|
+
const serverDir = path.join(binDir, '..');
|
|
41
|
+
const distPath = path.join(serverDir, 'dist', 'main.js');
|
|
42
|
+
const ecosystemPath = path.join(serverDir, 'ecosystem.config.js');
|
|
153
43
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
.action(async (options) => {
|
|
162
|
-
console.log(chalk.blue('🚀 Starting ReactPress Server...\n'));
|
|
163
|
-
|
|
164
|
-
const cwd = process.cwd();
|
|
165
|
-
const envPath = path.join(cwd, '.env');
|
|
166
|
-
|
|
167
|
-
// 检查.env文件是否存在
|
|
168
|
-
if (!fs.existsSync(envPath)) {
|
|
169
|
-
console.log(chalk.red('❌ Configuration file (.env) not found!'));
|
|
170
|
-
console.log(chalk.yellow('Please run: npx @fecommunity/reactpress-server init'));
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// 检查是否在项目根目录(有server目录)
|
|
175
|
-
const serverPath = path.join(cwd, 'server');
|
|
176
|
-
const hasServerDir = fs.existsSync(serverPath);
|
|
177
|
-
|
|
178
|
-
let serverMainPath;
|
|
179
|
-
if (hasServerDir) {
|
|
180
|
-
// 在项目根目录,使用相对路径
|
|
181
|
-
serverMainPath = path.join(serverPath, 'dist', 'main.js');
|
|
182
|
-
const packageDir = serverPath;
|
|
183
|
-
|
|
184
|
-
// 检查构建文件是否存在
|
|
185
|
-
if (!fs.existsSync(serverMainPath)) {
|
|
186
|
-
console.log(chalk.yellow('⚠️ Built server not found, building...'));
|
|
187
|
-
try {
|
|
188
|
-
execSync('pnpm run build:server', { cwd: cwd, stdio: 'inherit' });
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.log(chalk.red('❌ Build failed'));
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
// 可能在独立安装的@reactpress/server包中
|
|
196
|
-
try {
|
|
197
|
-
const serverPackagePath = require.resolve('@fecommunity/reactpress-server/package.json');
|
|
198
|
-
const serverPackageDir = path.dirname(serverPackagePath);
|
|
199
|
-
serverMainPath = path.join(serverPackageDir, 'dist', 'main.js');
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.log(chalk.red('❌ ReactPress server package not found!'));
|
|
202
|
-
console.log(chalk.yellow('Please install: npm install @fecommunity/reactpress-server'));
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// 设置环境变量
|
|
208
|
-
const env = {
|
|
209
|
-
...process.env,
|
|
210
|
-
NODE_ENV: options.dev ? 'development' : 'production'
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
if (options.port) {
|
|
214
|
-
env.SERVER_PORT = options.port;
|
|
215
|
-
}
|
|
216
|
-
|
|
44
|
+
// Function to check if PM2 is installed
|
|
45
|
+
function isPM2Installed() {
|
|
46
|
+
try {
|
|
47
|
+
require.resolve('pm2');
|
|
48
|
+
return true;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// Check if PM2 is installed globally
|
|
217
51
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
cwd: cwd,
|
|
223
|
-
stdio: 'inherit',
|
|
224
|
-
env
|
|
225
|
-
});
|
|
226
|
-
} else {
|
|
227
|
-
console.log(chalk.yellow('⚠️ Development mode requires server source code'));
|
|
228
|
-
console.log(chalk.cyan('Starting in production mode instead...'));
|
|
229
|
-
execSync(`node ${serverMainPath}`, {
|
|
230
|
-
cwd: process.cwd(),
|
|
231
|
-
stdio: 'inherit',
|
|
232
|
-
env
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
console.log(chalk.green('🌟 Starting in production mode...'));
|
|
237
|
-
execSync(`node ${serverMainPath}`, {
|
|
238
|
-
cwd: process.cwd(),
|
|
239
|
-
stdio: 'inherit',
|
|
240
|
-
env
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
} catch (error) {
|
|
244
|
-
console.log(chalk.red('❌ Failed to start server'));
|
|
245
|
-
process.exit(1);
|
|
52
|
+
spawnSync('pm2', ['--version'], { stdio: 'ignore' });
|
|
53
|
+
return true;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return false;
|
|
246
56
|
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Function to install PM2
|
|
61
|
+
function installPM2() {
|
|
62
|
+
console.log('[ReactPress Server] Installing PM2...');
|
|
63
|
+
const installResult = spawnSync('npm', ['install', 'pm2', '--no-save'], {
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
cwd: serverDir
|
|
247
66
|
});
|
|
67
|
+
|
|
68
|
+
if (installResult.status !== 0) {
|
|
69
|
+
console.error('[ReactPress Server] Failed to install PM2');
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
248
75
|
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const cwd = process.cwd();
|
|
257
|
-
const serverPath = path.join(cwd, 'server');
|
|
258
|
-
|
|
259
|
-
if (!fs.existsSync(serverPath)) {
|
|
260
|
-
console.log(chalk.red('❌ Server directory not found!'));
|
|
261
|
-
console.log(chalk.yellow('This command should be run from the project root directory.'));
|
|
76
|
+
// Function to start with PM2
|
|
77
|
+
function startWithPM2() {
|
|
78
|
+
// Check if PM2 is installed
|
|
79
|
+
if (!isPM2Installed()) {
|
|
80
|
+
// Try to install PM2
|
|
81
|
+
if (!installPM2()) {
|
|
82
|
+
console.error('[ReactPress Server] Cannot start with PM2');
|
|
262
83
|
process.exit(1);
|
|
263
84
|
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check if the server is built
|
|
88
|
+
if (!fs.existsSync(distPath)) {
|
|
89
|
+
console.log('[ReactPress Server] Server not built yet. Building...');
|
|
90
|
+
|
|
91
|
+
// Try to build the server
|
|
92
|
+
const buildResult = spawnSync('npm', ['run', 'build'], {
|
|
93
|
+
stdio: 'inherit',
|
|
94
|
+
cwd: serverDir
|
|
95
|
+
});
|
|
264
96
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
cwd: cwd,
|
|
268
|
-
stdio: 'inherit'
|
|
269
|
-
});
|
|
270
|
-
console.log(chalk.green('✅ Build completed successfully!'));
|
|
271
|
-
} catch (error) {
|
|
272
|
-
console.log(chalk.red('❌ Build failed'));
|
|
97
|
+
if (buildResult.status !== 0) {
|
|
98
|
+
console.error('[ReactPress Server] Failed to build server');
|
|
273
99
|
process.exit(1);
|
|
274
100
|
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
.command
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const args = ['start', '--dev'];
|
|
287
|
-
if (options.port) {
|
|
288
|
-
args.push('--port', options.port);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log('[ReactPress Server] Starting with PM2...');
|
|
104
|
+
|
|
105
|
+
// Use ecosystem.config.js if it exists, otherwise use direct command
|
|
106
|
+
let pm2Command = 'pm2';
|
|
107
|
+
try {
|
|
108
|
+
// Try to resolve PM2 path
|
|
109
|
+
pm2Command = path.join(serverDir, 'node_modules', '.bin', 'pm2');
|
|
110
|
+
if (!fs.existsSync(pm2Command)) {
|
|
111
|
+
pm2Command = 'pm2';
|
|
289
112
|
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
pm2Command = 'pm2';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if ecosystem.config.js exists
|
|
118
|
+
if (fs.existsSync(ecosystemPath)) {
|
|
119
|
+
const pm2 = spawn(pm2Command, ['start', ecosystemPath], {
|
|
120
|
+
stdio: 'inherit',
|
|
121
|
+
cwd: serverDir
|
|
122
|
+
});
|
|
290
123
|
|
|
291
|
-
|
|
292
|
-
|
|
124
|
+
pm2.on('close', (code) => {
|
|
125
|
+
console.log(`[ReactPress Server] PM2 process exited with code ${code}`);
|
|
126
|
+
process.exit(code);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
pm2.on('error', (error) => {
|
|
130
|
+
console.error('[ReactPress Server] Failed to start with PM2:', error);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
// Fallback to direct start
|
|
135
|
+
const pm2 = spawn(pm2Command, ['start', distPath, '--name', 'reactpress-server'], {
|
|
293
136
|
stdio: 'inherit',
|
|
294
|
-
|
|
137
|
+
cwd: serverDir
|
|
295
138
|
});
|
|
296
139
|
|
|
297
|
-
|
|
140
|
+
pm2.on('close', (code) => {
|
|
141
|
+
console.log(`[ReactPress Server] PM2 process exited with code ${code}`);
|
|
298
142
|
process.exit(code);
|
|
299
143
|
});
|
|
300
|
-
|
|
144
|
+
|
|
145
|
+
pm2.on('error', (error) => {
|
|
146
|
+
console.error('[ReactPress Server] Failed to start with PM2:', error);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Function to start with regular Node.js
|
|
153
|
+
function startWithNode() {
|
|
154
|
+
// Check if the server is built
|
|
155
|
+
if (!fs.existsSync(distPath)) {
|
|
156
|
+
console.log('[ReactPress Server] Server not built yet. Building...');
|
|
157
|
+
|
|
158
|
+
// Try to build the server
|
|
159
|
+
const buildResult = spawnSync('npm', ['run', 'build'], {
|
|
160
|
+
stdio: 'inherit',
|
|
161
|
+
cwd: serverDir
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (buildResult.status !== 0) {
|
|
165
|
+
console.error('[ReactPress Server] Failed to build server');
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Change to the server directory
|
|
171
|
+
process.chdir(serverDir);
|
|
172
|
+
|
|
173
|
+
// Set environment variables
|
|
174
|
+
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|
175
|
+
|
|
176
|
+
// Import and run the server
|
|
177
|
+
try {
|
|
178
|
+
require(distPath);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('[ReactPress Server] Failed to start server:', error);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
301
184
|
|
|
302
|
-
|
|
185
|
+
// Main execution
|
|
186
|
+
if (usePM2) {
|
|
187
|
+
startWithPM2();
|
|
188
|
+
} else {
|
|
189
|
+
startWithNode();
|
|
190
|
+
}
|