@fecommunity/reactpress 2.0.0-beta-4-beta.1 → 2.0.0-beta.0
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/{scripts → bin}/reactpress.js +0 -0
- package/package.json +38 -34
- package/scripts/build-packages.js +271 -0
- package/scripts/deploy.sh +25 -0
- package/scripts/{reactpress-publish.js → publish-packages.js} +47 -237
- package/scripts/reactpress-server-old.js +130 -0
- package/scripts/reactpress-server.js +666 -0
- package/scripts/reactpress-cli.js +0 -90
- package/scripts/reactpress-deploy.sh +0 -47
- /package/scripts/{reactpress-dev.js → dev.js} +0 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const mysql = require('mysql2/promise');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const open = require('open');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const PORT = 3003;
|
|
11
|
+
const envPath = path.join(process.cwd(), 'server', '.env');
|
|
12
|
+
|
|
13
|
+
// WordPress-style 5-minute installation
|
|
14
|
+
async function install() {
|
|
15
|
+
console.log(chalk.blue('🚀 ReactPress 5-Minute Installation'));
|
|
16
|
+
console.log(chalk.cyan('🌍 Starting installation wizard...'));
|
|
17
|
+
|
|
18
|
+
const app = express();
|
|
19
|
+
app.use(express.json());
|
|
20
|
+
app.use(express.urlencoded({ extended: true }));
|
|
21
|
+
app.get('/', (req, res) => res.send(getWordPressHTML()));
|
|
22
|
+
|
|
23
|
+
// Enhanced database testing with proper error handling
|
|
24
|
+
app.post('/test-db', async (req, res) => {
|
|
25
|
+
const { host, port, user, password, database } = req.body;
|
|
26
|
+
|
|
27
|
+
// Input validation
|
|
28
|
+
if (!host || !port || !user || !database) {
|
|
29
|
+
return res.json({
|
|
30
|
+
success: false,
|
|
31
|
+
message: 'All database fields are required.'
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
console.log(chalk.cyan(`Testing connection to ${user}@${host}:${port}/${database}...`));
|
|
37
|
+
|
|
38
|
+
// Test basic connection first
|
|
39
|
+
const conn = await mysql.createConnection({
|
|
40
|
+
host: host.trim(),
|
|
41
|
+
port: parseInt(port),
|
|
42
|
+
user: user.trim(),
|
|
43
|
+
password: password,
|
|
44
|
+
connectTimeout: 5000,
|
|
45
|
+
acquireTimeout: 5000,
|
|
46
|
+
timeout: 5000
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Test if we can access the specific database
|
|
50
|
+
try {
|
|
51
|
+
// Use query instead of execute for USE statement
|
|
52
|
+
await conn.query(`USE \`${database.trim()}\``);
|
|
53
|
+
console.log(chalk.green(`Database '${database}' exists and accessible`));
|
|
54
|
+
} catch (dbError) {
|
|
55
|
+
if (dbError.code === 'ER_BAD_DB_ERROR') {
|
|
56
|
+
// Database doesn't exist, try to create it
|
|
57
|
+
await conn.query(`CREATE DATABASE \`${database.trim()}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
|
|
58
|
+
console.log(chalk.green(`Database '${database}' created successfully`));
|
|
59
|
+
} else {
|
|
60
|
+
throw dbError;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await conn.end();
|
|
65
|
+
console.log(chalk.green('✅ Database connection test passed'));
|
|
66
|
+
|
|
67
|
+
res.json({
|
|
68
|
+
success: true,
|
|
69
|
+
message: `Successfully connected to database '${database}' on ${host}:${port}`
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.log(chalk.red('❌ Database connection failed:'), error.message);
|
|
74
|
+
|
|
75
|
+
let errorMessage = 'Database connection failed: ';
|
|
76
|
+
|
|
77
|
+
switch (error.code) {
|
|
78
|
+
case 'ECONNREFUSED':
|
|
79
|
+
errorMessage += `Cannot connect to MySQL server at ${host}:${port}. Please ensure MySQL is running.`;
|
|
80
|
+
break;
|
|
81
|
+
case 'ER_ACCESS_DENIED_ERROR':
|
|
82
|
+
errorMessage += `Access denied for user '${user}'. Please check your username and password.`;
|
|
83
|
+
break;
|
|
84
|
+
case 'ENOTFOUND':
|
|
85
|
+
errorMessage += `Host '${host}' not found. Please check the hostname.`;
|
|
86
|
+
break;
|
|
87
|
+
case 'ETIMEDOUT':
|
|
88
|
+
errorMessage += `Connection timeout to ${host}:${port}. Please check your network and firewall settings.`;
|
|
89
|
+
break;
|
|
90
|
+
case 'ER_BAD_DB_ERROR':
|
|
91
|
+
errorMessage += `Database '${database}' does not exist and cannot be created. Please check your permissions.`;
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
errorMessage += error.message;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
res.json({ success: false, message: errorMessage });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
app.post('/install', async (req, res) => {
|
|
102
|
+
const { db, site } = req.body;
|
|
103
|
+
|
|
104
|
+
// Final validation before installation
|
|
105
|
+
try {
|
|
106
|
+
const testConn = await mysql.createConnection({
|
|
107
|
+
host: db.host.trim(),
|
|
108
|
+
port: parseInt(db.port),
|
|
109
|
+
user: db.user.trim(),
|
|
110
|
+
password: db.password,
|
|
111
|
+
database: db.database.trim()
|
|
112
|
+
});
|
|
113
|
+
await testConn.end();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return res.json({
|
|
116
|
+
success: false,
|
|
117
|
+
message: 'Final database validation failed. Please test your connection again.'
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const env = `# ReactPress Configuration
|
|
122
|
+
# Generated on ${new Date().toISOString()}
|
|
123
|
+
|
|
124
|
+
DB_HOST=${db.host}
|
|
125
|
+
DB_PORT=${db.port}
|
|
126
|
+
DB_USER=${db.user}
|
|
127
|
+
DB_PASSWD=${db.password}
|
|
128
|
+
DB_DATABASE=${db.database}
|
|
129
|
+
CLIENT_SITE_URL=${site.clientUrl}
|
|
130
|
+
SERVER_SITE_URL=${site.serverUrl}
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
fs.writeFileSync(envPath, env);
|
|
134
|
+
console.log(chalk.green('✅ Configuration saved to .env file'));
|
|
135
|
+
|
|
136
|
+
res.json({ success: true, message: 'ReactPress installed successfully!' });
|
|
137
|
+
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
console.log(chalk.green('\n🎉 Installation completed!'));
|
|
140
|
+
console.log(chalk.cyan('Run: npx reactpress-server start'));
|
|
141
|
+
process.exit(0);
|
|
142
|
+
}, 2000);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const server = app.listen(PORT, () => {
|
|
146
|
+
const url = `http://localhost:${PORT}`;
|
|
147
|
+
console.log(chalk.green(`✅ Installation wizard: ${url}`));
|
|
148
|
+
open(url);
|
|
149
|
+
console.log(chalk.gray('Press Ctrl+C to cancel\n'));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
process.on('SIGINT', () => {
|
|
153
|
+
console.log(chalk.yellow('\n⚠️ Installation cancelled'));
|
|
154
|
+
server.close();
|
|
155
|
+
process.exit(0);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function start() {
|
|
160
|
+
if (!fs.existsSync(envPath)) {
|
|
161
|
+
console.log(chalk.red('❌ No configuration found!'));
|
|
162
|
+
console.log(chalk.yellow('Run: npx reactpress-server install'));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
console.log(chalk.blue('🚀 Starting ReactPress Server...'));
|
|
166
|
+
const { spawn } = require('child_process');
|
|
167
|
+
spawn('npm', ['run', 'dev'], { cwd: path.resolve(__dirname, '../server'), stdio: 'inherit' });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getWordPressHTML() {
|
|
171
|
+
return `<!DOCTYPE html>
|
|
172
|
+
<html lang="en">
|
|
173
|
+
<head>
|
|
174
|
+
<meta charset="UTF-8">
|
|
175
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
176
|
+
<title>ReactPress › Installation</title>
|
|
177
|
+
<style>
|
|
178
|
+
* {
|
|
179
|
+
margin: 0;
|
|
180
|
+
padding: 0;
|
|
181
|
+
box-sizing: border-box;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
body {
|
|
185
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
186
|
+
background: #f1f1f1;
|
|
187
|
+
color: #444;
|
|
188
|
+
line-height: 1.4;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.wp-container {
|
|
192
|
+
max-width: 600px;
|
|
193
|
+
margin: 40px auto;
|
|
194
|
+
background: #fff;
|
|
195
|
+
border: 1px solid #ccd0d4;
|
|
196
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.wp-header {
|
|
200
|
+
background: #0073aa;
|
|
201
|
+
color: #fff;
|
|
202
|
+
padding: 24px;
|
|
203
|
+
text-align: center;
|
|
204
|
+
border-bottom: 1px solid #005a87;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.wp-header h1 {
|
|
208
|
+
font-size: 24px;
|
|
209
|
+
font-weight: 400;
|
|
210
|
+
margin-bottom: 8px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.wp-header p {
|
|
214
|
+
font-size: 16px;
|
|
215
|
+
opacity: 0.9;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.wp-content {
|
|
219
|
+
padding: 24px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.wp-step {
|
|
223
|
+
display: none;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.wp-step.active {
|
|
227
|
+
display: block;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.wp-step h2 {
|
|
231
|
+
font-size: 22px;
|
|
232
|
+
font-weight: 600;
|
|
233
|
+
margin-bottom: 20px;
|
|
234
|
+
color: #1d2327;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.wp-description {
|
|
238
|
+
background: #f0f6fc;
|
|
239
|
+
border: 1px solid #c3e4f0;
|
|
240
|
+
border-left: 4px solid #0073aa;
|
|
241
|
+
padding: 12px 16px;
|
|
242
|
+
margin-bottom: 20px;
|
|
243
|
+
font-size: 14px;
|
|
244
|
+
color: #0c4a6e;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.wp-form-table {
|
|
248
|
+
width: 100%;
|
|
249
|
+
margin-bottom: 20px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.wp-form-table th {
|
|
253
|
+
text-align: left;
|
|
254
|
+
padding: 10px 0;
|
|
255
|
+
width: 140px;
|
|
256
|
+
vertical-align: top;
|
|
257
|
+
font-weight: 600;
|
|
258
|
+
color: #1d2327;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.wp-form-table td {
|
|
262
|
+
padding: 10px 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.wp-form-table input[type="text"],
|
|
266
|
+
.wp-form-table input[type="password"],
|
|
267
|
+
.wp-form-table input[type="number"],
|
|
268
|
+
.wp-form-table input[type="url"] {
|
|
269
|
+
width: 100%;
|
|
270
|
+
max-width: 320px;
|
|
271
|
+
padding: 8px 12px;
|
|
272
|
+
border: 1px solid #8c8f94;
|
|
273
|
+
border-radius: 4px;
|
|
274
|
+
font-size: 14px;
|
|
275
|
+
line-height: 1.4;
|
|
276
|
+
transition: border-color 0.15s ease-in-out;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.wp-form-table input:focus {
|
|
280
|
+
border-color: #0073aa;
|
|
281
|
+
outline: 0;
|
|
282
|
+
box-shadow: 0 0 0 1px #0073aa;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.wp-form-table .description {
|
|
286
|
+
font-size: 13px;
|
|
287
|
+
color: #646970;
|
|
288
|
+
margin-top: 4px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.wp-button {
|
|
292
|
+
background: #0073aa;
|
|
293
|
+
color: #fff;
|
|
294
|
+
border: 1px solid #0073aa;
|
|
295
|
+
padding: 8px 16px;
|
|
296
|
+
font-size: 14px;
|
|
297
|
+
font-weight: 600;
|
|
298
|
+
border-radius: 3px;
|
|
299
|
+
cursor: pointer;
|
|
300
|
+
transition: all 0.15s ease-in-out;
|
|
301
|
+
text-decoration: none;
|
|
302
|
+
display: inline-block;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.wp-button:hover {
|
|
306
|
+
background: #005a87;
|
|
307
|
+
border-color: #005a87;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.wp-button:disabled {
|
|
311
|
+
background: #f0f0f1;
|
|
312
|
+
border-color: #dcdcde;
|
|
313
|
+
color: #a7aaad;
|
|
314
|
+
cursor: not-allowed;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.wp-button.button-large {
|
|
318
|
+
padding: 12px 24px;
|
|
319
|
+
font-size: 16px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.wp-notice {
|
|
323
|
+
border: 1px solid #c3c4c7;
|
|
324
|
+
border-left-width: 4px;
|
|
325
|
+
padding: 12px;
|
|
326
|
+
margin: 16px 0;
|
|
327
|
+
background: #fff;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.wp-notice.notice-success {
|
|
331
|
+
border-left-color: #00a32a;
|
|
332
|
+
background: #f7fcf7;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.wp-notice.notice-error {
|
|
336
|
+
border-left-color: #d63638;
|
|
337
|
+
background: #fcf2f2;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.wp-notice p {
|
|
341
|
+
margin: 0;
|
|
342
|
+
font-size: 14px;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.wp-progress {
|
|
346
|
+
background: #f0f0f1;
|
|
347
|
+
height: 4px;
|
|
348
|
+
margin-bottom: 20px;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.wp-progress-bar {
|
|
352
|
+
background: #0073aa;
|
|
353
|
+
height: 100%;
|
|
354
|
+
transition: width 0.3s ease;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.wp-loader {
|
|
358
|
+
display: inline-block;
|
|
359
|
+
width: 16px;
|
|
360
|
+
height: 16px;
|
|
361
|
+
border: 2px solid #f3f3f3;
|
|
362
|
+
border-top: 2px solid #0073aa;
|
|
363
|
+
border-radius: 50%;
|
|
364
|
+
animation: wp-spin 1s linear infinite;
|
|
365
|
+
margin-right: 8px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
@keyframes wp-spin {
|
|
369
|
+
0% { transform: rotate(0deg); }
|
|
370
|
+
100% { transform: rotate(360deg); }
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.wp-footer {
|
|
374
|
+
background: #f6f7f7;
|
|
375
|
+
border-top: 1px solid #c3c4c7;
|
|
376
|
+
padding: 16px 24px;
|
|
377
|
+
text-align: center;
|
|
378
|
+
font-size: 13px;
|
|
379
|
+
color: #646970;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.text-center {
|
|
383
|
+
text-align: center;
|
|
384
|
+
}
|
|
385
|
+
</style>
|
|
386
|
+
</head>
|
|
387
|
+
<body>
|
|
388
|
+
<div class="wp-container">
|
|
389
|
+
<div class="wp-header">
|
|
390
|
+
<h1>ReactPress</h1>
|
|
391
|
+
<p>5-Minute Installation</p>
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
<div class="wp-progress">
|
|
395
|
+
<div class="wp-progress-bar" id="progressBar" style="width: 33%;"></div>
|
|
396
|
+
</div>
|
|
397
|
+
|
|
398
|
+
<div class="wp-content">
|
|
399
|
+
<!-- Step 1: Database Configuration -->
|
|
400
|
+
<div class="wp-step active" id="step1">
|
|
401
|
+
<h2>Database Connection Information</h2>
|
|
402
|
+
|
|
403
|
+
<div class="wp-description">
|
|
404
|
+
You should have this information available from your web hosting provider. If you don't have it, contact them before continuing.
|
|
405
|
+
</div>
|
|
406
|
+
|
|
407
|
+
<table class="wp-form-table">
|
|
408
|
+
<tr>
|
|
409
|
+
<th><label for="dbHost">Database Host</label></th>
|
|
410
|
+
<td>
|
|
411
|
+
<input type="text" id="dbHost" value="127.0.0.1" />
|
|
412
|
+
<p class="description">Usually "localhost" or "127.0.0.1"</p>
|
|
413
|
+
</td>
|
|
414
|
+
</tr>
|
|
415
|
+
<tr>
|
|
416
|
+
<th><label for="dbPort">Database Port</label></th>
|
|
417
|
+
<td>
|
|
418
|
+
<input type="number" id="dbPort" value="3306" />
|
|
419
|
+
<p class="description">Default MySQL port is 3306</p>
|
|
420
|
+
</td>
|
|
421
|
+
</tr>
|
|
422
|
+
<tr>
|
|
423
|
+
<th><label for="dbUser">Username</label></th>
|
|
424
|
+
<td>
|
|
425
|
+
<input type="text" id="dbUser" value="" placeholder="Database username" />
|
|
426
|
+
<p class="description">Your MySQL username</p>
|
|
427
|
+
</td>
|
|
428
|
+
</tr>
|
|
429
|
+
<tr>
|
|
430
|
+
<th><label for="dbPassword">Password</label></th>
|
|
431
|
+
<td>
|
|
432
|
+
<input type="password" id="dbPassword" value="" placeholder="Database password" />
|
|
433
|
+
<p class="description">Your MySQL password</p>
|
|
434
|
+
</td>
|
|
435
|
+
</tr>
|
|
436
|
+
<tr>
|
|
437
|
+
<th><label for="dbName">Database Name</label></th>
|
|
438
|
+
<td>
|
|
439
|
+
<input type="text" id="dbName" value="reactpress" />
|
|
440
|
+
<p class="description">The name of the database you want to use with ReactPress</p>
|
|
441
|
+
</td>
|
|
442
|
+
</tr>
|
|
443
|
+
</table>
|
|
444
|
+
|
|
445
|
+
<div id="dbResult"></div>
|
|
446
|
+
|
|
447
|
+
<p class="text-center">
|
|
448
|
+
<button class="wp-button button-large" onclick="testDatabase()" id="testBtn">
|
|
449
|
+
Test Database Connection
|
|
450
|
+
</button>
|
|
451
|
+
</p>
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
<!-- Step 2: Site Configuration -->
|
|
455
|
+
<div class="wp-step" id="step2">
|
|
456
|
+
<h2>Site Information</h2>
|
|
457
|
+
|
|
458
|
+
<div class="wp-description">
|
|
459
|
+
Please provide the following information. Don't worry, you can always change these settings later.
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
<table class="wp-form-table">
|
|
463
|
+
<tr>
|
|
464
|
+
<th><label for="clientUrl">Client Site URL</label></th>
|
|
465
|
+
<td>
|
|
466
|
+
<input type="url" id="clientUrl" value="http://localhost:3001" />
|
|
467
|
+
<p class="description">The URL where your ReactPress frontend will be accessible</p>
|
|
468
|
+
</td>
|
|
469
|
+
</tr>
|
|
470
|
+
<tr>
|
|
471
|
+
<th><label for="serverUrl">API Server URL</label></th>
|
|
472
|
+
<td>
|
|
473
|
+
<input type="url" id="serverUrl" value="http://localhost:3002" />
|
|
474
|
+
<p class="description">The URL where your ReactPress API server will run</p>
|
|
475
|
+
</td>
|
|
476
|
+
</tr>
|
|
477
|
+
</table>
|
|
478
|
+
|
|
479
|
+
<div id="installResult"></div>
|
|
480
|
+
|
|
481
|
+
<p class="text-center">
|
|
482
|
+
<button class="wp-button button-large" onclick="installReactPress()" id="installBtn">
|
|
483
|
+
Install ReactPress
|
|
484
|
+
</button>
|
|
485
|
+
</p>
|
|
486
|
+
</div>
|
|
487
|
+
|
|
488
|
+
<!-- Step 3: Success -->
|
|
489
|
+
<div class="wp-step" id="step3">
|
|
490
|
+
<h2>Success!</h2>
|
|
491
|
+
|
|
492
|
+
<div class="wp-notice notice-success">
|
|
493
|
+
<p><strong>ReactPress has been installed successfully!</strong></p>
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<p>Your ReactPress installation is now complete. You can now:</p>
|
|
497
|
+
|
|
498
|
+
<ul style="margin: 16px 0 16px 20px;">
|
|
499
|
+
<li>Close this browser window</li>
|
|
500
|
+
<li>Run <code>npx reactpress-server start</code> to start your server</li>
|
|
501
|
+
<li>Visit your ReactPress site at the URLs you configured</li>
|
|
502
|
+
</ul>
|
|
503
|
+
</div>
|
|
504
|
+
</div>
|
|
505
|
+
|
|
506
|
+
<div class="wp-footer">
|
|
507
|
+
<p>Need help? <a href="https://github.com/fecommunity/reactpress/issues" target="_blank">ReactPress Documentation</a></p>
|
|
508
|
+
</div>
|
|
509
|
+
</div>
|
|
510
|
+
|
|
511
|
+
<script>
|
|
512
|
+
let currentStep = 1;
|
|
513
|
+
const totalSteps = 3;
|
|
514
|
+
let isTestingConnection = false;
|
|
515
|
+
|
|
516
|
+
function updateProgress() {
|
|
517
|
+
const progress = (currentStep / totalSteps) * 100;
|
|
518
|
+
document.getElementById('progressBar').style.width = progress + '%';
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function showStep(step) {
|
|
522
|
+
document.querySelectorAll('.wp-step').forEach(s => s.classList.remove('active'));
|
|
523
|
+
document.getElementById('step' + step).classList.add('active');
|
|
524
|
+
currentStep = step;
|
|
525
|
+
updateProgress();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function showNotice(message, type = 'error', containerId = 'dbResult') {
|
|
529
|
+
const container = document.getElementById(containerId);
|
|
530
|
+
container.innerHTML = \`
|
|
531
|
+
<div class="wp-notice notice-\${type}">
|
|
532
|
+
<p>\${message}</p>
|
|
533
|
+
</div>
|
|
534
|
+
\`;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function validateDatabaseInputs() {
|
|
538
|
+
const host = document.getElementById('dbHost').value.trim();
|
|
539
|
+
const port = document.getElementById('dbPort').value.trim();
|
|
540
|
+
const user = document.getElementById('dbUser').value.trim();
|
|
541
|
+
const password = document.getElementById('dbPassword').value;
|
|
542
|
+
const database = document.getElementById('dbName').value.trim();
|
|
543
|
+
|
|
544
|
+
if (!host || !port || !user || !database) {
|
|
545
|
+
showNotice('Please fill in all required fields.', 'error');
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
550
|
+
showNotice('Please enter a valid port number (1-65535).', 'error');
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return { host, port, user, password, database };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async function testDatabase() {
|
|
558
|
+
if (isTestingConnection) return;
|
|
559
|
+
|
|
560
|
+
const dbData = validateDatabaseInputs();
|
|
561
|
+
if (!dbData) return;
|
|
562
|
+
|
|
563
|
+
isTestingConnection = true;
|
|
564
|
+
const testBtn = document.getElementById('testBtn');
|
|
565
|
+
const originalText = testBtn.innerHTML;
|
|
566
|
+
|
|
567
|
+
testBtn.disabled = true;
|
|
568
|
+
testBtn.innerHTML = '<span class="wp-loader"></span>Testing connection...';
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
const response = await fetch('/test-db', {
|
|
572
|
+
method: 'POST',
|
|
573
|
+
headers: {
|
|
574
|
+
'Content-Type': 'application/json'
|
|
575
|
+
},
|
|
576
|
+
body: JSON.stringify(dbData)
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const result = await response.json();
|
|
580
|
+
|
|
581
|
+
if (result.success) {
|
|
582
|
+
showNotice(result.message, 'success');
|
|
583
|
+
setTimeout(() => {
|
|
584
|
+
showStep(2);
|
|
585
|
+
}, 1500);
|
|
586
|
+
} else {
|
|
587
|
+
showNotice(result.message, 'error');
|
|
588
|
+
}
|
|
589
|
+
} catch (error) {
|
|
590
|
+
showNotice('Failed to connect to server. Please try again.', 'error');
|
|
591
|
+
} finally {
|
|
592
|
+
isTestingConnection = false;
|
|
593
|
+
testBtn.disabled = false;
|
|
594
|
+
testBtn.innerHTML = originalText;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async function installReactPress() {
|
|
599
|
+
const dbData = validateDatabaseInputs();
|
|
600
|
+
if (!dbData) {
|
|
601
|
+
showStep(1);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const clientUrl = document.getElementById('clientUrl').value.trim();
|
|
606
|
+
const serverUrl = document.getElementById('serverUrl').value.trim();
|
|
607
|
+
|
|
608
|
+
if (!clientUrl || !serverUrl) {
|
|
609
|
+
showNotice('Please fill in all site URLs.', 'error', 'installResult');
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const installBtn = document.getElementById('installBtn');
|
|
614
|
+
const originalText = installBtn.innerHTML;
|
|
615
|
+
|
|
616
|
+
installBtn.disabled = true;
|
|
617
|
+
installBtn.innerHTML = '<span class="wp-loader"></span>Installing ReactPress...';
|
|
618
|
+
|
|
619
|
+
try {
|
|
620
|
+
const response = await fetch('/install', {
|
|
621
|
+
method: 'POST',
|
|
622
|
+
headers: {
|
|
623
|
+
'Content-Type': 'application/json'
|
|
624
|
+
},
|
|
625
|
+
body: JSON.stringify({
|
|
626
|
+
db: dbData,
|
|
627
|
+
site: { clientUrl, serverUrl }
|
|
628
|
+
})
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
const result = await response.json();
|
|
632
|
+
|
|
633
|
+
if (result.success) {
|
|
634
|
+
showStep(3);
|
|
635
|
+
} else {
|
|
636
|
+
showNotice(result.message, 'error', 'installResult');
|
|
637
|
+
}
|
|
638
|
+
} catch (error) {
|
|
639
|
+
showNotice('Installation failed. Please try again.', 'error', 'installResult');
|
|
640
|
+
} finally {
|
|
641
|
+
installBtn.disabled = false;
|
|
642
|
+
installBtn.innerHTML = originalText;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Initialize progress bar
|
|
647
|
+
updateProgress();
|
|
648
|
+
|
|
649
|
+
// Auto-focus first input
|
|
650
|
+
document.getElementById('dbHost').focus();
|
|
651
|
+
</script>
|
|
652
|
+
</body>
|
|
653
|
+
</html>`;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Main entry point
|
|
657
|
+
const args = process.argv.slice(2);
|
|
658
|
+
if (args.length === 0 || args[0] === 'install') {
|
|
659
|
+
install();
|
|
660
|
+
} else if (args[0] === 'start') {
|
|
661
|
+
start();
|
|
662
|
+
} else {
|
|
663
|
+
console.log(chalk.blue('ReactPress Server CLI'));
|
|
664
|
+
console.log(chalk.gray('npx reactpress-server # Install'));
|
|
665
|
+
console.log(chalk.gray('npx reactpress-server start # Start server'));
|
|
666
|
+
}
|