@qwickapps/server 1.1.7 → 1.1.9
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/dist/core/gateway.d.ts +5 -0
- package/dist/core/gateway.d.ts.map +1 -1
- package/dist/core/gateway.js +380 -6
- package/dist/core/gateway.js.map +1 -1
- package/dist/core/logging.js +1 -1
- package/dist/core/logging.js.map +1 -1
- package/package.json +1 -1
- package/src/core/gateway.ts +401 -7
- package/src/core/logging.ts +1 -1
package/dist/core/gateway.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export interface GatewayConfig {
|
|
|
31
31
|
productName: string;
|
|
32
32
|
/** Product version */
|
|
33
33
|
version?: string;
|
|
34
|
+
/**
|
|
35
|
+
* URL to the product logo image (SVG, PNG, etc.).
|
|
36
|
+
* Used on the default landing page when no frontend app is configured.
|
|
37
|
+
*/
|
|
38
|
+
logoUrl?: string;
|
|
34
39
|
/** Branding configuration */
|
|
35
40
|
branding?: ControlPanelConfig['branding'];
|
|
36
41
|
/** CORS origins */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/core/gateway.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAmC,MAAM,SAAS,CAAC;AAG5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAM5F;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IAEpB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE1C,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE/B,wCAAwC;IACxC,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAEpC,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAEhD;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ,8BAA8B;QAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,oCAAoC;QACpC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,iCAAiC;QACjC,WAAW,CAAC,EAAE;YACZ,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,KAAK,CAAC,EAAE,KAAK,CAAC;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,GAAG,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAC/C,CAAC;KACH,CAAC;IAEF;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QACtB,GAAG,EAAE,WAAW,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KAC/B,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,YAAY,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IAEpD,wCAAwC;IACxC,OAAO,EAAE;QACP,GAAG,EAAE,WAAW,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KAC/B,GAAG,IAAI,CAAC;IAET,6CAA6C;IAC7C,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3B,iCAAiC;IACjC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1B,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IAEpB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/core/gateway.ts"],"names":[],"mappings":"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"}
|
package/dist/core/gateway.js
CHANGED
|
@@ -113,6 +113,374 @@ function generateLandingPageHtml(config, controlPanelPath) {
|
|
|
113
113
|
</body>
|
|
114
114
|
</html>`;
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Generate default landing page HTML when no frontend app is configured
|
|
118
|
+
* Shows system status with animated background
|
|
119
|
+
*/
|
|
120
|
+
function generateDefaultLandingPageHtml(productName, controlPanelPath, apiBasePath, version, logoUrl) {
|
|
121
|
+
return `<!DOCTYPE html>
|
|
122
|
+
<html lang="en">
|
|
123
|
+
<head>
|
|
124
|
+
<meta charset="UTF-8">
|
|
125
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
126
|
+
<title>${productName}</title>
|
|
127
|
+
<style>
|
|
128
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
129
|
+
|
|
130
|
+
:root {
|
|
131
|
+
--primary: #6366f1;
|
|
132
|
+
--primary-glow: rgba(99, 102, 241, 0.4);
|
|
133
|
+
--success: #22c55e;
|
|
134
|
+
--warning: #f59e0b;
|
|
135
|
+
--error: #ef4444;
|
|
136
|
+
--bg-dark: #0a0a0f;
|
|
137
|
+
--bg-card: rgba(255, 255, 255, 0.03);
|
|
138
|
+
--text-primary: #f1f5f9;
|
|
139
|
+
--text-secondary: #94a3b8;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
body {
|
|
143
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
144
|
+
background: var(--bg-dark);
|
|
145
|
+
color: var(--text-primary);
|
|
146
|
+
min-height: 100vh;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Animated gradient background */
|
|
154
|
+
.bg-gradient {
|
|
155
|
+
position: fixed;
|
|
156
|
+
top: 0;
|
|
157
|
+
left: 0;
|
|
158
|
+
right: 0;
|
|
159
|
+
bottom: 0;
|
|
160
|
+
background:
|
|
161
|
+
radial-gradient(ellipse at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
|
|
162
|
+
radial-gradient(ellipse at 80% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 50%),
|
|
163
|
+
radial-gradient(ellipse at 50% 50%, rgba(59, 130, 246, 0.05) 0%, transparent 70%);
|
|
164
|
+
animation: gradientShift 15s ease-in-out infinite;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@keyframes gradientShift {
|
|
168
|
+
0%, 100% {
|
|
169
|
+
background-position: 0% 0%, 100% 100%, 50% 50%;
|
|
170
|
+
opacity: 1;
|
|
171
|
+
}
|
|
172
|
+
50% {
|
|
173
|
+
background-position: 100% 0%, 0% 100%, 50% 50%;
|
|
174
|
+
opacity: 0.8;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Floating particles */
|
|
179
|
+
.particles {
|
|
180
|
+
position: fixed;
|
|
181
|
+
top: 0;
|
|
182
|
+
left: 0;
|
|
183
|
+
right: 0;
|
|
184
|
+
bottom: 0;
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
pointer-events: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.particle {
|
|
190
|
+
position: absolute;
|
|
191
|
+
width: 4px;
|
|
192
|
+
height: 4px;
|
|
193
|
+
background: var(--primary);
|
|
194
|
+
border-radius: 50%;
|
|
195
|
+
opacity: 0.3;
|
|
196
|
+
animation: float 20s infinite;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.particle:nth-child(1) { left: 10%; animation-delay: 0s; animation-duration: 25s; }
|
|
200
|
+
.particle:nth-child(2) { left: 20%; animation-delay: 2s; animation-duration: 20s; }
|
|
201
|
+
.particle:nth-child(3) { left: 30%; animation-delay: 4s; animation-duration: 28s; }
|
|
202
|
+
.particle:nth-child(4) { left: 40%; animation-delay: 1s; animation-duration: 22s; }
|
|
203
|
+
.particle:nth-child(5) { left: 50%; animation-delay: 3s; animation-duration: 24s; }
|
|
204
|
+
.particle:nth-child(6) { left: 60%; animation-delay: 5s; animation-duration: 26s; }
|
|
205
|
+
.particle:nth-child(7) { left: 70%; animation-delay: 2s; animation-duration: 21s; }
|
|
206
|
+
.particle:nth-child(8) { left: 80%; animation-delay: 4s; animation-duration: 23s; }
|
|
207
|
+
.particle:nth-child(9) { left: 90%; animation-delay: 1s; animation-duration: 27s; }
|
|
208
|
+
|
|
209
|
+
@keyframes float {
|
|
210
|
+
0% { transform: translateY(100vh) scale(0); opacity: 0; }
|
|
211
|
+
10% { opacity: 0.3; }
|
|
212
|
+
90% { opacity: 0.3; }
|
|
213
|
+
100% { transform: translateY(-100vh) scale(1); opacity: 0; }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Grid pattern overlay */
|
|
217
|
+
.grid-overlay {
|
|
218
|
+
position: fixed;
|
|
219
|
+
top: 0;
|
|
220
|
+
left: 0;
|
|
221
|
+
right: 0;
|
|
222
|
+
bottom: 0;
|
|
223
|
+
background-image:
|
|
224
|
+
linear-gradient(rgba(99, 102, 241, 0.03) 1px, transparent 1px),
|
|
225
|
+
linear-gradient(90deg, rgba(99, 102, 241, 0.03) 1px, transparent 1px);
|
|
226
|
+
background-size: 60px 60px;
|
|
227
|
+
pointer-events: none;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.container {
|
|
231
|
+
position: relative;
|
|
232
|
+
z-index: 10;
|
|
233
|
+
text-align: center;
|
|
234
|
+
max-width: 500px;
|
|
235
|
+
padding: 3rem 2rem;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.logo {
|
|
239
|
+
width: 80px;
|
|
240
|
+
height: 80px;
|
|
241
|
+
margin: 0 auto 2rem;
|
|
242
|
+
display: flex;
|
|
243
|
+
align-items: center;
|
|
244
|
+
justify-content: center;
|
|
245
|
+
animation: logoFloat 6s ease-in-out infinite;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.logo.default {
|
|
249
|
+
background: linear-gradient(135deg, var(--primary) 0%, #8b5cf6 100%);
|
|
250
|
+
border-radius: 20px;
|
|
251
|
+
box-shadow: 0 20px 40px var(--primary-glow);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.logo.custom {
|
|
255
|
+
filter: drop-shadow(0 20px 40px var(--primary-glow));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@keyframes logoFloat {
|
|
259
|
+
0%, 100% { transform: translateY(0); }
|
|
260
|
+
50% { transform: translateY(-10px); }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.logo svg {
|
|
264
|
+
width: 48px;
|
|
265
|
+
height: 48px;
|
|
266
|
+
fill: white;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.logo img {
|
|
270
|
+
width: 80px;
|
|
271
|
+
height: 80px;
|
|
272
|
+
object-fit: contain;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
h1 {
|
|
276
|
+
font-size: 2.5rem;
|
|
277
|
+
font-weight: 700;
|
|
278
|
+
margin-bottom: 0.5rem;
|
|
279
|
+
background: linear-gradient(135deg, var(--text-primary) 0%, var(--primary) 100%);
|
|
280
|
+
-webkit-background-clip: text;
|
|
281
|
+
-webkit-text-fill-color: transparent;
|
|
282
|
+
background-clip: text;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.status-badge {
|
|
286
|
+
display: inline-flex;
|
|
287
|
+
align-items: center;
|
|
288
|
+
gap: 0.5rem;
|
|
289
|
+
padding: 0.75rem 1.5rem;
|
|
290
|
+
background: var(--bg-card);
|
|
291
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
292
|
+
border-radius: 100px;
|
|
293
|
+
margin: 1.5rem 0 2rem;
|
|
294
|
+
backdrop-filter: blur(10px);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.status-dot {
|
|
298
|
+
width: 10px;
|
|
299
|
+
height: 10px;
|
|
300
|
+
border-radius: 50%;
|
|
301
|
+
background: var(--success);
|
|
302
|
+
box-shadow: 0 0 10px var(--success);
|
|
303
|
+
animation: pulse 2s ease-in-out infinite;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.status-dot.degraded {
|
|
307
|
+
background: var(--warning);
|
|
308
|
+
box-shadow: 0 0 10px var(--warning);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.status-dot.unhealthy {
|
|
312
|
+
background: var(--error);
|
|
313
|
+
box-shadow: 0 0 10px var(--error);
|
|
314
|
+
animation: none;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@keyframes pulse {
|
|
318
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
319
|
+
50% { opacity: 0.7; transform: scale(1.1); }
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.status-text {
|
|
323
|
+
font-size: 0.95rem;
|
|
324
|
+
font-weight: 500;
|
|
325
|
+
color: var(--text-primary);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.description {
|
|
329
|
+
color: var(--text-secondary);
|
|
330
|
+
font-size: 1rem;
|
|
331
|
+
line-height: 1.6;
|
|
332
|
+
margin-bottom: 2rem;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.links {
|
|
336
|
+
display: flex;
|
|
337
|
+
flex-wrap: wrap;
|
|
338
|
+
gap: 1rem;
|
|
339
|
+
justify-content: center;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.link {
|
|
343
|
+
display: inline-flex;
|
|
344
|
+
align-items: center;
|
|
345
|
+
gap: 0.5rem;
|
|
346
|
+
padding: 0.875rem 1.75rem;
|
|
347
|
+
background: var(--primary);
|
|
348
|
+
color: white;
|
|
349
|
+
text-decoration: none;
|
|
350
|
+
border-radius: 12px;
|
|
351
|
+
font-weight: 500;
|
|
352
|
+
font-size: 0.95rem;
|
|
353
|
+
transition: all 0.3s ease;
|
|
354
|
+
box-shadow: 0 4px 15px var(--primary-glow);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.link:hover {
|
|
358
|
+
transform: translateY(-2px);
|
|
359
|
+
box-shadow: 0 8px 25px var(--primary-glow);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.footer {
|
|
363
|
+
position: fixed;
|
|
364
|
+
bottom: 1.5rem;
|
|
365
|
+
left: 0;
|
|
366
|
+
right: 0;
|
|
367
|
+
text-align: center;
|
|
368
|
+
color: var(--text-secondary);
|
|
369
|
+
font-size: 0.85rem;
|
|
370
|
+
z-index: 10;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.footer a {
|
|
374
|
+
color: var(--primary);
|
|
375
|
+
text-decoration: none;
|
|
376
|
+
font-weight: 500;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.footer a:hover {
|
|
380
|
+
text-decoration: underline;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* Loading state */
|
|
384
|
+
.loading .status-dot {
|
|
385
|
+
background: var(--text-secondary);
|
|
386
|
+
box-shadow: none;
|
|
387
|
+
animation: none;
|
|
388
|
+
}
|
|
389
|
+
</style>
|
|
390
|
+
</head>
|
|
391
|
+
<body>
|
|
392
|
+
<div class="bg-gradient"></div>
|
|
393
|
+
<div class="particles">
|
|
394
|
+
<div class="particle"></div>
|
|
395
|
+
<div class="particle"></div>
|
|
396
|
+
<div class="particle"></div>
|
|
397
|
+
<div class="particle"></div>
|
|
398
|
+
<div class="particle"></div>
|
|
399
|
+
<div class="particle"></div>
|
|
400
|
+
<div class="particle"></div>
|
|
401
|
+
<div class="particle"></div>
|
|
402
|
+
<div class="particle"></div>
|
|
403
|
+
</div>
|
|
404
|
+
<div class="grid-overlay"></div>
|
|
405
|
+
|
|
406
|
+
<div class="container">
|
|
407
|
+
${logoUrl
|
|
408
|
+
? `<div class="logo custom"><img src="${logoUrl}" alt="${productName} logo" /></div>`
|
|
409
|
+
: `<div class="logo default">
|
|
410
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
411
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
|
412
|
+
</svg>
|
|
413
|
+
</div>`}
|
|
414
|
+
|
|
415
|
+
<h1>${productName}</h1>
|
|
416
|
+
|
|
417
|
+
<div class="status-badge loading" id="status-badge">
|
|
418
|
+
<div class="status-dot" id="status-dot"></div>
|
|
419
|
+
<span class="status-text" id="status-text">Checking status...</span>
|
|
420
|
+
</div>
|
|
421
|
+
|
|
422
|
+
<p class="description" id="description">
|
|
423
|
+
Enterprise-grade service powered by QwickApps
|
|
424
|
+
</p>
|
|
425
|
+
|
|
426
|
+
<div class="links">
|
|
427
|
+
<a href="${controlPanelPath}" class="link">
|
|
428
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
429
|
+
<rect x="3" y="3" width="7" height="7"></rect>
|
|
430
|
+
<rect x="14" y="3" width="7" height="7"></rect>
|
|
431
|
+
<rect x="14" y="14" width="7" height="7"></rect>
|
|
432
|
+
<rect x="3" y="14" width="7" height="7"></rect>
|
|
433
|
+
</svg>
|
|
434
|
+
Control Panel
|
|
435
|
+
</a>
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
|
|
439
|
+
<div class="footer">
|
|
440
|
+
Powered by <a href="https://qwickapps.com" target="_blank">QwickApps Server</a> - <a href="https://github.com/qwickapps/server" target="_blank">Version ${version}</a>
|
|
441
|
+
</div>
|
|
442
|
+
|
|
443
|
+
<script>
|
|
444
|
+
async function checkStatus() {
|
|
445
|
+
const badge = document.getElementById('status-badge');
|
|
446
|
+
const dot = document.getElementById('status-dot');
|
|
447
|
+
const text = document.getElementById('status-text');
|
|
448
|
+
const desc = document.getElementById('description');
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
const res = await fetch('${apiBasePath}/health');
|
|
452
|
+
const data = await res.json();
|
|
453
|
+
|
|
454
|
+
badge.classList.remove('loading');
|
|
455
|
+
|
|
456
|
+
if (data.status === 'healthy') {
|
|
457
|
+
dot.className = 'status-dot';
|
|
458
|
+
text.textContent = 'All systems operational';
|
|
459
|
+
desc.textContent = 'The service is running smoothly and ready to handle requests.';
|
|
460
|
+
} else if (data.status === 'degraded') {
|
|
461
|
+
dot.className = 'status-dot degraded';
|
|
462
|
+
text.textContent = 'Degraded performance';
|
|
463
|
+
desc.textContent = 'Some services may be experiencing issues. Core functionality remains available.';
|
|
464
|
+
} else {
|
|
465
|
+
dot.className = 'status-dot unhealthy';
|
|
466
|
+
text.textContent = 'System maintenance';
|
|
467
|
+
desc.textContent = 'The service is currently undergoing maintenance. Please check back shortly.';
|
|
468
|
+
}
|
|
469
|
+
} catch (e) {
|
|
470
|
+
badge.classList.remove('loading');
|
|
471
|
+
dot.className = 'status-dot unhealthy';
|
|
472
|
+
text.textContent = 'Unable to connect';
|
|
473
|
+
desc.textContent = 'Could not reach the service. Please try again later.';
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Check status on load and every 30 seconds
|
|
478
|
+
checkStatus();
|
|
479
|
+
setInterval(checkStatus, 30000);
|
|
480
|
+
</script>
|
|
481
|
+
</body>
|
|
482
|
+
</html>`;
|
|
483
|
+
}
|
|
116
484
|
/**
|
|
117
485
|
* Create a gateway that proxies to an internal service
|
|
118
486
|
*
|
|
@@ -161,13 +529,15 @@ export function createGateway(config, serviceFactory) {
|
|
|
161
529
|
const guardConfig = config.controlPanelGuard;
|
|
162
530
|
// API paths to proxy
|
|
163
531
|
const proxyPaths = config.proxyPaths || ['/api/v1'];
|
|
532
|
+
// Version for display
|
|
533
|
+
const version = config.version || process.env.npm_package_version || '1.0.0';
|
|
164
534
|
let service = null;
|
|
165
535
|
// Create control panel
|
|
166
536
|
const controlPanel = createControlPanel({
|
|
167
537
|
config: {
|
|
168
538
|
productName: config.productName,
|
|
169
539
|
port: gatewayPort,
|
|
170
|
-
version
|
|
540
|
+
version,
|
|
171
541
|
branding: config.branding,
|
|
172
542
|
cors: config.corsOrigins ? { origins: config.corsOrigins } : undefined,
|
|
173
543
|
// Skip body parsing for proxied paths
|
|
@@ -228,9 +598,17 @@ export function createGateway(config, serviceFactory) {
|
|
|
228
598
|
};
|
|
229
599
|
controlPanel.app.use(createProxyMiddleware(healthProxyOptions));
|
|
230
600
|
};
|
|
601
|
+
// Calculate API base path for landing page
|
|
602
|
+
const apiBasePath = controlPanelPath === '/' ? '/api' : `${controlPanelPath}/api`;
|
|
231
603
|
// Setup frontend app at root path
|
|
232
604
|
const setupFrontendApp = () => {
|
|
605
|
+
// If no frontend app configured, serve default landing page with status
|
|
233
606
|
if (!config.frontendApp) {
|
|
607
|
+
logger.info('Frontend app: Serving default landing page');
|
|
608
|
+
controlPanel.app.get('/', (_req, res) => {
|
|
609
|
+
const html = generateDefaultLandingPageHtml(config.productName, controlPanelPath, apiBasePath, version, config.logoUrl);
|
|
610
|
+
res.type('html').send(html);
|
|
611
|
+
});
|
|
234
612
|
return;
|
|
235
613
|
}
|
|
236
614
|
const { redirectUrl, staticPath, landingPage } = config.frontendApp;
|
|
@@ -273,8 +651,6 @@ export function createGateway(config, serviceFactory) {
|
|
|
273
651
|
setupFrontendApp();
|
|
274
652
|
// 4. Start control panel gateway
|
|
275
653
|
await controlPanel.start();
|
|
276
|
-
// Calculate API base path
|
|
277
|
-
const apiBasePath = controlPanelPath === '/' ? '/api' : `${controlPanelPath}/api`;
|
|
278
654
|
// Log startup info
|
|
279
655
|
logger.info(`${config.productName} Gateway`);
|
|
280
656
|
logger.info(`Gateway Port: ${gatewayPort} (public)`);
|
|
@@ -288,9 +664,7 @@ export function createGateway(config, serviceFactory) {
|
|
|
288
664
|
else {
|
|
289
665
|
logger.info('Control Panel Auth: None (not recommended)');
|
|
290
666
|
}
|
|
291
|
-
|
|
292
|
-
logger.info(`Frontend App: GET /`);
|
|
293
|
-
}
|
|
667
|
+
logger.info(`Frontend App: GET /`);
|
|
294
668
|
logger.info(`Control Panel UI: GET ${controlPanelPath.padEnd(20)}`);
|
|
295
669
|
logger.info(`Gateway Health: GET ${apiBasePath}/health`);
|
|
296
670
|
logger.info(`Service Health: GET /health`);
|
package/dist/core/gateway.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/core/gateway.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAsB,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAgB,MAAM,uBAAuB,CAAC;AAC5E,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/core/gateway.ts"],"names":[],"mappings":"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"}
|
package/dist/core/logging.js
CHANGED
|
@@ -18,7 +18,7 @@ import { resolve } from 'node:path';
|
|
|
18
18
|
import { getLogger } from '@qwickapps/logging';
|
|
19
19
|
// Default configuration
|
|
20
20
|
const DEFAULT_CONFIG = {
|
|
21
|
-
namespace: '
|
|
21
|
+
namespace: 'App',
|
|
22
22
|
level: process.env.LOG_LEVEL || (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
|
|
23
23
|
logDir: process.env.LOG_DIR || './logs',
|
|
24
24
|
fileLogging: process.env.LOG_FILE !== 'false',
|
package/dist/core/logging.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/core/logging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,OAAO,EAAW,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAyB,SAAS,EAA0B,MAAM,oBAAoB,CAAC;AAgB9F,wBAAwB;AACxB,MAAM,cAAc,GAA4B;IAC9C,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/core/logging.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/core/gateway.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface GatewayConfig {
|
|
|
43
43
|
/** Product version */
|
|
44
44
|
version?: string;
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* URL to the product logo image (SVG, PNG, etc.).
|
|
48
|
+
* Used on the default landing page when no frontend app is configured.
|
|
49
|
+
*/
|
|
50
|
+
logoUrl?: string;
|
|
51
|
+
|
|
46
52
|
/** Branding configuration */
|
|
47
53
|
branding?: ControlPanelConfig['branding'];
|
|
48
54
|
|
|
@@ -248,6 +254,381 @@ function generateLandingPageHtml(
|
|
|
248
254
|
</html>`;
|
|
249
255
|
}
|
|
250
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Generate default landing page HTML when no frontend app is configured
|
|
259
|
+
* Shows system status with animated background
|
|
260
|
+
*/
|
|
261
|
+
function generateDefaultLandingPageHtml(
|
|
262
|
+
productName: string,
|
|
263
|
+
controlPanelPath: string,
|
|
264
|
+
apiBasePath: string,
|
|
265
|
+
version: string,
|
|
266
|
+
logoUrl?: string
|
|
267
|
+
): string {
|
|
268
|
+
return `<!DOCTYPE html>
|
|
269
|
+
<html lang="en">
|
|
270
|
+
<head>
|
|
271
|
+
<meta charset="UTF-8">
|
|
272
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
273
|
+
<title>${productName}</title>
|
|
274
|
+
<style>
|
|
275
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
276
|
+
|
|
277
|
+
:root {
|
|
278
|
+
--primary: #6366f1;
|
|
279
|
+
--primary-glow: rgba(99, 102, 241, 0.4);
|
|
280
|
+
--success: #22c55e;
|
|
281
|
+
--warning: #f59e0b;
|
|
282
|
+
--error: #ef4444;
|
|
283
|
+
--bg-dark: #0a0a0f;
|
|
284
|
+
--bg-card: rgba(255, 255, 255, 0.03);
|
|
285
|
+
--text-primary: #f1f5f9;
|
|
286
|
+
--text-secondary: #94a3b8;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
body {
|
|
290
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
291
|
+
background: var(--bg-dark);
|
|
292
|
+
color: var(--text-primary);
|
|
293
|
+
min-height: 100vh;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
display: flex;
|
|
296
|
+
align-items: center;
|
|
297
|
+
justify-content: center;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Animated gradient background */
|
|
301
|
+
.bg-gradient {
|
|
302
|
+
position: fixed;
|
|
303
|
+
top: 0;
|
|
304
|
+
left: 0;
|
|
305
|
+
right: 0;
|
|
306
|
+
bottom: 0;
|
|
307
|
+
background:
|
|
308
|
+
radial-gradient(ellipse at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
|
|
309
|
+
radial-gradient(ellipse at 80% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 50%),
|
|
310
|
+
radial-gradient(ellipse at 50% 50%, rgba(59, 130, 246, 0.05) 0%, transparent 70%);
|
|
311
|
+
animation: gradientShift 15s ease-in-out infinite;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@keyframes gradientShift {
|
|
315
|
+
0%, 100% {
|
|
316
|
+
background-position: 0% 0%, 100% 100%, 50% 50%;
|
|
317
|
+
opacity: 1;
|
|
318
|
+
}
|
|
319
|
+
50% {
|
|
320
|
+
background-position: 100% 0%, 0% 100%, 50% 50%;
|
|
321
|
+
opacity: 0.8;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* Floating particles */
|
|
326
|
+
.particles {
|
|
327
|
+
position: fixed;
|
|
328
|
+
top: 0;
|
|
329
|
+
left: 0;
|
|
330
|
+
right: 0;
|
|
331
|
+
bottom: 0;
|
|
332
|
+
overflow: hidden;
|
|
333
|
+
pointer-events: none;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.particle {
|
|
337
|
+
position: absolute;
|
|
338
|
+
width: 4px;
|
|
339
|
+
height: 4px;
|
|
340
|
+
background: var(--primary);
|
|
341
|
+
border-radius: 50%;
|
|
342
|
+
opacity: 0.3;
|
|
343
|
+
animation: float 20s infinite;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.particle:nth-child(1) { left: 10%; animation-delay: 0s; animation-duration: 25s; }
|
|
347
|
+
.particle:nth-child(2) { left: 20%; animation-delay: 2s; animation-duration: 20s; }
|
|
348
|
+
.particle:nth-child(3) { left: 30%; animation-delay: 4s; animation-duration: 28s; }
|
|
349
|
+
.particle:nth-child(4) { left: 40%; animation-delay: 1s; animation-duration: 22s; }
|
|
350
|
+
.particle:nth-child(5) { left: 50%; animation-delay: 3s; animation-duration: 24s; }
|
|
351
|
+
.particle:nth-child(6) { left: 60%; animation-delay: 5s; animation-duration: 26s; }
|
|
352
|
+
.particle:nth-child(7) { left: 70%; animation-delay: 2s; animation-duration: 21s; }
|
|
353
|
+
.particle:nth-child(8) { left: 80%; animation-delay: 4s; animation-duration: 23s; }
|
|
354
|
+
.particle:nth-child(9) { left: 90%; animation-delay: 1s; animation-duration: 27s; }
|
|
355
|
+
|
|
356
|
+
@keyframes float {
|
|
357
|
+
0% { transform: translateY(100vh) scale(0); opacity: 0; }
|
|
358
|
+
10% { opacity: 0.3; }
|
|
359
|
+
90% { opacity: 0.3; }
|
|
360
|
+
100% { transform: translateY(-100vh) scale(1); opacity: 0; }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* Grid pattern overlay */
|
|
364
|
+
.grid-overlay {
|
|
365
|
+
position: fixed;
|
|
366
|
+
top: 0;
|
|
367
|
+
left: 0;
|
|
368
|
+
right: 0;
|
|
369
|
+
bottom: 0;
|
|
370
|
+
background-image:
|
|
371
|
+
linear-gradient(rgba(99, 102, 241, 0.03) 1px, transparent 1px),
|
|
372
|
+
linear-gradient(90deg, rgba(99, 102, 241, 0.03) 1px, transparent 1px);
|
|
373
|
+
background-size: 60px 60px;
|
|
374
|
+
pointer-events: none;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.container {
|
|
378
|
+
position: relative;
|
|
379
|
+
z-index: 10;
|
|
380
|
+
text-align: center;
|
|
381
|
+
max-width: 500px;
|
|
382
|
+
padding: 3rem 2rem;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.logo {
|
|
386
|
+
width: 80px;
|
|
387
|
+
height: 80px;
|
|
388
|
+
margin: 0 auto 2rem;
|
|
389
|
+
display: flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
justify-content: center;
|
|
392
|
+
animation: logoFloat 6s ease-in-out infinite;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.logo.default {
|
|
396
|
+
background: linear-gradient(135deg, var(--primary) 0%, #8b5cf6 100%);
|
|
397
|
+
border-radius: 20px;
|
|
398
|
+
box-shadow: 0 20px 40px var(--primary-glow);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.logo.custom {
|
|
402
|
+
filter: drop-shadow(0 20px 40px var(--primary-glow));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@keyframes logoFloat {
|
|
406
|
+
0%, 100% { transform: translateY(0); }
|
|
407
|
+
50% { transform: translateY(-10px); }
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.logo svg {
|
|
411
|
+
width: 48px;
|
|
412
|
+
height: 48px;
|
|
413
|
+
fill: white;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.logo img {
|
|
417
|
+
width: 80px;
|
|
418
|
+
height: 80px;
|
|
419
|
+
object-fit: contain;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
h1 {
|
|
423
|
+
font-size: 2.5rem;
|
|
424
|
+
font-weight: 700;
|
|
425
|
+
margin-bottom: 0.5rem;
|
|
426
|
+
background: linear-gradient(135deg, var(--text-primary) 0%, var(--primary) 100%);
|
|
427
|
+
-webkit-background-clip: text;
|
|
428
|
+
-webkit-text-fill-color: transparent;
|
|
429
|
+
background-clip: text;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.status-badge {
|
|
433
|
+
display: inline-flex;
|
|
434
|
+
align-items: center;
|
|
435
|
+
gap: 0.5rem;
|
|
436
|
+
padding: 0.75rem 1.5rem;
|
|
437
|
+
background: var(--bg-card);
|
|
438
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
439
|
+
border-radius: 100px;
|
|
440
|
+
margin: 1.5rem 0 2rem;
|
|
441
|
+
backdrop-filter: blur(10px);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.status-dot {
|
|
445
|
+
width: 10px;
|
|
446
|
+
height: 10px;
|
|
447
|
+
border-radius: 50%;
|
|
448
|
+
background: var(--success);
|
|
449
|
+
box-shadow: 0 0 10px var(--success);
|
|
450
|
+
animation: pulse 2s ease-in-out infinite;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.status-dot.degraded {
|
|
454
|
+
background: var(--warning);
|
|
455
|
+
box-shadow: 0 0 10px var(--warning);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.status-dot.unhealthy {
|
|
459
|
+
background: var(--error);
|
|
460
|
+
box-shadow: 0 0 10px var(--error);
|
|
461
|
+
animation: none;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@keyframes pulse {
|
|
465
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
466
|
+
50% { opacity: 0.7; transform: scale(1.1); }
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.status-text {
|
|
470
|
+
font-size: 0.95rem;
|
|
471
|
+
font-weight: 500;
|
|
472
|
+
color: var(--text-primary);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.description {
|
|
476
|
+
color: var(--text-secondary);
|
|
477
|
+
font-size: 1rem;
|
|
478
|
+
line-height: 1.6;
|
|
479
|
+
margin-bottom: 2rem;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.links {
|
|
483
|
+
display: flex;
|
|
484
|
+
flex-wrap: wrap;
|
|
485
|
+
gap: 1rem;
|
|
486
|
+
justify-content: center;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.link {
|
|
490
|
+
display: inline-flex;
|
|
491
|
+
align-items: center;
|
|
492
|
+
gap: 0.5rem;
|
|
493
|
+
padding: 0.875rem 1.75rem;
|
|
494
|
+
background: var(--primary);
|
|
495
|
+
color: white;
|
|
496
|
+
text-decoration: none;
|
|
497
|
+
border-radius: 12px;
|
|
498
|
+
font-weight: 500;
|
|
499
|
+
font-size: 0.95rem;
|
|
500
|
+
transition: all 0.3s ease;
|
|
501
|
+
box-shadow: 0 4px 15px var(--primary-glow);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.link:hover {
|
|
505
|
+
transform: translateY(-2px);
|
|
506
|
+
box-shadow: 0 8px 25px var(--primary-glow);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.footer {
|
|
510
|
+
position: fixed;
|
|
511
|
+
bottom: 1.5rem;
|
|
512
|
+
left: 0;
|
|
513
|
+
right: 0;
|
|
514
|
+
text-align: center;
|
|
515
|
+
color: var(--text-secondary);
|
|
516
|
+
font-size: 0.85rem;
|
|
517
|
+
z-index: 10;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.footer a {
|
|
521
|
+
color: var(--primary);
|
|
522
|
+
text-decoration: none;
|
|
523
|
+
font-weight: 500;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.footer a:hover {
|
|
527
|
+
text-decoration: underline;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Loading state */
|
|
531
|
+
.loading .status-dot {
|
|
532
|
+
background: var(--text-secondary);
|
|
533
|
+
box-shadow: none;
|
|
534
|
+
animation: none;
|
|
535
|
+
}
|
|
536
|
+
</style>
|
|
537
|
+
</head>
|
|
538
|
+
<body>
|
|
539
|
+
<div class="bg-gradient"></div>
|
|
540
|
+
<div class="particles">
|
|
541
|
+
<div class="particle"></div>
|
|
542
|
+
<div class="particle"></div>
|
|
543
|
+
<div class="particle"></div>
|
|
544
|
+
<div class="particle"></div>
|
|
545
|
+
<div class="particle"></div>
|
|
546
|
+
<div class="particle"></div>
|
|
547
|
+
<div class="particle"></div>
|
|
548
|
+
<div class="particle"></div>
|
|
549
|
+
<div class="particle"></div>
|
|
550
|
+
</div>
|
|
551
|
+
<div class="grid-overlay"></div>
|
|
552
|
+
|
|
553
|
+
<div class="container">
|
|
554
|
+
${logoUrl
|
|
555
|
+
? `<div class="logo custom"><img src="${logoUrl}" alt="${productName} logo" /></div>`
|
|
556
|
+
: `<div class="logo default">
|
|
557
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
558
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
|
559
|
+
</svg>
|
|
560
|
+
</div>`}
|
|
561
|
+
|
|
562
|
+
<h1>${productName}</h1>
|
|
563
|
+
|
|
564
|
+
<div class="status-badge loading" id="status-badge">
|
|
565
|
+
<div class="status-dot" id="status-dot"></div>
|
|
566
|
+
<span class="status-text" id="status-text">Checking status...</span>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
<p class="description" id="description">
|
|
570
|
+
Enterprise-grade service powered by QwickApps
|
|
571
|
+
</p>
|
|
572
|
+
|
|
573
|
+
<div class="links">
|
|
574
|
+
<a href="${controlPanelPath}" class="link">
|
|
575
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
576
|
+
<rect x="3" y="3" width="7" height="7"></rect>
|
|
577
|
+
<rect x="14" y="3" width="7" height="7"></rect>
|
|
578
|
+
<rect x="14" y="14" width="7" height="7"></rect>
|
|
579
|
+
<rect x="3" y="14" width="7" height="7"></rect>
|
|
580
|
+
</svg>
|
|
581
|
+
Control Panel
|
|
582
|
+
</a>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
|
|
586
|
+
<div class="footer">
|
|
587
|
+
Powered by <a href="https://qwickapps.com" target="_blank">QwickApps Server</a> - <a href="https://github.com/qwickapps/server" target="_blank">Version ${version}</a>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
<script>
|
|
591
|
+
async function checkStatus() {
|
|
592
|
+
const badge = document.getElementById('status-badge');
|
|
593
|
+
const dot = document.getElementById('status-dot');
|
|
594
|
+
const text = document.getElementById('status-text');
|
|
595
|
+
const desc = document.getElementById('description');
|
|
596
|
+
|
|
597
|
+
try {
|
|
598
|
+
const res = await fetch('${apiBasePath}/health');
|
|
599
|
+
const data = await res.json();
|
|
600
|
+
|
|
601
|
+
badge.classList.remove('loading');
|
|
602
|
+
|
|
603
|
+
if (data.status === 'healthy') {
|
|
604
|
+
dot.className = 'status-dot';
|
|
605
|
+
text.textContent = 'All systems operational';
|
|
606
|
+
desc.textContent = 'The service is running smoothly and ready to handle requests.';
|
|
607
|
+
} else if (data.status === 'degraded') {
|
|
608
|
+
dot.className = 'status-dot degraded';
|
|
609
|
+
text.textContent = 'Degraded performance';
|
|
610
|
+
desc.textContent = 'Some services may be experiencing issues. Core functionality remains available.';
|
|
611
|
+
} else {
|
|
612
|
+
dot.className = 'status-dot unhealthy';
|
|
613
|
+
text.textContent = 'System maintenance';
|
|
614
|
+
desc.textContent = 'The service is currently undergoing maintenance. Please check back shortly.';
|
|
615
|
+
}
|
|
616
|
+
} catch (e) {
|
|
617
|
+
badge.classList.remove('loading');
|
|
618
|
+
dot.className = 'status-dot unhealthy';
|
|
619
|
+
text.textContent = 'Unable to connect';
|
|
620
|
+
desc.textContent = 'Could not reach the service. Please try again later.';
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Check status on load and every 30 seconds
|
|
625
|
+
checkStatus();
|
|
626
|
+
setInterval(checkStatus, 30000);
|
|
627
|
+
</script>
|
|
628
|
+
</body>
|
|
629
|
+
</html>`;
|
|
630
|
+
}
|
|
631
|
+
|
|
251
632
|
/**
|
|
252
633
|
* Create a gateway that proxies to an internal service
|
|
253
634
|
*
|
|
@@ -305,6 +686,9 @@ export function createGateway(
|
|
|
305
686
|
// API paths to proxy
|
|
306
687
|
const proxyPaths = config.proxyPaths || ['/api/v1'];
|
|
307
688
|
|
|
689
|
+
// Version for display
|
|
690
|
+
const version = config.version || process.env.npm_package_version || '1.0.0';
|
|
691
|
+
|
|
308
692
|
let service: GatewayInstance['service'] = null;
|
|
309
693
|
|
|
310
694
|
// Create control panel
|
|
@@ -312,7 +696,7 @@ export function createGateway(
|
|
|
312
696
|
config: {
|
|
313
697
|
productName: config.productName,
|
|
314
698
|
port: gatewayPort,
|
|
315
|
-
version
|
|
699
|
+
version,
|
|
316
700
|
branding: config.branding,
|
|
317
701
|
cors: config.corsOrigins ? { origins: config.corsOrigins } : undefined,
|
|
318
702
|
// Skip body parsing for proxied paths
|
|
@@ -381,9 +765,24 @@ export function createGateway(
|
|
|
381
765
|
controlPanel.app.use(createProxyMiddleware(healthProxyOptions));
|
|
382
766
|
};
|
|
383
767
|
|
|
768
|
+
// Calculate API base path for landing page
|
|
769
|
+
const apiBasePath = controlPanelPath === '/' ? '/api' : `${controlPanelPath}/api`;
|
|
770
|
+
|
|
384
771
|
// Setup frontend app at root path
|
|
385
772
|
const setupFrontendApp = () => {
|
|
773
|
+
// If no frontend app configured, serve default landing page with status
|
|
386
774
|
if (!config.frontendApp) {
|
|
775
|
+
logger.info('Frontend app: Serving default landing page');
|
|
776
|
+
controlPanel.app.get('/', (_req, res) => {
|
|
777
|
+
const html = generateDefaultLandingPageHtml(
|
|
778
|
+
config.productName,
|
|
779
|
+
controlPanelPath,
|
|
780
|
+
apiBasePath,
|
|
781
|
+
version,
|
|
782
|
+
config.logoUrl
|
|
783
|
+
);
|
|
784
|
+
res.type('html').send(html);
|
|
785
|
+
});
|
|
387
786
|
return;
|
|
388
787
|
}
|
|
389
788
|
|
|
@@ -437,9 +836,6 @@ export function createGateway(
|
|
|
437
836
|
// 4. Start control panel gateway
|
|
438
837
|
await controlPanel.start();
|
|
439
838
|
|
|
440
|
-
// Calculate API base path
|
|
441
|
-
const apiBasePath = controlPanelPath === '/' ? '/api' : `${controlPanelPath}/api`;
|
|
442
|
-
|
|
443
839
|
// Log startup info
|
|
444
840
|
logger.info(`${config.productName} Gateway`);
|
|
445
841
|
logger.info(`Gateway Port: ${gatewayPort} (public)`);
|
|
@@ -453,9 +849,7 @@ export function createGateway(
|
|
|
453
849
|
logger.info('Control Panel Auth: None (not recommended)');
|
|
454
850
|
}
|
|
455
851
|
|
|
456
|
-
|
|
457
|
-
logger.info(`Frontend App: GET /`);
|
|
458
|
-
}
|
|
852
|
+
logger.info(`Frontend App: GET /`);
|
|
459
853
|
logger.info(`Control Panel UI: GET ${controlPanelPath.padEnd(20)}`);
|
|
460
854
|
logger.info(`Gateway Health: GET ${apiBasePath}/health`);
|
|
461
855
|
logger.info(`Service Health: GET /health`);
|
package/src/core/logging.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface LoggingConfig {
|
|
|
34
34
|
|
|
35
35
|
// Default configuration
|
|
36
36
|
const DEFAULT_CONFIG: Required<LoggingConfig> = {
|
|
37
|
-
namespace: '
|
|
37
|
+
namespace: 'App',
|
|
38
38
|
level: (process.env.LOG_LEVEL as LogLevel) || (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
|
|
39
39
|
logDir: process.env.LOG_DIR || './logs',
|
|
40
40
|
fileLogging: process.env.LOG_FILE !== 'false',
|