@fecommunity/reactpress-server 1.0.0-beta.12 → 1.0.0-beta.14
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/README.md +288 -33
- package/bin/reactpress-server.js +176 -599
- package/dist/app.module.js +2 -2
- package/dist/app.module.js.map +1 -1
- package/dist/generate-swagger.d.ts +1 -0
- package/dist/generate-swagger.js +73 -0
- package/dist/generate-swagger.js.map +1 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.js +258 -39
- package/dist/main.js.map +1 -1
- package/dist/modules/setting/setting.constant.d.ts +2 -2
- package/dist/modules/setting/setting.constant.js +3 -3
- package/dist/modules/setting/setting.constant.js.map +1 -1
- package/dist/modules/setting/setting.service.js +34 -11
- package/dist/modules/setting/setting.service.js.map +1 -1
- package/dist/public/favicon.png +0 -0
- package/dist/public/index.html +439 -0
- package/dist/public/swagger/custom.css +11 -0
- package/dist/starter.d.ts +1 -0
- package/dist/starter.js +84 -0
- package/dist/starter.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +982 -53
- package/package.json +31 -23
- package/public/favicon.png +0 -0
- package/public/index.html +439 -0
- package/public/swagger/custom.css +11 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>ReactPress › Installation</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* 保持原有样式不变 */
|
|
9
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f1f1f1; color: #444; line-height: 1.4; }
|
|
11
|
+
.wp-container { max-width: 600px; margin: 40px auto; background: #fff; border: 1px solid #ccd0d4; box-shadow: 0 1px 3px rgba(0,0,0,0.04); }
|
|
12
|
+
.wp-header { background: #0073aa; color: #fff; padding: 24px; text-align: center; border-bottom: 1px solid #005a87; }
|
|
13
|
+
.wp-header h1 { font-size: 24px; font-weight: 400; margin-bottom: 8px; }
|
|
14
|
+
.wp-header p { font-size: 16px; opacity: 0.9; }
|
|
15
|
+
.wp-content { padding: 24px; }
|
|
16
|
+
.wp-step { display: none; }
|
|
17
|
+
.wp-step.active { display: block; }
|
|
18
|
+
.wp-step h2 { font-size: 22px; font-weight: 600; margin-bottom: 20px; color: #1d2327; }
|
|
19
|
+
.wp-description { background: #f0f6fc; border: 1px solid #c3e4f0; border-left: 4px solid #0073aa; padding: 12px 16px; margin-bottom: 20px; font-size: 14px; color: #0c4a6e; }
|
|
20
|
+
.wp-form-table { width: 100%; margin-bottom: 20px; }
|
|
21
|
+
.wp-form-table th { text-align: left; padding: 10px 0; width: 140px; vertical-align: top; font-weight: 600; color: #1d2327; }
|
|
22
|
+
.wp-form-table td { padding: 10px 0; }
|
|
23
|
+
.wp-form-table input[type="text"], .wp-form-table input[type="password"], .wp-form-table input[type="number"], .wp-form-table input[type="url"] { width: 100%; max-width: 320px; padding: 8px 12px; border: 1px solid #8c8f94; border-radius: 4px; font-size: 14px; line-height: 1.4; transition: border-color 0.15s ease-in-out; }
|
|
24
|
+
.wp-form-table input:focus { border-color: #0073aa; outline: 0; box-shadow: 0 0 0 1px #0073aa; }
|
|
25
|
+
.wp-form-table .description { font-size: 13px; color: #646970; margin-top: 4px; }
|
|
26
|
+
.wp-button { background: #0073aa; color: #fff; border: 1px solid #0073aa; padding: 8px 16px; font-size: 14px; font-weight: 600; border-radius: 3px; cursor: pointer; transition: all 0.15s ease-in-out; text-decoration: none; display: inline-block; }
|
|
27
|
+
.wp-button:hover { background: #005a87; border-color: #005a87; }
|
|
28
|
+
.wp-button:disabled { background: #f0f0f1; border-color: #dcdcde; color: #a7aaad; cursor: not-allowed; }
|
|
29
|
+
.wp-button.button-large { padding: 12px 24px; font-size: 16px; }
|
|
30
|
+
.wp-notice { border: 1px solid #c3c4c7; border-left-width: 4px; padding: 12px; margin: 16px 0; background: #fff; }
|
|
31
|
+
.wp-notice.notice-success { border-left-color: #00a32a; background: #f7fcf7; }
|
|
32
|
+
.wp-notice.notice-error { border-left-color: #d63638; background: #fcf2f2; }
|
|
33
|
+
.wp-notice p { margin: 0; font-size: 14px; }
|
|
34
|
+
.wp-progress { background: #f0f0f1; height: 4px; margin-bottom: 20px; }
|
|
35
|
+
.wp-progress-bar { background: #0073aa; height: 100%; transition: width 0.3s ease; }
|
|
36
|
+
.wp-loader { display: inline-block; width: 16px; height: 16px; border: 2px solid #f3f3f3; border-top: 2px solid #0073aa; border-radius: 50%; animation: wp-spin 1s linear infinite; margin-right: 8px; }
|
|
37
|
+
@keyframes wp-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
|
38
|
+
.wp-footer { background: #f6f7f7; border-top: 1px solid #c3c4c7; padding: 16px 24px; text-align: center; font-size: 13px; color: #646970; }
|
|
39
|
+
.text-center { text-align: center; }
|
|
40
|
+
.countdown-container { display: none; text-align: center; margin: 20px 0; }
|
|
41
|
+
.countdown-timer { font-weight: bold; color: #0073aa; }
|
|
42
|
+
|
|
43
|
+
/* 新增重试按钮样式 */
|
|
44
|
+
.retry-button {
|
|
45
|
+
background: #d63638;
|
|
46
|
+
color: white;
|
|
47
|
+
border: none;
|
|
48
|
+
padding: 8px 16px;
|
|
49
|
+
border-radius: 3px;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
margin-top: 10px;
|
|
52
|
+
}
|
|
53
|
+
.retry-button:hover {
|
|
54
|
+
background: #b32d2e;
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div class="wp-container">
|
|
60
|
+
<div class="wp-header">
|
|
61
|
+
<h1>ReactPress</h1>
|
|
62
|
+
<p>5-Minute Installation</p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="wp-progress">
|
|
66
|
+
<div class="wp-progress-bar" id="progressBar" style="width: 33%;"></div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="wp-content">
|
|
70
|
+
<!-- Step 1: Database Configuration -->
|
|
71
|
+
<div class="wp-step active" id="step1">
|
|
72
|
+
<h2>Database Connection Information</h2>
|
|
73
|
+
|
|
74
|
+
<div class="wp-description">
|
|
75
|
+
You should have this information available from your web hosting provider. If you don't have it, contact them before continuing.
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<table class="wp-form-table">
|
|
79
|
+
<tr>
|
|
80
|
+
<th><label for="dbHost">Database Host</label></th>
|
|
81
|
+
<td>
|
|
82
|
+
<input type="text" id="dbHost" value="127.0.0.1" />
|
|
83
|
+
<p class="description">Usually "localhost" or "127.0.0.1"</p>
|
|
84
|
+
</td>
|
|
85
|
+
</tr>
|
|
86
|
+
<tr>
|
|
87
|
+
<th><label for="dbPort">Database Port</label></th>
|
|
88
|
+
<td>
|
|
89
|
+
<input type="number" id="dbPort" value="3306" />
|
|
90
|
+
<p class="description">Default MySQL port is 3306</p>
|
|
91
|
+
</td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<th><label for="dbUser">Username</label></th>
|
|
95
|
+
<td>
|
|
96
|
+
<input type="text" id="dbUser" value="" placeholder="Database username" />
|
|
97
|
+
<p class="description">Your MySQL username</p>
|
|
98
|
+
</td>
|
|
99
|
+
</tr>
|
|
100
|
+
<tr>
|
|
101
|
+
<th><label for="dbPassword">Password</label></th>
|
|
102
|
+
<td>
|
|
103
|
+
<input type="password" id="dbPassword" value="" placeholder="Database password" />
|
|
104
|
+
<p class="description">Your MySQL password</p>
|
|
105
|
+
</td>
|
|
106
|
+
</tr>
|
|
107
|
+
<tr>
|
|
108
|
+
<th><label for="dbName">Database Name</label></th>
|
|
109
|
+
<td>
|
|
110
|
+
<input type="text" id="dbName" value="reactpress" />
|
|
111
|
+
<p class="description">The name of the database you want to use with ReactPress</p>
|
|
112
|
+
</td>
|
|
113
|
+
</tr>
|
|
114
|
+
</table>
|
|
115
|
+
|
|
116
|
+
<div id="dbResult"></div>
|
|
117
|
+
|
|
118
|
+
<p class="text-center">
|
|
119
|
+
<button class="wp-button button-large" onclick="testDatabase()" id="testBtn">
|
|
120
|
+
Test Database Connection
|
|
121
|
+
</button>
|
|
122
|
+
</p>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- Step 2: Site Configuration -->
|
|
126
|
+
<div class="wp-step" id="step2">
|
|
127
|
+
<h2>Site Information</h2>
|
|
128
|
+
|
|
129
|
+
<div class="wp-description">
|
|
130
|
+
Please provide the following information. Don't worry, you can always change these settings later.
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<table class="wp-form-table">
|
|
134
|
+
<tr>
|
|
135
|
+
<th><label for="clientUrl">Client Site URL</label></th>
|
|
136
|
+
<td>
|
|
137
|
+
<input type="url" id="clientUrl" value="http://localhost:3001" />
|
|
138
|
+
<p class="description">The URL where your ReactPress frontend will be accessible</p>
|
|
139
|
+
</td>
|
|
140
|
+
</tr>
|
|
141
|
+
<tr>
|
|
142
|
+
<th><label for="serverUrl">API Server URL</label></th>
|
|
143
|
+
<td>
|
|
144
|
+
<input type="url" id="serverUrl" value="http://localhost:3002" />
|
|
145
|
+
<p class="description">The URL where your ReactPress API server will run</p>
|
|
146
|
+
</td>
|
|
147
|
+
</tr>
|
|
148
|
+
</table>
|
|
149
|
+
|
|
150
|
+
<div id="installResult"></div>
|
|
151
|
+
|
|
152
|
+
<p class="text-center">
|
|
153
|
+
<button class="wp-button button-large" onclick="installReactPress()" id="installBtn">
|
|
154
|
+
Install & Start ReactPress
|
|
155
|
+
</button>
|
|
156
|
+
</p>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Step 3: Success -->
|
|
160
|
+
<div class="wp-step" id="step3">
|
|
161
|
+
<h2>🎉 Installation Complete!</h2>
|
|
162
|
+
|
|
163
|
+
<div class="wp-notice notice-success">
|
|
164
|
+
<p><strong>ReactPress has been installed successfully!</strong></p>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<p>Your ReactPress server configuration has been saved. You can:</p>
|
|
168
|
+
|
|
169
|
+
<ul style="margin: 16px 0 16px 20px;">
|
|
170
|
+
<li>Close this browser window</li>
|
|
171
|
+
<li>Restart the server by running <code>npx @fecommunity/reactpress-server</code> in your terminal</li>
|
|
172
|
+
<li>Access your admin panel at <strong id="adminUrl">http://localhost:3001/admin</strong> (when client is running)</li>
|
|
173
|
+
</ul>
|
|
174
|
+
|
|
175
|
+
<!-- 重定向状态显示 -->
|
|
176
|
+
<div class="wp-notice" id="redirectStatus">
|
|
177
|
+
<p>Waiting for server to start...</p>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div class="text-center">
|
|
181
|
+
<button class="wp-button" onclick="checkServerStatus()" id="checkStatusBtn">
|
|
182
|
+
Check Server Status
|
|
183
|
+
</button>
|
|
184
|
+
<button class="retry-button" onclick="retryRedirect()" id="retryBtn" style="display: none;">
|
|
185
|
+
Retry Connection
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="wp-description">
|
|
190
|
+
<strong>Note:</strong> To start the server with your new configuration, run <code>npx @fecommunity/reactpress-server</code> in your terminal.
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="wp-footer">
|
|
196
|
+
<p>Need help? <a href="https://github.com/fecommunity/reactpress/issues" target="_blank">ReactPress Documentation</a></p>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<script>
|
|
201
|
+
// State management
|
|
202
|
+
let currentStep = 1;
|
|
203
|
+
const totalSteps = 3;
|
|
204
|
+
let isTestingConnection = false;
|
|
205
|
+
let countdownInterval = null;
|
|
206
|
+
let serverUrl = 'http://localhost:3002';
|
|
207
|
+
let redirectAttempts = 0;
|
|
208
|
+
const maxRedirectAttempts = 30; // 30秒超时
|
|
209
|
+
|
|
210
|
+
// Update progress bar
|
|
211
|
+
function updateProgress() {
|
|
212
|
+
const progress = (currentStep / totalSteps) * 100;
|
|
213
|
+
document.getElementById('progressBar').style.width = progress + '%';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Show a specific step
|
|
217
|
+
function showStep(step) {
|
|
218
|
+
document.querySelectorAll('.wp-step').forEach(s => s.classList.remove('active'));
|
|
219
|
+
document.getElementById('step' + step).classList.add('active');
|
|
220
|
+
currentStep = step;
|
|
221
|
+
updateProgress();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Show a notice message
|
|
225
|
+
function showNotice(message, type = 'error', containerId = 'dbResult') {
|
|
226
|
+
const container = document.getElementById(containerId);
|
|
227
|
+
container.innerHTML = `
|
|
228
|
+
<div class="wp-notice notice-${type}">
|
|
229
|
+
<p>${message}</p>
|
|
230
|
+
</div>
|
|
231
|
+
`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 检查服务器状态
|
|
235
|
+
async function checkServerStatus() {
|
|
236
|
+
const statusElement = document.getElementById('redirectStatus');
|
|
237
|
+
const checkBtn = document.getElementById('checkStatusBtn');
|
|
238
|
+
const retryBtn = document.getElementById('retryBtn');
|
|
239
|
+
|
|
240
|
+
statusElement.innerHTML = '<p>Checking server status...</p>';
|
|
241
|
+
statusElement.className = 'wp-notice';
|
|
242
|
+
checkBtn.disabled = true;
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const response = await fetch(`${serverUrl}/api`, {
|
|
246
|
+
method: 'GET',
|
|
247
|
+
headers: { 'Content-Type': 'application/json' },
|
|
248
|
+
// 添加超时处理
|
|
249
|
+
signal: AbortSignal.timeout(5000)
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (response.ok) {
|
|
253
|
+
statusElement.innerHTML = '<p>Server is running! Redirecting to API documentation...</p>';
|
|
254
|
+
statusElement.className = 'wp-notice notice-success';
|
|
255
|
+
|
|
256
|
+
// 服务器已启动,重定向到API页面
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
window.location.href = `${serverUrl}/api`;
|
|
259
|
+
}, 1000);
|
|
260
|
+
} else {
|
|
261
|
+
statusElement.innerHTML = '<p>Server responded but with an error. Please try again later.</p>';
|
|
262
|
+
statusElement.className = 'wp-notice notice-error';
|
|
263
|
+
retryBtn.style.display = 'inline-block';
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
statusElement.innerHTML = `<p>Cannot connect to server: ${error.message}. The server may still be starting up.</p>`;
|
|
267
|
+
statusElement.className = 'wp-notice notice-error';
|
|
268
|
+
retryBtn.style.display = 'inline-block';
|
|
269
|
+
} finally {
|
|
270
|
+
checkBtn.disabled = false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 重试重定向
|
|
275
|
+
function retryRedirect() {
|
|
276
|
+
const retryBtn = document.getElementById('retryBtn');
|
|
277
|
+
retryBtn.style.display = 'none';
|
|
278
|
+
checkServerStatus();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 启动服务器状态检查
|
|
282
|
+
function startServerStatusCheck() {
|
|
283
|
+
// 每2秒检查一次服务器状态,最多尝试30次(60秒)
|
|
284
|
+
const checkInterval = setInterval(() => {
|
|
285
|
+
if (redirectAttempts >= maxRedirectAttempts) {
|
|
286
|
+
clearInterval(checkInterval);
|
|
287
|
+
const statusElement = document.getElementById('redirectStatus');
|
|
288
|
+
statusElement.innerHTML = '<p>Server did not start in time. Please check your terminal for errors and try restarting the server manually.</p>';
|
|
289
|
+
statusElement.className = 'wp-notice notice-error';
|
|
290
|
+
document.getElementById('retryBtn').style.display = 'inline-block';
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
fetch(`${serverUrl}/api`, {
|
|
295
|
+
method: 'HEAD',
|
|
296
|
+
// 添加超时处理
|
|
297
|
+
signal: AbortSignal.timeout(3000)
|
|
298
|
+
})
|
|
299
|
+
.then(response => {
|
|
300
|
+
if (response.ok) {
|
|
301
|
+
clearInterval(checkInterval);
|
|
302
|
+
const statusElement = document.getElementById('redirectStatus');
|
|
303
|
+
statusElement.innerHTML = '<p>Server is running! Redirecting to API documentation...</p>';
|
|
304
|
+
statusElement.className = 'wp-notice notice-success';
|
|
305
|
+
|
|
306
|
+
// 服务器已启动,重定向到API页面
|
|
307
|
+
setTimeout(() => {
|
|
308
|
+
window.location.href = `${serverUrl}/api`;
|
|
309
|
+
}, 1000);
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
.catch(() => {
|
|
313
|
+
// 忽略错误,继续尝试
|
|
314
|
+
redirectAttempts++;
|
|
315
|
+
const statusElement = document.getElementById('redirectStatus');
|
|
316
|
+
statusElement.innerHTML = `<p>Waiting for server to start... (${redirectAttempts}/${maxRedirectAttempts})</p>`;
|
|
317
|
+
});
|
|
318
|
+
}, 2000);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Validate database input fields
|
|
322
|
+
function validateDatabaseInputs() {
|
|
323
|
+
const host = document.getElementById('dbHost').value.trim();
|
|
324
|
+
const port = document.getElementById('dbPort').value.trim();
|
|
325
|
+
const user = document.getElementById('dbUser').value.trim();
|
|
326
|
+
const password = document.getElementById('dbPassword').value;
|
|
327
|
+
const database = document.getElementById('dbName').value.trim();
|
|
328
|
+
|
|
329
|
+
if (!host || !port || !user || !database) {
|
|
330
|
+
showNotice('Please fill in all required fields.', 'error');
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
335
|
+
showNotice('Please enter a valid port number (1-65535).', 'error');
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return { host, port, user, password, database };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Test database connection
|
|
343
|
+
async function testDatabase() {
|
|
344
|
+
if (isTestingConnection) return;
|
|
345
|
+
|
|
346
|
+
const dbData = validateDatabaseInputs();
|
|
347
|
+
if (!dbData) return;
|
|
348
|
+
|
|
349
|
+
isTestingConnection = true;
|
|
350
|
+
const testBtn = document.getElementById('testBtn');
|
|
351
|
+
const originalText = testBtn.innerHTML;
|
|
352
|
+
|
|
353
|
+
testBtn.disabled = true;
|
|
354
|
+
testBtn.innerHTML = '<span class="wp-loader"></span>Testing connection...';
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const response = await fetch('/test-db', {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
headers: { 'Content-Type': 'application/json' },
|
|
360
|
+
body: JSON.stringify(dbData)
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const result = await response.json();
|
|
364
|
+
|
|
365
|
+
if (result.success) {
|
|
366
|
+
showNotice(result.message, 'success');
|
|
367
|
+
setTimeout(() => {
|
|
368
|
+
showStep(2);
|
|
369
|
+
}, 1500);
|
|
370
|
+
} else {
|
|
371
|
+
showNotice(result.message, 'error');
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
showNotice('Failed to connect to server. Please try again.', 'error');
|
|
375
|
+
} finally {
|
|
376
|
+
isTestingConnection = false;
|
|
377
|
+
testBtn.disabled = false;
|
|
378
|
+
testBtn.innerHTML = originalText;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Install ReactPress
|
|
383
|
+
async function installReactPress() {
|
|
384
|
+
const dbData = validateDatabaseInputs();
|
|
385
|
+
if (!dbData) {
|
|
386
|
+
showStep(1);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const clientUrl = document.getElementById('clientUrl').value.trim();
|
|
391
|
+
serverUrl = document.getElementById('serverUrl').value.trim();
|
|
392
|
+
|
|
393
|
+
if (!clientUrl || !serverUrl) {
|
|
394
|
+
showNotice('Please fill in all site URLs.', 'error', 'installResult');
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// 更新管理URL显示
|
|
399
|
+
document.getElementById('adminUrl').textContent = `${clientUrl}/admin`;
|
|
400
|
+
|
|
401
|
+
const installBtn = document.getElementById('installBtn');
|
|
402
|
+
const originalText = installBtn.innerHTML;
|
|
403
|
+
|
|
404
|
+
installBtn.disabled = true;
|
|
405
|
+
installBtn.innerHTML = '<span class="wp-loader"></span>Installing & Starting...';
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
const response = await fetch('/install', {
|
|
409
|
+
method: 'POST',
|
|
410
|
+
headers: { 'Content-Type': 'application/json' },
|
|
411
|
+
body: JSON.stringify({
|
|
412
|
+
db: dbData,
|
|
413
|
+
site: { clientUrl, serverUrl }
|
|
414
|
+
})
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
const result = await response.json();
|
|
418
|
+
|
|
419
|
+
if (result.success) {
|
|
420
|
+
showStep(3);
|
|
421
|
+
// 启动服务器状态检查
|
|
422
|
+
startServerStatusCheck();
|
|
423
|
+
} else {
|
|
424
|
+
showNotice(result.message, 'error', 'installResult');
|
|
425
|
+
}
|
|
426
|
+
} catch (error) {
|
|
427
|
+
showNotice('Installation failed. Please try again.', 'error', 'installResult');
|
|
428
|
+
} finally {
|
|
429
|
+
installBtn.disabled = false;
|
|
430
|
+
installBtn.innerHTML = originalText;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Initialize the wizard
|
|
435
|
+
updateProgress();
|
|
436
|
+
document.getElementById('dbHost').focus();
|
|
437
|
+
</script>
|
|
438
|
+
</body>
|
|
439
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function bootstrap(): Promise<import("@nestjs/common").INestApplication>;
|
package/dist/starter.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bootstrap = void 0;
|
|
4
|
+
const core_1 = require("@nestjs/core");
|
|
5
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
6
|
+
const bodyParser = require("body-parser");
|
|
7
|
+
const compression = require("compression");
|
|
8
|
+
const express = require("express");
|
|
9
|
+
const rateLimit = require("express-rate-limit");
|
|
10
|
+
const helmet = require("helmet");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const swagger_themes_1 = require("swagger-themes");
|
|
13
|
+
const app_module_1 = require("./app.module");
|
|
14
|
+
const http_exception_filter_1 = require("./filters/http-exception.filter");
|
|
15
|
+
const transform_interceptor_1 = require("./interceptors/transform.interceptor");
|
|
16
|
+
async function bootstrap() {
|
|
17
|
+
try {
|
|
18
|
+
const app = await core_1.NestFactory.create(app_module_1.AppModule);
|
|
19
|
+
const configService = app.get('ConfigService');
|
|
20
|
+
app.enableCors();
|
|
21
|
+
app.setGlobalPrefix(configService.get('SERVER_API_PREFIX', '/api'));
|
|
22
|
+
app.use(rateLimit({
|
|
23
|
+
windowMs: 60 * 1000,
|
|
24
|
+
max: 10000,
|
|
25
|
+
}));
|
|
26
|
+
const rootDir = path_1.join(__dirname, '../public');
|
|
27
|
+
app.use('/public', express.static(rootDir));
|
|
28
|
+
app.use(compression());
|
|
29
|
+
app.use(helmet());
|
|
30
|
+
app.useGlobalInterceptors(new transform_interceptor_1.TransformInterceptor());
|
|
31
|
+
app.useGlobalFilters(new http_exception_filter_1.HttpExceptionFilter());
|
|
32
|
+
app.use(bodyParser.json({ limit: '10mb' }));
|
|
33
|
+
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }));
|
|
34
|
+
const swaggerConfig = new swagger_1.DocumentBuilder()
|
|
35
|
+
.setTitle('ReactPress API Documentation')
|
|
36
|
+
.setDescription('Comprehensive API documentation for ReactPress - A modern content management system built with NestJS')
|
|
37
|
+
.setVersion('2.0')
|
|
38
|
+
.setContact('ReactPress Team', 'https://github.com/fecommunity/reactpress', 'admin@gaoredu.com')
|
|
39
|
+
.setLicense('MIT', 'https://github.com/fecommunity/reactpress/blob/main/LICENSE')
|
|
40
|
+
.addServer(configService.get('SERVER_SITE_URL', 'http://localhost:3002'), 'API Server')
|
|
41
|
+
.build();
|
|
42
|
+
const document = swagger_1.SwaggerModule.createDocument(app, swaggerConfig);
|
|
43
|
+
const theme = new swagger_themes_1.SwaggerTheme();
|
|
44
|
+
const options = {
|
|
45
|
+
customCss: theme.getBuffer(swagger_themes_1.SwaggerThemeNameEnum.MATERIAL),
|
|
46
|
+
customSiteTitle: 'ReactPress API Documentation',
|
|
47
|
+
customfavIcon: '/public/favicon.png',
|
|
48
|
+
swaggerOptions: {
|
|
49
|
+
docExpansion: 'list',
|
|
50
|
+
filter: true,
|
|
51
|
+
showRequestDuration: true,
|
|
52
|
+
persistAuthorization: true,
|
|
53
|
+
displayOperationId: true,
|
|
54
|
+
operationsSorter: 'method',
|
|
55
|
+
tagsSorter: 'alpha',
|
|
56
|
+
},
|
|
57
|
+
customCssUrl: '/public/swagger/custom.css',
|
|
58
|
+
};
|
|
59
|
+
swagger_1.SwaggerModule.setup('api', app, document, options);
|
|
60
|
+
const configuredPort = configService.get('SERVER_PORT', 3002);
|
|
61
|
+
await app.listen(configuredPort);
|
|
62
|
+
console.log(`[ReactPress] Application started on http://localhost:${configuredPort}`);
|
|
63
|
+
console.log(`[ReactPress] API Documentation available at http://localhost:${configuredPort}/api`);
|
|
64
|
+
return app;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('[ReactPress] Failed to start application:', error);
|
|
68
|
+
if (error.code === 'EADDRINUSE') {
|
|
69
|
+
console.error('[ReactPress] Port is already in use. Please check for other running instances.');
|
|
70
|
+
console.error('[ReactPress] You can change the port in your .env file or terminate the conflicting process.');
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.bootstrap = bootstrap;
|
|
76
|
+
process.on('SIGINT', () => {
|
|
77
|
+
console.log('\n[ReactPress] Application shutting down gracefully...');
|
|
78
|
+
process.exit(0);
|
|
79
|
+
});
|
|
80
|
+
process.on('SIGTERM', () => {
|
|
81
|
+
console.log('\n[ReactPress] Application shutting down gracefully...');
|
|
82
|
+
process.exit(0);
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=starter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter.js","sourceRoot":"","sources":["../src/starter.ts"],"names":[],"mappings":";;;AAAA,uCAA2C;AAC3C,6CAAiE;AACjE,0CAA0C;AAC1C,2CAA2C;AAC3C,mCAAmC;AACnC,gDAAgD;AAChD,iCAAiC;AACjC,+BAA4B;AAC5B,mDAAoE;AAEpE,6CAAyC;AACzC,2EAAsE;AACtE,gFAA4E;AAErE,KAAK,UAAU,SAAS;IAC7B,IAAI;QACF,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAE/C,GAAG,CAAC,UAAU,EAAE,CAAC;QACjB,GAAG,CAAC,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;QAEpE,GAAG,CAAC,GAAG,CACL,SAAS,CAAC;YACR,QAAQ,EAAE,EAAE,GAAG,IAAI;YACnB,GAAG,EAAE,KAAK;SACX,CAAC,CACH,CAAC;QAEF,MAAM,OAAO,GAAG,WAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAC7C,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAE5C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAClB,GAAG,CAAC,qBAAqB,CAAC,IAAI,4CAAoB,EAAE,CAAC,CAAC;QACtD,GAAG,CAAC,gBAAgB,CAAC,IAAI,2CAAmB,EAAE,CAAC,CAAC;QAChD,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5C,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAGlE,MAAM,aAAa,GAAG,IAAI,yBAAe,EAAE;aACxC,QAAQ,CAAC,8BAA8B,CAAC;aACxC,cAAc,CAAC,uGAAuG,CAAC;aACvH,UAAU,CAAC,KAAK,CAAC;aACjB,UAAU,CAAC,iBAAiB,EAAE,2CAA2C,EAAE,mBAAmB,CAAC;aAC/F,UAAU,CAAC,KAAK,EAAE,6DAA6D,CAAC;aAChF,SAAS,CAAC,aAAa,CAAC,GAAG,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,EAAE,YAAY,CAAC;aACtF,KAAK,EAAE,CAAC;QAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAGlE,MAAM,KAAK,GAAG,IAAI,6BAAY,EAAE,CAAC;QAGjC,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,qCAAoB,CAAC,QAAQ,CAAC;YACzD,eAAe,EAAE,8BAA8B;YAC/C,aAAa,EAAE,qBAAqB;YACpC,cAAc,EAAE;gBACd,YAAY,EAAE,MAAM;gBACpB,MAAM,EAAE,IAAI;gBACZ,mBAAmB,EAAE,IAAI;gBACzB,oBAAoB,EAAE,IAAI;gBAC1B,kBAAkB,EAAE,IAAI;gBACxB,gBAAgB,EAAE,QAAQ;gBAC1B,UAAU,EAAE,OAAO;aACpB;YACD,YAAY,EAAE,4BAA4B;SAC3C,CAAC;QAGF,uBAAa,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEnD,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAE9D,MAAM,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,wDAAwD,cAAc,EAAE,CAAC,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,gEAAgE,cAAc,MAAM,CAAC,CAAC;QAElG,OAAO,GAAG,CAAC;KAEZ;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;QAElE,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE;YAC/B,OAAO,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAC;YAChG,OAAO,CAAC,KAAK,CAAC,8FAA8F,CAAC,CAAC;SAC/G;QAED,MAAM,KAAK,CAAC;KACb;AACH,CAAC;AA9ED,8BA8EC;AAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;IACxB,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACzB,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|