@imenam/self-register-proxy 1.0.37 → 1.0.38
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 +13 -1
- package/config.js +3 -0
- package/package.json +1 -1
- package/routes/dashboard.js +2 -2
- package/views/dashboard.js +110 -1
package/README.md
CHANGED
|
@@ -41,6 +41,18 @@ npm start
|
|
|
41
41
|
|
|
42
42
|
Le proxy démarre par défaut sur le port `3000`.
|
|
43
43
|
|
|
44
|
+
#### Interface HTML minimale (`--minimal-dashboard`)
|
|
45
|
+
|
|
46
|
+
Par défaut, la page d’accueil en HTML affiche l’interface complète (statistiques, barre d’outils, actions sur les services, etc.). Le flag **`--minimal-dashboard`** réduit cette page au **nom du proxy** (`PROXY_NAME`) et, pour chaque service enregistré, au **nom** et au bouton **Ouvrir** uniquement — sans URL détaillée, sans port visible, sans barre d’outils ni modale d’ajout. Il n’existe pas d’équivalent via variable d’environnement ; seul ce flag CLI est pris en charge.
|
|
47
|
+
|
|
48
|
+
Avec **`npx`**, placez **`--`** avant le flag pour qu’il soit transmis au script Node :
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx @imenam/self-register-proxy -- --minimal-dashboard
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Les réponses **JSON** de `GET /` lorsque le client envoie `Accept: application/json` restent **inchangées** (le mode minimal ne concerne que le HTML).
|
|
55
|
+
|
|
44
56
|
### Configuration via fichier .env
|
|
45
57
|
|
|
46
58
|
Créez un fichier `.env` à la racine du projet (voir `.env.example`) :
|
|
@@ -91,7 +103,7 @@ http://localhost:3000/myapp/ → votre application sur le port 4000
|
|
|
91
103
|
|
|
92
104
|
L'URL racine `/` affiche une page listant tous les serveurs enregistrés avec des liens pour y accéder.
|
|
93
105
|
|
|
94
|
-
Le nom du proxy (configurable via `PROXY_NAME`) est affiché dans le titre et l'en-tête de la page.
|
|
106
|
+
Le nom du proxy (configurable via `PROXY_NAME`) est affiché dans le titre et l'en-tête de la page. Pour une variante HTML épurée (titre + noms + bouton « Ouvrir » uniquement), démarrez le proxy avec `--minimal-dashboard` (voir **Démarrage rapide**).
|
|
95
107
|
|
|
96
108
|
- **Navigateurs** (`Accept: text/html`) → Page HTML avec interface moderne
|
|
97
109
|
- **Clients API** (`Accept: application/json`) → Réponse JSON
|
package/config.js
CHANGED
|
@@ -15,6 +15,8 @@ const TERMINAL_CWD = process.env.PROXY_TERMINAL_CWD || process.env.HOME || proce
|
|
|
15
15
|
const TERMINAL_COMMAND = process.env.PROXY_TERMINAL_COMMAND || '';
|
|
16
16
|
const OPENCODE_CLEAN_UP = process.env.PROXY_OPENCODE_CLEAN_UP || '';
|
|
17
17
|
|
|
18
|
+
const MINIMAL_DASHBOARD = process.argv.includes('--minimal-dashboard');
|
|
19
|
+
|
|
18
20
|
module.exports = {
|
|
19
21
|
PROXY_PORT,
|
|
20
22
|
PROXY_NAME,
|
|
@@ -28,4 +30,5 @@ module.exports = {
|
|
|
28
30
|
TERMINAL_CWD,
|
|
29
31
|
TERMINAL_COMMAND,
|
|
30
32
|
OPENCODE_CLEAN_UP,
|
|
33
|
+
MINIMAL_DASHBOARD,
|
|
31
34
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imenam/self-register-proxy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
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/routes/dashboard.js
CHANGED
|
@@ -5,7 +5,7 @@ const { safeEqual, createSession, setSessionCookie, destroySession, clearSession
|
|
|
5
5
|
const { registry } = require('../services/registry');
|
|
6
6
|
const { renderLogin } = require('../views/login');
|
|
7
7
|
const { renderDashboard } = require('../views/dashboard');
|
|
8
|
-
const { PROXY_NAME, PROXY_PORT, ADMIN_PASSWORD, INTERNAL_PORT_START, INTERNAL_PORT_END, OPENCODE_CLEAN_UP, TERMINAL_CWD } = require('../config');
|
|
8
|
+
const { PROXY_NAME, PROXY_PORT, ADMIN_PASSWORD, INTERNAL_PORT_START, INTERNAL_PORT_END, OPENCODE_CLEAN_UP, TERMINAL_CWD, MINIMAL_DASHBOARD } = require('../config');
|
|
9
9
|
|
|
10
10
|
const router = Router();
|
|
11
11
|
|
|
@@ -100,7 +100,7 @@ router.get('/', requireAuth, (req, res) => {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
const availablePorts = (INTERNAL_PORT_END - INTERNAL_PORT_START) - registry.usedPorts.size;
|
|
103
|
-
const html = renderDashboard(routes, availablePorts, PROXY_NAME, !!ADMIN_PASSWORD, OPENCODE_CLEAN_UP);
|
|
103
|
+
const html = renderDashboard(routes, availablePorts, PROXY_NAME, !!ADMIN_PASSWORD, OPENCODE_CLEAN_UP, MINIMAL_DASHBOARD);
|
|
104
104
|
res.type('html').send(html);
|
|
105
105
|
});
|
|
106
106
|
|
package/views/dashboard.js
CHANGED
|
@@ -7,8 +7,117 @@ const { escapeHtml } = require('../services/proxyRouter');
|
|
|
7
7
|
* @param {string} proxyName
|
|
8
8
|
* @param {boolean} hasAuth - true si l'authentification est activée
|
|
9
9
|
* @param {string|undefined} cleanUpCommand - si défini, affiche le bouton de nettoyage
|
|
10
|
+
* @param {boolean} [minimalDashboard] - interface HTML réduite (nom + Ouvrir uniquement)
|
|
10
11
|
*/
|
|
11
|
-
function renderDashboard(routes, availablePorts, proxyName, hasAuth, cleanUpCommand) {
|
|
12
|
+
function renderDashboard(routes, availablePorts, proxyName, hasAuth, cleanUpCommand, minimalDashboard) {
|
|
13
|
+
if (minimalDashboard) {
|
|
14
|
+
const servicesHtml = routes.length > 0
|
|
15
|
+
? `<div class="services-list">
|
|
16
|
+
${routes.map(route => `
|
|
17
|
+
<div class="service-card">
|
|
18
|
+
<div class="service-info">
|
|
19
|
+
<span class="service-name">${escapeHtml(route.name)}</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="service-actions">
|
|
22
|
+
<a href="${escapeHtml(route.path)}" class="action-btn primary" title="Accéder">Ouvrir</a>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
`).join('')}
|
|
26
|
+
</div>`
|
|
27
|
+
: `<div class="empty-state minimal-empty">
|
|
28
|
+
<p>Aucun service</p>
|
|
29
|
+
</div>`;
|
|
30
|
+
|
|
31
|
+
return `<!DOCTYPE html>
|
|
32
|
+
<html lang="fr">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8">
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
36
|
+
<title>${escapeHtml(proxyName)}</title>
|
|
37
|
+
<style>
|
|
38
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
39
|
+
body.minimal-dashboard {
|
|
40
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
41
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
42
|
+
min-height: 100vh;
|
|
43
|
+
color: #e4e4e7;
|
|
44
|
+
padding: 1rem;
|
|
45
|
+
}
|
|
46
|
+
.minimal-dashboard .container { max-width: 560px; margin: 0 auto; }
|
|
47
|
+
.minimal-dashboard header { text-align: center; margin-bottom: 1rem; }
|
|
48
|
+
.minimal-dashboard h1 {
|
|
49
|
+
font-size: 1.75rem;
|
|
50
|
+
background: linear-gradient(90deg, #6366f1, #8b5cf6);
|
|
51
|
+
-webkit-background-clip: text;
|
|
52
|
+
-webkit-text-fill-color: transparent;
|
|
53
|
+
background-clip: text;
|
|
54
|
+
}
|
|
55
|
+
.services-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
56
|
+
.service-card {
|
|
57
|
+
background: rgba(255, 255, 255, 0.05);
|
|
58
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
59
|
+
border-radius: 8px;
|
|
60
|
+
padding: 0.6rem 0.9rem;
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
gap: 0.75rem;
|
|
65
|
+
transition: all 0.15s ease;
|
|
66
|
+
}
|
|
67
|
+
.service-card:hover {
|
|
68
|
+
background: rgba(255, 255, 255, 0.08);
|
|
69
|
+
border-color: rgba(139, 92, 246, 0.4);
|
|
70
|
+
}
|
|
71
|
+
.service-info { display: flex; align-items: center; min-width: 0; flex: 1; }
|
|
72
|
+
.service-name {
|
|
73
|
+
font-size: 0.95rem;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
color: #f4f4f5;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
overflow: hidden;
|
|
78
|
+
text-overflow: ellipsis;
|
|
79
|
+
}
|
|
80
|
+
.service-actions { display: flex; flex-shrink: 0; align-items: center; }
|
|
81
|
+
.action-btn {
|
|
82
|
+
background: rgba(255, 255, 255, 0.1);
|
|
83
|
+
color: #e4e4e7;
|
|
84
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
85
|
+
padding: 0.35rem 0.6rem;
|
|
86
|
+
border-radius: 5px;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
font-size: 0.75rem;
|
|
89
|
+
font-weight: 500;
|
|
90
|
+
text-decoration: none;
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
}
|
|
94
|
+
.action-btn.primary {
|
|
95
|
+
background: linear-gradient(90deg, #6366f1, #8b5cf6);
|
|
96
|
+
border: none;
|
|
97
|
+
color: white;
|
|
98
|
+
}
|
|
99
|
+
.action-btn.primary:hover { opacity: 0.9; }
|
|
100
|
+
.empty-state.minimal-empty {
|
|
101
|
+
text-align: center;
|
|
102
|
+
padding: 2rem 1rem;
|
|
103
|
+
background: rgba(255, 255, 255, 0.03);
|
|
104
|
+
border-radius: 10px;
|
|
105
|
+
border: 1px dashed rgba(255, 255, 255, 0.1);
|
|
106
|
+
}
|
|
107
|
+
.empty-state.minimal-empty p { color: #a1a1aa; font-size: 0.95rem; }
|
|
108
|
+
</style>
|
|
109
|
+
</head>
|
|
110
|
+
<body class="minimal-dashboard">
|
|
111
|
+
<div class="container">
|
|
112
|
+
<header>
|
|
113
|
+
<h1>${escapeHtml(proxyName)}</h1>
|
|
114
|
+
</header>
|
|
115
|
+
${servicesHtml}
|
|
116
|
+
</div>
|
|
117
|
+
</body>
|
|
118
|
+
</html>`;
|
|
119
|
+
}
|
|
120
|
+
|
|
12
121
|
const servicesHtml = routes.length > 0
|
|
13
122
|
? `<div class="services-list">
|
|
14
123
|
${routes.map(route => `
|