dynamic-self-register-proxy 1.0.4 → 1.0.5
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/package.json +1 -1
- package/proxy.js +134 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dynamic-self-register-proxy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Dynamic reverse proxy with self-registration API - applications can register themselves and receive an automatically assigned port",
|
|
5
5
|
"main": "proxy-client.js",
|
|
6
6
|
"bin": {
|
package/proxy.js
CHANGED
|
@@ -9,7 +9,7 @@ app.use(express.json());
|
|
|
9
9
|
// ============================================
|
|
10
10
|
// CONFIGURATION
|
|
11
11
|
// ============================================
|
|
12
|
-
const PROXY_PORT = process.env.PROXY_PORT ||
|
|
12
|
+
const PROXY_PORT = process.env.PROXY_PORT || 3000;
|
|
13
13
|
const INTERNAL_PORT_START = 4000;
|
|
14
14
|
const INTERNAL_PORT_END = 5000;
|
|
15
15
|
const HEALTH_CHECK_INTERVAL = process.env.HEALTH_CHECK_INTERVAL || 30000; // 30 secondes par défaut
|
|
@@ -413,6 +413,52 @@ app.get('/proxy/routes', (req, res) => {
|
|
|
413
413
|
});
|
|
414
414
|
});
|
|
415
415
|
|
|
416
|
+
/**
|
|
417
|
+
* POST /proxy/check
|
|
418
|
+
* Déclenche manuellement un health check pour une route spécifique
|
|
419
|
+
* Body: { path: "/myapp" }
|
|
420
|
+
*/
|
|
421
|
+
app.post('/proxy/check', async (req, res) => {
|
|
422
|
+
const { path } = req.body;
|
|
423
|
+
|
|
424
|
+
if (!path) {
|
|
425
|
+
return res.status(400).json({
|
|
426
|
+
success: false,
|
|
427
|
+
error: 'Path is required'
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
432
|
+
const route = registry.routes.get(normalizedPath);
|
|
433
|
+
|
|
434
|
+
if (!route) {
|
|
435
|
+
return res.status(404).json({
|
|
436
|
+
success: false,
|
|
437
|
+
error: 'Path not found'
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
console.log(`[MANUAL HEALTH CHECK] Checking ${normalizedPath}...`);
|
|
442
|
+
const isHealthy = await checkServerHealth(normalizedPath, route);
|
|
443
|
+
|
|
444
|
+
if (!isHealthy) {
|
|
445
|
+
await unregisterServer(normalizedPath);
|
|
446
|
+
return res.json({
|
|
447
|
+
success: true,
|
|
448
|
+
path: normalizedPath,
|
|
449
|
+
healthy: false,
|
|
450
|
+
message: 'Server was unhealthy and has been removed'
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
res.json({
|
|
455
|
+
success: true,
|
|
456
|
+
path: normalizedPath,
|
|
457
|
+
healthy: true,
|
|
458
|
+
message: 'Server is healthy'
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
416
462
|
/**
|
|
417
463
|
* GET /proxy/health
|
|
418
464
|
* Health check du proxy
|
|
@@ -581,6 +627,40 @@ app.get('/', (req, res) => {
|
|
|
581
627
|
.service-link:hover {
|
|
582
628
|
opacity: 0.9;
|
|
583
629
|
}
|
|
630
|
+
.service-actions {
|
|
631
|
+
display: flex;
|
|
632
|
+
gap: 1rem;
|
|
633
|
+
align-items: center;
|
|
634
|
+
}
|
|
635
|
+
.check-btn {
|
|
636
|
+
background: rgba(255, 255, 255, 0.1);
|
|
637
|
+
color: #e4e4e7;
|
|
638
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
639
|
+
padding: 0.5rem 1rem;
|
|
640
|
+
border-radius: 8px;
|
|
641
|
+
cursor: pointer;
|
|
642
|
+
font-size: 0.85rem;
|
|
643
|
+
font-weight: 500;
|
|
644
|
+
transition: all 0.2s;
|
|
645
|
+
}
|
|
646
|
+
.check-btn:hover {
|
|
647
|
+
background: rgba(255, 255, 255, 0.15);
|
|
648
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
649
|
+
}
|
|
650
|
+
.check-btn.loading {
|
|
651
|
+
opacity: 0.5;
|
|
652
|
+
cursor: not-allowed;
|
|
653
|
+
}
|
|
654
|
+
.check-btn.healthy {
|
|
655
|
+
background: rgba(16, 185, 129, 0.2);
|
|
656
|
+
border-color: rgba(16, 185, 129, 0.4);
|
|
657
|
+
color: #10b981;
|
|
658
|
+
}
|
|
659
|
+
.check-btn.unhealthy {
|
|
660
|
+
background: rgba(239, 68, 68, 0.2);
|
|
661
|
+
border-color: rgba(239, 68, 68, 0.4);
|
|
662
|
+
color: #ef4444;
|
|
663
|
+
}
|
|
584
664
|
.empty-state {
|
|
585
665
|
text-align: center;
|
|
586
666
|
padding: 4rem 2rem;
|
|
@@ -648,9 +728,14 @@ app.get('/', (req, res) => {
|
|
|
648
728
|
<span>Port interne: ${route.port}</span>
|
|
649
729
|
<span>Enregistré: ${new Date(route.registeredAt).toLocaleString('fr-FR')}</span>
|
|
650
730
|
</div>
|
|
651
|
-
<
|
|
652
|
-
|
|
653
|
-
|
|
731
|
+
<div class="service-actions">
|
|
732
|
+
<a href="${escapeHtml(route.path)}" class="service-link">
|
|
733
|
+
Accéder au service →
|
|
734
|
+
</a>
|
|
735
|
+
<button onclick="checkHealth('${escapeHtml(route.path)}', this)" class="check-btn">
|
|
736
|
+
Vérifier la santé
|
|
737
|
+
</button>
|
|
738
|
+
</div>
|
|
654
739
|
</div>
|
|
655
740
|
`).join('')}
|
|
656
741
|
</div>
|
|
@@ -666,6 +751,51 @@ app.get('/', (req, res) => {
|
|
|
666
751
|
<p>API: <a href="/proxy/routes">/proxy/routes</a> | <a href="/proxy/health">/proxy/health</a></p>
|
|
667
752
|
</footer>
|
|
668
753
|
</div>
|
|
754
|
+
|
|
755
|
+
<script>
|
|
756
|
+
async function checkHealth(path, btn) {
|
|
757
|
+
if (btn.classList.contains('loading')) return;
|
|
758
|
+
|
|
759
|
+
const originalText = btn.innerText;
|
|
760
|
+
btn.innerText = 'Vérification...';
|
|
761
|
+
btn.classList.add('loading');
|
|
762
|
+
|
|
763
|
+
try {
|
|
764
|
+
const response = await fetch('/proxy/check', {
|
|
765
|
+
method: 'POST',
|
|
766
|
+
headers: { 'Content-Type': 'application/json' },
|
|
767
|
+
body: JSON.stringify({ path })
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
const data = await response.json();
|
|
771
|
+
|
|
772
|
+
if (data.healthy) {
|
|
773
|
+
btn.innerText = '✅ Sain';
|
|
774
|
+
btn.classList.remove('loading');
|
|
775
|
+
btn.classList.add('healthy');
|
|
776
|
+
setTimeout(() => {
|
|
777
|
+
btn.innerText = originalText;
|
|
778
|
+
btn.classList.remove('healthy');
|
|
779
|
+
}, 3000);
|
|
780
|
+
} else {
|
|
781
|
+
btn.innerText = '❌ Hors ligne';
|
|
782
|
+
btn.classList.remove('loading');
|
|
783
|
+
btn.classList.add('unhealthy');
|
|
784
|
+
|
|
785
|
+
// Si le serveur a été supprimé, on rafraîchit la liste après un court délai
|
|
786
|
+
setTimeout(() => {
|
|
787
|
+
window.location.reload();
|
|
788
|
+
}, 2000);
|
|
789
|
+
}
|
|
790
|
+
} catch (error) {
|
|
791
|
+
btn.innerText = '⚠️ Erreur';
|
|
792
|
+
btn.classList.remove('loading');
|
|
793
|
+
setTimeout(() => {
|
|
794
|
+
btn.innerText = originalText;
|
|
795
|
+
}, 3000);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
</script>
|
|
669
799
|
</body>
|
|
670
800
|
</html>
|
|
671
801
|
`.trim();
|