dynamic-self-register-proxy 1.0.9 → 1.0.11

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 CHANGED
@@ -461,6 +461,15 @@ const INTERNAL_PORT_END = 5000;
461
461
  - **Négociation de contenu** : l'URL racine `/` retourne HTML pour les navigateurs, JSON pour les API
462
462
  - **Health check automatique** : désenregistrement automatique des serveurs non répondants
463
463
 
464
+ ## 📝 Logs et Débogage
465
+
466
+ Le serveur enregistre ses activités (requêtes, erreurs, enregistrements) dans un fichier de log persistant :
467
+
468
+ - **Windows** : `C:\var\log\proxy-server\server.log`
469
+ - **Linux/Mac** : `/var/log/proxy-server/server.log`
470
+
471
+ Le format des logs inclut la date, l'heure, le PID et le tag `[PROXY]`.
472
+
464
473
  ## Limitations
465
474
 
466
475
  - Les routes sont stockées en mémoire (perdues au redémarrage du proxy)
package/logger.js ADDED
@@ -0,0 +1,69 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ let currentPrefix = 'PROXY';
5
+
6
+ /**
7
+ * Log a message to a file at the root of the disk.
8
+ * @param {string} message The message to log
9
+ * @param {string} [prefix] Optional prefix to override
10
+ */
11
+ function logToFile(message, prefix = '') {
12
+ const logDir = process.platform === 'win32'
13
+ ? 'C:\\var\\log\\proxy-server'
14
+ : '/var/log/proxy-server';
15
+ const logFile = path.join(logDir, 'server.log');
16
+ const now = new Date().toLocaleString('fr-FR', {
17
+ year: 'numeric',
18
+ month: '2-digit',
19
+ day: '2-digit',
20
+ hour: '2-digit',
21
+ minute: '2-digit',
22
+ second: '2-digit',
23
+ });
24
+
25
+ const effectivePrefix = prefix || currentPrefix;
26
+ const label = effectivePrefix ? ` [${effectivePrefix}]` : "";
27
+ const logEntry = `[${now}] [PID ${process.pid}]${label} ${message}\n`;
28
+
29
+ try {
30
+ if (!fs.existsSync(logDir)) {
31
+ fs.mkdirSync(logDir, { recursive: true });
32
+ }
33
+ fs.appendFileSync(logFile, logEntry);
34
+ } catch (error) {
35
+ // On ne log pas l'erreur de log ici pour éviter une boucle infinie
36
+ // mais on l'affiche sur la console
37
+ process.stderr.write(`Critical: Failed to write to log file: ${error.message}\n`);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Override console methods to also log to file
43
+ * @param {string} [prefix] Prefix for this process
44
+ */
45
+ function setupLogging(prefix = 'PROXY') {
46
+ currentPrefix = prefix;
47
+ const originalLog = console.log;
48
+ const originalError = console.error;
49
+
50
+ console.log = (...args) => {
51
+ const message = args.map(arg =>
52
+ typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg
53
+ ).join(' ');
54
+ logToFile(message);
55
+ originalLog.apply(console, args);
56
+ };
57
+
58
+ console.error = (...args) => {
59
+ const message = args.map(arg =>
60
+ typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg
61
+ ).join(' ');
62
+ logToFile(`[ERROR] ${message}`);
63
+ originalError.apply(console, args);
64
+ };
65
+
66
+ logToFile('--- Logging system initialized ---');
67
+ }
68
+
69
+ module.exports = { logToFile, setupLogging };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dynamic-self-register-proxy",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
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": {
@@ -15,6 +15,7 @@
15
15
  "files": [
16
16
  "proxy.js",
17
17
  "proxy-client.js",
18
+ "logger.js",
18
19
  "README.md",
19
20
  "LICENSE"
20
21
  ],
package/proxy-client.js CHANGED
@@ -1,144 +1,144 @@
1
- /**
2
- * ProxyClient - Helper pour s'enregistrer auprès du Dynamic Proxy
3
- *
4
- * Usage:
5
- * const ProxyClient = require('./proxy-client');
6
- * const proxy = new ProxyClient('http://localhost:3000');
7
- * const { port } = await proxy.register('/myapp', 'My App');
8
- * app.listen(port);
9
- */
10
-
11
- class ProxyClient {
12
- /**
13
- * @param {string} proxyUrl - URL du proxy (ex: 'http://localhost:3000')
14
- */
15
- constructor(proxyUrl = 'http://localhost:3000') {
16
- this.proxyUrl = proxyUrl;
17
- this.registeredPath = null;
18
- this.assignedPort = null;
19
- }
20
-
21
- /**
22
- * Enregistre une route auprès du proxy
23
- * @param {string} path - Chemin URL (ex: '/myapp')
24
- * @param {string} [name] - Nom de l'application
25
- * @param {number} [port] - Port spécifique (optionnel, sinon auto-attribué)
26
- * @returns {Promise<{success: boolean, path: string, port: number, name: string}>}
27
- */
28
- async register(path, name, port) {
29
- const body = { path, name };
30
- if (port) body.port = port;
31
-
32
- const response = await fetch(`${this.proxyUrl}/proxy/register`, {
33
- method: 'POST',
34
- headers: { 'Content-Type': 'application/json' },
35
- body: JSON.stringify(body)
36
- });
37
-
38
- const data = await response.json();
39
-
40
- if (!response.ok) {
41
- throw new Error(data.error || 'Registration failed');
42
- }
43
-
44
- this.registeredPath = data.path;
45
- this.assignedPort = data.port;
46
-
47
- return data;
48
- }
49
-
50
- /**
51
- * Supprime l'enregistrement de cette application
52
- * @returns {Promise<{success: boolean, path: string, freedPort: number}>}
53
- */
54
- async unregister() {
55
- if (!this.registeredPath) {
56
- return { success: false, error: 'Not registered' };
57
- }
58
-
59
- try {
60
- const response = await fetch(`${this.proxyUrl}/proxy/unregister`, {
61
- method: 'DELETE',
62
- headers: { 'Content-Type': 'application/json' },
63
- body: JSON.stringify({ path: this.registeredPath })
64
- });
65
-
66
- const data = await response.json();
67
-
68
- if (response.ok) {
69
- this.registeredPath = null;
70
- this.assignedPort = null;
71
- }
72
-
73
- return data;
74
- } catch (error) {
75
- console.error('Unregister error:', error.message);
76
- return { success: false, error: error.message };
77
- }
78
- }
79
-
80
- /**
81
- * Liste toutes les routes enregistrées
82
- * @returns {Promise<{count: number, routes: Array, availablePorts: number}>}
83
- */
84
- async listRoutes() {
85
- const response = await fetch(`${this.proxyUrl}/proxy/routes`);
86
- return response.json();
87
- }
88
-
89
- /**
90
- * Vérifie l'état du proxy
91
- * @returns {Promise<{status: string, uptime: number, registeredRoutes: number}>}
92
- */
93
- async health() {
94
- const response = await fetch(`${this.proxyUrl}/proxy/health`);
95
- return response.json();
96
- }
97
-
98
- /**
99
- * Configure les handlers pour un arrêt propre
100
- * @param {http.Server} server - Instance du serveur HTTP/Express
101
- */
102
- setupGracefulShutdown(server) {
103
- const shutdown = async () => {
104
- console.log('Shutting down...');
105
- await this.unregister();
106
- server.close(() => {
107
- console.log('Server closed.');
108
- process.exit(0);
109
- });
110
- };
111
-
112
- process.on('SIGTERM', shutdown);
113
- process.on('SIGINT', shutdown);
114
- }
115
-
116
- /**
117
- * Ajoute la route /proxy/health requise par le proxy pour le health check polling
118
- * @param {express.Application} app - Instance de l'application Express
119
- * @param {object} [options] - Options de configuration
120
- * @param {function} [options.healthCheck] - Fonction personnalisée pour vérifier la santé (doit retourner true/false)
121
- */
122
- setupHealthRoute(app, options = {}) {
123
- app.get('/proxy/health', async (req, res) => {
124
- try {
125
- // Si une fonction de health check personnalisée est fournie
126
- if (options.healthCheck) {
127
- const isHealthy = await options.healthCheck();
128
- if (isHealthy) {
129
- return res.status(200).json({ status: 'healthy' });
130
- } else {
131
- return res.status(503).json({ status: 'unhealthy' });
132
- }
133
- }
134
-
135
- // Par défaut, retourne 200 si le serveur répond
136
- res.status(200).json({ status: 'healthy' });
137
- } catch (error) {
138
- res.status(503).json({ status: 'unhealthy', error: error.message });
139
- }
140
- });
141
- }
142
- }
143
-
144
- module.exports = ProxyClient;
1
+ /**
2
+ * ProxyClient - Helper pour s'enregistrer auprès du Dynamic Proxy
3
+ *
4
+ * Usage:
5
+ * const ProxyClient = require('./proxy-client');
6
+ * const proxy = new ProxyClient('http://localhost:3000');
7
+ * const { port } = await proxy.register('/myapp', 'My App');
8
+ * app.listen(port);
9
+ */
10
+
11
+ class ProxyClient {
12
+ /**
13
+ * @param {string} proxyUrl - URL du proxy (ex: 'http://localhost:3000')
14
+ */
15
+ constructor(proxyUrl = 'http://localhost:3000') {
16
+ this.proxyUrl = proxyUrl;
17
+ this.registeredPath = null;
18
+ this.assignedPort = null;
19
+ }
20
+
21
+ /**
22
+ * Enregistre une route auprès du proxy
23
+ * @param {string} path - Chemin URL (ex: '/myapp')
24
+ * @param {string} [name] - Nom de l'application
25
+ * @param {number} [port] - Port spécifique (optionnel, sinon auto-attribué)
26
+ * @returns {Promise<{success: boolean, path: string, port: number, name: string}>}
27
+ */
28
+ async register(path, name, port) {
29
+ const body = { path, name };
30
+ if (port) body.port = port;
31
+
32
+ const response = await fetch(`${this.proxyUrl}/proxy/register`, {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify(body)
36
+ });
37
+
38
+ const data = await response.json();
39
+
40
+ if (!response.ok) {
41
+ throw new Error(data.error || 'Registration failed');
42
+ }
43
+
44
+ this.registeredPath = data.path;
45
+ this.assignedPort = data.port;
46
+
47
+ return data;
48
+ }
49
+
50
+ /**
51
+ * Supprime l'enregistrement de cette application
52
+ * @returns {Promise<{success: boolean, path: string, freedPort: number}>}
53
+ */
54
+ async unregister() {
55
+ if (!this.registeredPath) {
56
+ return { success: false, error: 'Not registered' };
57
+ }
58
+
59
+ try {
60
+ const response = await fetch(`${this.proxyUrl}/proxy/unregister`, {
61
+ method: 'DELETE',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({ path: this.registeredPath })
64
+ });
65
+
66
+ const data = await response.json();
67
+
68
+ if (response.ok) {
69
+ this.registeredPath = null;
70
+ this.assignedPort = null;
71
+ }
72
+
73
+ return data;
74
+ } catch (error) {
75
+ console.error('Unregister error:', error.message);
76
+ return { success: false, error: error.message };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Liste toutes les routes enregistrées
82
+ * @returns {Promise<{count: number, routes: Array, availablePorts: number}>}
83
+ */
84
+ async listRoutes() {
85
+ const response = await fetch(`${this.proxyUrl}/proxy/routes`);
86
+ return response.json();
87
+ }
88
+
89
+ /**
90
+ * Vérifie l'état du proxy
91
+ * @returns {Promise<{status: string, uptime: number, registeredRoutes: number}>}
92
+ */
93
+ async health() {
94
+ const response = await fetch(`${this.proxyUrl}/proxy/health`);
95
+ return response.json();
96
+ }
97
+
98
+ /**
99
+ * Configure les handlers pour un arrêt propre
100
+ * @param {http.Server} server - Instance du serveur HTTP/Express
101
+ */
102
+ setupGracefulShutdown(server) {
103
+ const shutdown = async () => {
104
+ console.log('Shutting down...');
105
+ await this.unregister();
106
+ server.close(() => {
107
+ console.log('Server closed.');
108
+ process.exit(0);
109
+ });
110
+ };
111
+
112
+ process.on('SIGTERM', shutdown);
113
+ process.on('SIGINT', shutdown);
114
+ }
115
+
116
+ /**
117
+ * Ajoute la route /proxy/health requise par le proxy pour le health check polling
118
+ * @param {express.Application} app - Instance de l'application Express
119
+ * @param {object} [options] - Options de configuration
120
+ * @param {function} [options.healthCheck] - Fonction personnalisée pour vérifier la santé (doit retourner true/false)
121
+ */
122
+ setupHealthRoute(app, options = {}) {
123
+ app.get('/proxy/health', async (req, res) => {
124
+ try {
125
+ // Si une fonction de health check personnalisée est fournie
126
+ if (options.healthCheck) {
127
+ const isHealthy = await options.healthCheck();
128
+ if (isHealthy) {
129
+ return res.status(200).json({ status: 'healthy' });
130
+ } else {
131
+ return res.status(503).json({ status: 'unhealthy' });
132
+ }
133
+ }
134
+
135
+ // Par défaut, retourne 200 si le serveur répond
136
+ res.status(200).json({ status: 'healthy' });
137
+ } catch (error) {
138
+ res.status(503).json({ status: 'unhealthy', error: error.message });
139
+ }
140
+ });
141
+ }
142
+ }
143
+
144
+ module.exports = ProxyClient;