@infodb/revx 0.2.2 → 0.4.0
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 +59 -110
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +13 -62
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +20 -54
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +5 -17
- package/dist/commands/validate.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/config.d.ts +1 -64
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +6 -12
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/proxy.d.ts +2 -17
- package/dist/utils/proxy.d.ts.map +1 -1
- package/dist/utils/proxy.js +3 -102
- package/dist/utils/proxy.js.map +1 -1
- package/package.json +1 -1
- package/sample/revx.simple.yaml +4 -4
- package/sample/revx.yaml +9 -58
package/README.md
CHANGED
|
@@ -7,14 +7,12 @@ Reverse proxy CLI tool with YAML configuration. Built with Express and http-prox
|
|
|
7
7
|
- YAML-based configuration
|
|
8
8
|
- Simple reverse proxy setup
|
|
9
9
|
- **Automatic route sorting** - Routes are automatically sorted by specificity (longest paths first)
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Request/Response header transformation
|
|
10
|
+
- WebSocket support (critical for HMR)
|
|
11
|
+
- Static file serving
|
|
13
12
|
- CORS configuration
|
|
14
|
-
-
|
|
15
|
-
- Health checks
|
|
16
|
-
- Request ID tracking
|
|
13
|
+
- Path rewriting
|
|
17
14
|
- Environment variable expansion
|
|
15
|
+
- Optimized for development servers like Vite
|
|
18
16
|
|
|
19
17
|
## Installation
|
|
20
18
|
|
|
@@ -119,8 +117,6 @@ routes:
|
|
|
119
117
|
|
|
120
118
|
```yaml
|
|
121
119
|
global:
|
|
122
|
-
timeout: 30000
|
|
123
|
-
|
|
124
120
|
# Max concurrent sockets (default: 256)
|
|
125
121
|
# Increase for better performance with dev servers like Vite
|
|
126
122
|
maxSockets: 512
|
|
@@ -134,7 +130,6 @@ global:
|
|
|
134
130
|
logging:
|
|
135
131
|
enabled: true
|
|
136
132
|
format: "combined" # combined | dev | common | short | tiny
|
|
137
|
-
level: "info" # error | warn | info | debug
|
|
138
133
|
```
|
|
139
134
|
|
|
140
135
|
### Performance Tuning
|
|
@@ -196,22 +191,10 @@ routes:
|
|
|
196
191
|
"^/api": ""
|
|
197
192
|
```
|
|
198
193
|
|
|
199
|
-
#### Load Balancing
|
|
200
|
-
|
|
201
|
-
```yaml
|
|
202
|
-
routes:
|
|
203
|
-
- path: "/balanced/*"
|
|
204
|
-
targets:
|
|
205
|
-
- "http://server1.example.com"
|
|
206
|
-
- "http://server2.example.com"
|
|
207
|
-
- "http://server3.example.com"
|
|
208
|
-
strategy: "round-robin" # round-robin | random | ip-hash
|
|
209
|
-
pathRewrite:
|
|
210
|
-
"^/balanced": ""
|
|
211
|
-
```
|
|
212
|
-
|
|
213
194
|
#### WebSocket Proxy
|
|
214
195
|
|
|
196
|
+
WebSocket support is critical for Hot Module Replacement (HMR) with development servers like Vite:
|
|
197
|
+
|
|
215
198
|
```yaml
|
|
216
199
|
routes:
|
|
217
200
|
- path: "/ws"
|
|
@@ -220,64 +203,41 @@ routes:
|
|
|
220
203
|
changeOrigin: true
|
|
221
204
|
```
|
|
222
205
|
|
|
223
|
-
####
|
|
206
|
+
#### Static File Serving
|
|
207
|
+
|
|
208
|
+
Serve static files directly without proxying (useful for `vite build --watch` output):
|
|
224
209
|
|
|
225
210
|
```yaml
|
|
226
211
|
routes:
|
|
227
|
-
- path: "
|
|
228
|
-
|
|
229
|
-
transform:
|
|
230
|
-
request:
|
|
231
|
-
headers:
|
|
232
|
-
add:
|
|
233
|
-
X-API-Version: "v2"
|
|
234
|
-
X-Custom-Header: "value"
|
|
235
|
-
remove:
|
|
236
|
-
- "Cookie"
|
|
237
|
-
response:
|
|
238
|
-
headers:
|
|
239
|
-
add:
|
|
240
|
-
X-Proxy-Server: "revx"
|
|
241
|
-
remove:
|
|
242
|
-
- "Server"
|
|
212
|
+
- path: "/*"
|
|
213
|
+
static: "./dist"
|
|
243
214
|
```
|
|
244
215
|
|
|
245
|
-
|
|
246
|
-
|
|
216
|
+
**Combined with API proxy:**
|
|
247
217
|
```yaml
|
|
248
218
|
routes:
|
|
219
|
+
# API requests go to backend
|
|
249
220
|
- path: "/api/*"
|
|
250
|
-
target: "http://
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
followRedirects: true
|
|
254
|
-
headers:
|
|
255
|
-
X-Forwarded-Host: "${HOST}"
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Middleware
|
|
259
|
-
|
|
260
|
-
```yaml
|
|
261
|
-
middleware:
|
|
262
|
-
- type: "requestId"
|
|
263
|
-
enabled: true
|
|
264
|
-
headerName: "X-Request-ID"
|
|
221
|
+
target: "http://localhost:4000"
|
|
222
|
+
pathRewrite:
|
|
223
|
+
"^/api": ""
|
|
265
224
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
225
|
+
# Static files from Vite build
|
|
226
|
+
- path: "/*"
|
|
227
|
+
static: "./dist"
|
|
269
228
|
```
|
|
270
229
|
|
|
271
|
-
|
|
230
|
+
**Use case: Vite build --watch**
|
|
231
|
+
```bash
|
|
232
|
+
# Terminal 1: Run Vite in build watch mode
|
|
233
|
+
vite build --watch
|
|
272
234
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
enabled: true
|
|
276
|
-
key: "/path/to/private.key"
|
|
277
|
-
cert: "/path/to/certificate.crt"
|
|
278
|
-
ca: "/path/to/ca.crt" # Optional
|
|
235
|
+
# Terminal 2: Run revx to serve the built files
|
|
236
|
+
revx start
|
|
279
237
|
```
|
|
280
238
|
|
|
239
|
+
This serves pre-built static files, avoiding proxy-related errors that can occur with Vite dev server.
|
|
240
|
+
|
|
281
241
|
### Environment Variables
|
|
282
242
|
|
|
283
243
|
Use `${VARIABLE_NAME}` syntax to reference environment variables:
|
|
@@ -289,15 +249,12 @@ server:
|
|
|
289
249
|
routes:
|
|
290
250
|
- path: "/api/*"
|
|
291
251
|
target: "${API_URL}"
|
|
292
|
-
options:
|
|
293
|
-
headers:
|
|
294
|
-
Authorization: "Bearer ${API_TOKEN}"
|
|
295
252
|
```
|
|
296
253
|
|
|
297
254
|
Then run:
|
|
298
255
|
|
|
299
256
|
```bash
|
|
300
|
-
PORT=3000 API_URL=http://api.example.com
|
|
257
|
+
PORT=3000 API_URL=http://api.example.com revx start
|
|
301
258
|
```
|
|
302
259
|
|
|
303
260
|
## Examples
|
|
@@ -371,62 +328,54 @@ routes:
|
|
|
371
328
|
# Proxy everything else to Vite dev server
|
|
372
329
|
- path: "/*"
|
|
373
330
|
target: "http://localhost:5173"
|
|
374
|
-
ws: true
|
|
331
|
+
ws: true # Enable WebSocket for HMR
|
|
375
332
|
changeOrigin: true
|
|
376
333
|
```
|
|
377
334
|
|
|
378
|
-
###
|
|
335
|
+
### Vite Build Watch Mode
|
|
379
336
|
|
|
380
337
|
```yaml
|
|
381
338
|
server:
|
|
382
|
-
port:
|
|
383
|
-
name: "Production Load Balancer"
|
|
384
|
-
|
|
385
|
-
ssl:
|
|
386
|
-
enabled: true
|
|
387
|
-
key: "/etc/ssl/private/server.key"
|
|
388
|
-
cert: "/etc/ssl/certs/server.crt"
|
|
339
|
+
port: 3000
|
|
389
340
|
|
|
390
341
|
routes:
|
|
342
|
+
# Proxy API requests to backend
|
|
343
|
+
- path: "/api/*"
|
|
344
|
+
target: "http://localhost:4000"
|
|
345
|
+
pathRewrite:
|
|
346
|
+
"^/api": ""
|
|
347
|
+
|
|
348
|
+
# Serve static files built by Vite
|
|
391
349
|
- path: "/*"
|
|
392
|
-
|
|
393
|
-
- "http://app-server-1:8080"
|
|
394
|
-
- "http://app-server-2:8080"
|
|
395
|
-
- "http://app-server-3:8080"
|
|
396
|
-
strategy: "round-robin"
|
|
397
|
-
healthCheck:
|
|
398
|
-
enabled: true
|
|
399
|
-
interval: 30000
|
|
400
|
-
path: "/health"
|
|
401
|
-
timeout: 3000
|
|
350
|
+
static: "./dist"
|
|
402
351
|
```
|
|
403
352
|
|
|
404
|
-
|
|
353
|
+
Run with:
|
|
354
|
+
```bash
|
|
355
|
+
# Terminal 1: Build and watch
|
|
356
|
+
vite build --watch
|
|
405
357
|
|
|
406
|
-
|
|
358
|
+
# Terminal 2: Serve with revx
|
|
359
|
+
revx start
|
|
360
|
+
```
|
|
407
361
|
|
|
408
|
-
|
|
362
|
+
## Troubleshooting
|
|
363
|
+
|
|
364
|
+
### Vite Development Server Issues
|
|
409
365
|
|
|
410
|
-
|
|
411
|
-
- CONTENT_LENGTH_MISMATCH errors are typically benign with streaming responses
|
|
412
|
-
- The content is usually already successfully delivered to the client
|
|
366
|
+
When proxying directly to Vite dev server, you may encounter errors due to dynamic content transformation. For a more stable setup, consider using `vite build --watch` with static file serving instead:
|
|
413
367
|
|
|
414
|
-
|
|
368
|
+
```bash
|
|
369
|
+
# Terminal 1: Build and watch
|
|
370
|
+
vite build --watch
|
|
415
371
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
- The response has usually completed successfully before the error is detected
|
|
420
|
-
- Works transparently without configuration needed
|
|
372
|
+
# Terminal 2: Serve with revx
|
|
373
|
+
revx start
|
|
374
|
+
```
|
|
421
375
|
|
|
422
|
-
|
|
423
|
-
- Vite sends Content-Length based on original file size
|
|
424
|
-
- Vite then transforms the content (ESM imports, HMR injection)
|
|
425
|
-
- The transformed content has a different size
|
|
426
|
-
- By the time the mismatch is detected, the client has already received the content
|
|
427
|
-
- Ignoring the error prevents unnecessary 502 responses
|
|
376
|
+
This approach serves pre-built files and avoids proxy-related issues.
|
|
428
377
|
|
|
429
|
-
### Performance Issues with
|
|
378
|
+
### Performance Issues with Dev Servers
|
|
430
379
|
|
|
431
380
|
If you experience slow loading or timeouts when proxying to Vite or similar dev servers:
|
|
432
381
|
|
|
@@ -454,7 +403,7 @@ If you experience slow loading or timeouts when proxying to Vite or similar dev
|
|
|
454
403
|
- Only use `pathRewrite` when you need to modify the path
|
|
455
404
|
|
|
456
405
|
**Q: CORS errors**
|
|
457
|
-
- Enable CORS in global config
|
|
406
|
+
- Enable CORS in global config
|
|
458
407
|
- Check the `origin` setting matches your client URL
|
|
459
408
|
|
|
460
409
|
## Development
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA2EA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,iBAwBlG"}
|
package/dist/commands/init.js
CHANGED
|
@@ -14,9 +14,6 @@ server:
|
|
|
14
14
|
|
|
15
15
|
# Global configuration
|
|
16
16
|
global:
|
|
17
|
-
# Request timeout in milliseconds
|
|
18
|
-
timeout: 30000
|
|
19
|
-
|
|
20
17
|
# Max concurrent sockets (can also be set here)
|
|
21
18
|
# maxSockets: 256
|
|
22
19
|
|
|
@@ -31,7 +28,6 @@ global:
|
|
|
31
28
|
logging:
|
|
32
29
|
enabled: true
|
|
33
30
|
format: "combined" # combined | dev | common | short | tiny
|
|
34
|
-
level: "info" # error | warn | info | debug
|
|
35
31
|
|
|
36
32
|
# Route definitions
|
|
37
33
|
routes:
|
|
@@ -41,67 +37,22 @@ routes:
|
|
|
41
37
|
changeOrigin: true
|
|
42
38
|
pathRewrite:
|
|
43
39
|
"^/api": ""
|
|
44
|
-
options:
|
|
45
|
-
timeout: 5000
|
|
46
|
-
headers:
|
|
47
|
-
X-Forwarded-Host: "\${HOST}"
|
|
48
40
|
|
|
49
|
-
# Example 2: WebSocket proxy
|
|
41
|
+
# Example 2: WebSocket proxy (critical for HMR)
|
|
50
42
|
- path: "/ws"
|
|
51
43
|
target: "ws://localhost:5000"
|
|
52
44
|
ws: true
|
|
53
45
|
changeOrigin: true
|
|
54
46
|
|
|
55
|
-
# Example 3:
|
|
56
|
-
- path: "
|
|
57
|
-
|
|
58
|
-
- "http://localhost:8001"
|
|
59
|
-
- "http://localhost:8002"
|
|
60
|
-
- "http://localhost:8003"
|
|
61
|
-
strategy: "round-robin" # round-robin | random | ip-hash
|
|
62
|
-
pathRewrite:
|
|
63
|
-
"^/balanced": ""
|
|
64
|
-
healthCheck:
|
|
65
|
-
enabled: true
|
|
66
|
-
interval: 30000
|
|
67
|
-
path: "/health"
|
|
68
|
-
timeout: 3000
|
|
69
|
-
|
|
70
|
-
# Example 4: Request/Response transformation
|
|
71
|
-
- path: "/transform/*"
|
|
72
|
-
target: "http://localhost:6000"
|
|
73
|
-
changeOrigin: true
|
|
74
|
-
transform:
|
|
75
|
-
request:
|
|
76
|
-
headers:
|
|
77
|
-
add:
|
|
78
|
-
X-API-Version: "v2"
|
|
79
|
-
remove:
|
|
80
|
-
- "Cookie"
|
|
81
|
-
response:
|
|
82
|
-
headers:
|
|
83
|
-
add:
|
|
84
|
-
X-Proxy-Server: "revx"
|
|
85
|
-
remove:
|
|
86
|
-
- "Server"
|
|
87
|
-
|
|
88
|
-
# Middleware configuration
|
|
89
|
-
middleware:
|
|
90
|
-
# Request ID middleware
|
|
91
|
-
- type: "requestId"
|
|
92
|
-
enabled: true
|
|
93
|
-
headerName: "X-Request-ID"
|
|
47
|
+
# Example 3: Static file serving (for Vite build output)
|
|
48
|
+
- path: "/*"
|
|
49
|
+
static: "./dist"
|
|
94
50
|
|
|
95
|
-
#
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# SSL/TLS settings (optional)
|
|
101
|
-
# ssl:
|
|
102
|
-
# enabled: false
|
|
103
|
-
# key: "/path/to/private.key"
|
|
104
|
-
# cert: "/path/to/certificate.crt"
|
|
51
|
+
# Example 4: Proxy to Vite dev server
|
|
52
|
+
# - path: "/*"
|
|
53
|
+
# target: "http://localhost:5173"
|
|
54
|
+
# ws: true # Enable WebSocket for HMR
|
|
55
|
+
# changeOrigin: true
|
|
105
56
|
`;
|
|
106
57
|
const SIMPLE_CONFIG = `# revx.yaml - Simple Reverse Proxy Configuration
|
|
107
58
|
|
|
@@ -114,10 +65,10 @@ routes:
|
|
|
114
65
|
pathRewrite:
|
|
115
66
|
"^/api": ""
|
|
116
67
|
|
|
117
|
-
- path: "
|
|
118
|
-
target: "http://localhost:
|
|
119
|
-
|
|
120
|
-
|
|
68
|
+
- path: "/*"
|
|
69
|
+
target: "http://localhost:5173"
|
|
70
|
+
ws: true
|
|
71
|
+
changeOrigin: true
|
|
121
72
|
`;
|
|
122
73
|
export async function initCommand(options) {
|
|
123
74
|
const logger = new Logger(options.verbose);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDrB,CAAC;AAEF,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;CAerB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAiE;IACjG,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC;IACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IAEpD,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,KAAK,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAC;YAC/D,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;YAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;QAC/D,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAE1C,MAAM,CAAC,OAAO,CAAC,+BAA+B,QAAQ,EAAE,CAAC,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,oCAAoC,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,MAAM,CAAC,KAAK,CAAC,wCAAwC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAWA,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,YAAc,EAAE,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,iBAyBlG"}
|
package/dist/commands/start.js
CHANGED
|
@@ -2,14 +2,12 @@ import express from 'express';
|
|
|
2
2
|
import cors from 'cors';
|
|
3
3
|
import morgan from 'morgan';
|
|
4
4
|
import http from 'http';
|
|
5
|
-
import https from 'https';
|
|
6
5
|
import { createServer } from 'http';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
9
8
|
import { ConfigLoader } from '../utils/config.js';
|
|
10
9
|
import { ProxyManager } from '../utils/proxy.js';
|
|
11
10
|
import { Logger } from '../utils/logger.js';
|
|
12
|
-
import { randomUUID } from 'crypto';
|
|
13
11
|
export async function startCommand(configFile = 'revx.yaml', options) {
|
|
14
12
|
const logger = new Logger(options.verbose);
|
|
15
13
|
const configLoader = new ConfigLoader(logger);
|
|
@@ -19,7 +17,6 @@ export async function startCommand(configFile = 'revx.yaml', options) {
|
|
|
19
17
|
// Configure max sockets for better performance with dev servers like Vite
|
|
20
18
|
const maxSockets = config.server.maxSockets || config.global?.maxSockets || 256;
|
|
21
19
|
http.globalAgent.maxSockets = maxSockets;
|
|
22
|
-
https.globalAgent.maxSockets = maxSockets;
|
|
23
20
|
logger.verbose(`Max sockets configured: ${maxSockets}`);
|
|
24
21
|
const app = express();
|
|
25
22
|
const proxyManager = new ProxyManager(logger);
|
|
@@ -44,42 +41,32 @@ function setupMiddleware(app, config, logger) {
|
|
|
44
41
|
app.use(cors(corsOptions));
|
|
45
42
|
logger.verbose('CORS enabled', corsOptions);
|
|
46
43
|
}
|
|
47
|
-
const requestIdMiddleware = config.middleware?.find(m => m.type === 'requestId');
|
|
48
|
-
if (requestIdMiddleware?.enabled !== false) {
|
|
49
|
-
const headerName = requestIdMiddleware?.headerName || 'X-Request-ID';
|
|
50
|
-
app.use((req, res, next) => {
|
|
51
|
-
const requestId = randomUUID();
|
|
52
|
-
req.headers[headerName.toLowerCase()] = requestId;
|
|
53
|
-
res.setHeader(headerName, requestId);
|
|
54
|
-
next();
|
|
55
|
-
});
|
|
56
|
-
logger.verbose('Request ID middleware enabled');
|
|
57
|
-
}
|
|
58
44
|
if (config.global?.logging?.enabled !== false) {
|
|
59
45
|
const format = config.global?.logging?.format || 'combined';
|
|
60
46
|
app.use(morgan(format));
|
|
61
47
|
logger.verbose(`Logging middleware enabled: ${format}`);
|
|
62
48
|
}
|
|
63
|
-
const compressionMiddleware = config.middleware?.find(m => m.type === 'compression');
|
|
64
|
-
if (compressionMiddleware?.enabled) {
|
|
65
|
-
logger.verbose('Compression middleware enabled');
|
|
66
|
-
}
|
|
67
49
|
app.use(express.json());
|
|
68
50
|
app.use(express.urlencoded({ extended: true }));
|
|
69
51
|
}
|
|
52
|
+
function setupStaticRoute(app, route, logger) {
|
|
53
|
+
const rootPath = resolve(process.cwd(), route.static);
|
|
54
|
+
if (!existsSync(rootPath)) {
|
|
55
|
+
logger.error(`Static directory does not exist: ${rootPath}`);
|
|
56
|
+
throw new Error(`Static directory not found: ${rootPath}`);
|
|
57
|
+
}
|
|
58
|
+
logger.info(`Static files configured: ${route.path} -> ${rootPath}`);
|
|
59
|
+
app.use(route.path, express.static(rootPath));
|
|
60
|
+
}
|
|
70
61
|
function setupRoutes(app, config, proxyManager, logger) {
|
|
71
62
|
config.routes.forEach((route) => {
|
|
72
|
-
if (route.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
app.use(route.path, cors(routeCorsOptions));
|
|
79
|
-
logger.verbose(`Route-specific CORS for ${route.path}`, routeCorsOptions);
|
|
63
|
+
if (route.static) {
|
|
64
|
+
setupStaticRoute(app, route, logger);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const proxyMiddleware = proxyManager.createProxyMiddleware(route);
|
|
68
|
+
app.use(proxyMiddleware);
|
|
80
69
|
}
|
|
81
|
-
const proxyMiddleware = proxyManager.createProxyMiddleware(route);
|
|
82
|
-
app.use(proxyMiddleware);
|
|
83
70
|
});
|
|
84
71
|
app.get('/health', (req, res) => {
|
|
85
72
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
@@ -104,35 +91,14 @@ async function startServer(app, config, logger) {
|
|
|
104
91
|
return new Promise((resolve, reject) => {
|
|
105
92
|
const port = config.server.port;
|
|
106
93
|
const host = config.server.host || '0.0.0.0';
|
|
107
|
-
|
|
108
|
-
if (config.ssl?.enabled) {
|
|
109
|
-
try {
|
|
110
|
-
const httpsOptions = {
|
|
111
|
-
key: readFileSync(config.ssl.key, 'utf-8'),
|
|
112
|
-
cert: readFileSync(config.ssl.cert, 'utf-8'),
|
|
113
|
-
ca: config.ssl.ca ? readFileSync(config.ssl.ca, 'utf-8') : undefined
|
|
114
|
-
};
|
|
115
|
-
server = createHttpsServer(httpsOptions, app);
|
|
116
|
-
logger.info('SSL/TLS enabled');
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
if (error instanceof Error) {
|
|
120
|
-
reject(new Error(`Failed to load SSL certificates: ${error.message}`));
|
|
121
|
-
}
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
server = createServer(app);
|
|
127
|
-
}
|
|
94
|
+
const server = createServer(app);
|
|
128
95
|
server.listen(port, host, () => {
|
|
129
|
-
const protocol = config.ssl?.enabled ? 'https' : 'http';
|
|
130
96
|
logger.server(`${config.server.name || 'Reverse Proxy'} started`);
|
|
131
|
-
logger.success(`Listening on
|
|
97
|
+
logger.success(`Listening on http://${host}:${port}`);
|
|
132
98
|
logger.info('');
|
|
133
99
|
logger.info('Configured routes:');
|
|
134
100
|
config.routes.forEach((route) => {
|
|
135
|
-
const target = route.target ||
|
|
101
|
+
const target = route.target || route.static;
|
|
136
102
|
logger.info(` ${route.path} -> ${target}`);
|
|
137
103
|
});
|
|
138
104
|
logger.info('');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,OAA4C,MAAM,SAAS,CAAC;AACnE,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAGA,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAGA,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,iBAoCvF"}
|
|
@@ -18,26 +18,14 @@ export async function validateCommand(configFile, options) {
|
|
|
18
18
|
logger.info(` ${index + 1}. ${route.path}`);
|
|
19
19
|
if (route.target) {
|
|
20
20
|
logger.info(` Target: ${route.target}`);
|
|
21
|
+
if (route.ws) {
|
|
22
|
+
logger.info(` WebSocket: enabled`);
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
|
-
else if (route.
|
|
23
|
-
logger.info(`
|
|
24
|
-
route.targets.forEach((target, i) => {
|
|
25
|
-
logger.info(` ${i + 1}. ${target}`);
|
|
26
|
-
});
|
|
25
|
+
else if (route.static) {
|
|
26
|
+
logger.info(` Static: ${route.static}`);
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
|
-
if (config.middleware && config.middleware.length > 0) {
|
|
30
|
-
logger.info('');
|
|
31
|
-
logger.info('Middleware:');
|
|
32
|
-
config.middleware.forEach((mw, index) => {
|
|
33
|
-
const status = mw.enabled === false ? 'disabled' : 'enabled';
|
|
34
|
-
logger.info(` ${index + 1}. ${mw.type} (${status})`);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (config.ssl?.enabled) {
|
|
38
|
-
logger.info('');
|
|
39
|
-
logger.info('SSL: enabled');
|
|
40
|
-
}
|
|
41
29
|
}
|
|
42
30
|
}
|
|
43
31
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB,EAAE,OAA8B;IACtF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,kCAAkC,UAAU,EAAE,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE7C,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAEjD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBACrC,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC7C,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjB,MAAM,CAAC,IAAI,CAAC,gBAAgB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB,EAAE,OAA8B;IACtF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,kCAAkC,UAAU,EAAE,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE7C,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAEjD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBACrC,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC7C,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjB,MAAM,CAAC,IAAI,CAAC,gBAAgB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC5C,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;wBACb,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;oBACzC,CAAC;gBACH,CAAC;qBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,CAAC,gBAAgB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,MAAM,CAAC,KAAK,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/utils/config.d.ts
CHANGED
|
@@ -5,21 +5,14 @@ export interface CorsConfig {
|
|
|
5
5
|
methods?: string[];
|
|
6
6
|
credentials?: boolean;
|
|
7
7
|
}
|
|
8
|
-
export interface RateLimitConfig {
|
|
9
|
-
enabled?: boolean;
|
|
10
|
-
windowMs?: number;
|
|
11
|
-
max?: number;
|
|
12
|
-
}
|
|
13
8
|
export interface LoggingConfig {
|
|
14
9
|
enabled?: boolean;
|
|
15
10
|
format?: 'combined' | 'dev' | 'common' | 'short' | 'tiny';
|
|
16
11
|
level?: 'error' | 'warn' | 'info' | 'debug';
|
|
17
12
|
}
|
|
18
13
|
export interface GlobalConfig {
|
|
19
|
-
timeout?: number;
|
|
20
14
|
cors?: CorsConfig;
|
|
21
15
|
logging?: LoggingConfig;
|
|
22
|
-
rateLimit?: RateLimitConfig;
|
|
23
16
|
maxSockets?: number;
|
|
24
17
|
}
|
|
25
18
|
export interface ServerConfig {
|
|
@@ -28,74 +21,18 @@ export interface ServerConfig {
|
|
|
28
21
|
name?: string;
|
|
29
22
|
maxSockets?: number;
|
|
30
23
|
}
|
|
31
|
-
export interface RouteOptions {
|
|
32
|
-
timeout?: number;
|
|
33
|
-
followRedirects?: boolean;
|
|
34
|
-
headers?: Record<string, string>;
|
|
35
|
-
}
|
|
36
|
-
export interface CacheConfig {
|
|
37
|
-
enabled?: boolean;
|
|
38
|
-
ttl?: number;
|
|
39
|
-
}
|
|
40
|
-
export interface AuthConfig {
|
|
41
|
-
type?: 'bearer' | 'basic' | 'apikey';
|
|
42
|
-
headerName?: string;
|
|
43
|
-
}
|
|
44
|
-
export interface HealthCheckConfig {
|
|
45
|
-
enabled?: boolean;
|
|
46
|
-
interval?: number;
|
|
47
|
-
path?: string;
|
|
48
|
-
timeout?: number;
|
|
49
|
-
}
|
|
50
|
-
export interface TransformConfig {
|
|
51
|
-
request?: {
|
|
52
|
-
headers?: {
|
|
53
|
-
add?: Record<string, string>;
|
|
54
|
-
remove?: string[];
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
response?: {
|
|
58
|
-
headers?: {
|
|
59
|
-
add?: Record<string, string>;
|
|
60
|
-
remove?: string[];
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
24
|
export interface RouteConfig {
|
|
65
25
|
path: string;
|
|
66
26
|
target?: string;
|
|
67
|
-
|
|
27
|
+
static?: string;
|
|
68
28
|
changeOrigin?: boolean;
|
|
69
29
|
pathRewrite?: Record<string, string>;
|
|
70
30
|
ws?: boolean;
|
|
71
|
-
strategy?: 'round-robin' | 'random' | 'ip-hash';
|
|
72
|
-
options?: RouteOptions;
|
|
73
|
-
cors?: CorsConfig;
|
|
74
|
-
cache?: CacheConfig;
|
|
75
|
-
auth?: AuthConfig;
|
|
76
|
-
healthCheck?: HealthCheckConfig;
|
|
77
|
-
transform?: TransformConfig;
|
|
78
|
-
}
|
|
79
|
-
export interface MiddlewareConfig {
|
|
80
|
-
type: string;
|
|
81
|
-
enabled?: boolean;
|
|
82
|
-
paths?: string[];
|
|
83
|
-
config?: Record<string, any>;
|
|
84
|
-
headerName?: string;
|
|
85
|
-
threshold?: number;
|
|
86
|
-
}
|
|
87
|
-
export interface SslConfig {
|
|
88
|
-
enabled?: boolean;
|
|
89
|
-
key?: string;
|
|
90
|
-
cert?: string;
|
|
91
|
-
ca?: string;
|
|
92
31
|
}
|
|
93
32
|
export interface RevxConfig {
|
|
94
33
|
server: ServerConfig;
|
|
95
34
|
global?: GlobalConfig;
|
|
96
35
|
routes: RouteConfig[];
|
|
97
|
-
middleware?: MiddlewareConfig[];
|
|
98
|
-
ssl?: SslConfig;
|
|
99
36
|
}
|
|
100
37
|
export declare class ConfigLoader {
|
|
101
38
|
private logger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAC1D,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,EAAE,CAAC,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAElC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU;IAyBpC,OAAO,CAAC,0BAA0B;IAWlC,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,aAAa;CAgBtB"}
|
package/dist/utils/config.js
CHANGED
|
@@ -83,19 +83,13 @@ export class ConfigLoader {
|
|
|
83
83
|
if (!route.path) {
|
|
84
84
|
throw new Error('Route path is required');
|
|
85
85
|
}
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
const hasTarget = !!route.target;
|
|
87
|
+
const hasStatic = !!route.static;
|
|
88
|
+
if (!hasTarget && !hasStatic) {
|
|
89
|
+
throw new Error(`Route ${route.path} must have either target or static`);
|
|
88
90
|
}
|
|
89
|
-
if (
|
|
90
|
-
throw new Error(`Route ${route.path} cannot have both target and
|
|
91
|
-
}
|
|
92
|
-
if (route.targets) {
|
|
93
|
-
if (!Array.isArray(route.targets) || route.targets.length === 0) {
|
|
94
|
-
throw new Error(`Route ${route.path} targets must be a non-empty array`);
|
|
95
|
-
}
|
|
96
|
-
if (route.strategy && !['round-robin', 'random', 'ip-hash'].includes(route.strategy)) {
|
|
97
|
-
throw new Error(`Route ${route.path} has invalid strategy: ${route.strategy}`);
|
|
98
|
-
}
|
|
91
|
+
if (hasTarget && hasStatic) {
|
|
92
|
+
throw new Error(`Route ${route.path} cannot have both target and static`);
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
}
|
package/dist/utils/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/proxy.d.ts
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
import { RouteConfig } from './config.js';
|
|
2
2
|
import { Logger } from './logger.js';
|
|
3
|
-
import {
|
|
4
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
5
|
-
export declare class LoadBalancer {
|
|
6
|
-
private targets;
|
|
7
|
-
private strategy;
|
|
8
|
-
private logger;
|
|
9
|
-
private currentIndex;
|
|
10
|
-
private targetHealth;
|
|
11
|
-
constructor(targets: string[], strategy: "round-robin" | "random" | "ip-hash" | undefined, logger: Logger);
|
|
12
|
-
getTarget(req?: Request): string;
|
|
13
|
-
markUnhealthy(target: string): void;
|
|
14
|
-
markHealthy(target: string): void;
|
|
15
|
-
private hashCode;
|
|
16
|
-
}
|
|
3
|
+
import { ServerResponse } from 'http';
|
|
17
4
|
export declare class ProxyManager {
|
|
18
5
|
private logger;
|
|
19
|
-
private loadBalancers;
|
|
20
6
|
constructor(logger: Logger);
|
|
21
|
-
createProxyMiddleware(route: RouteConfig): import("http-proxy-middleware").RequestHandler<IncomingMessage, ServerResponse<IncomingMessage>, (err?: any) => void>;
|
|
22
|
-
getLoadBalancer(path: string): LoadBalancer | undefined;
|
|
7
|
+
createProxyMiddleware(route: RouteConfig): import("http-proxy-middleware").RequestHandler<import("http").IncomingMessage, ServerResponse<import("http").IncomingMessage>, (err?: any) => void>;
|
|
23
8
|
}
|
|
24
9
|
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/utils/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/utils/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAEtC,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAElC,qBAAqB,CAAC,KAAK,EAAE,WAAW;CA2CzC"}
|
package/dist/utils/proxy.js
CHANGED
|
@@ -1,117 +1,32 @@
|
|
|
1
1
|
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
2
|
-
export class LoadBalancer {
|
|
3
|
-
constructor(targets, strategy = 'round-robin', logger) {
|
|
4
|
-
this.targets = targets;
|
|
5
|
-
this.strategy = strategy;
|
|
6
|
-
this.logger = logger;
|
|
7
|
-
this.currentIndex = 0;
|
|
8
|
-
this.targetHealth = new Map();
|
|
9
|
-
targets.forEach(target => this.targetHealth.set(target, true));
|
|
10
|
-
}
|
|
11
|
-
getTarget(req) {
|
|
12
|
-
const healthyTargets = this.targets.filter(t => this.targetHealth.get(t));
|
|
13
|
-
if (healthyTargets.length === 0) {
|
|
14
|
-
this.logger.warning('All targets are unhealthy, using first target');
|
|
15
|
-
return this.targets[0];
|
|
16
|
-
}
|
|
17
|
-
switch (this.strategy) {
|
|
18
|
-
case 'random':
|
|
19
|
-
return healthyTargets[Math.floor(Math.random() * healthyTargets.length)];
|
|
20
|
-
case 'ip-hash':
|
|
21
|
-
if (req) {
|
|
22
|
-
const ip = req.ip || req.socket.remoteAddress || '';
|
|
23
|
-
const hash = this.hashCode(ip);
|
|
24
|
-
return healthyTargets[Math.abs(hash) % healthyTargets.length];
|
|
25
|
-
}
|
|
26
|
-
return healthyTargets[0];
|
|
27
|
-
case 'round-robin':
|
|
28
|
-
default:
|
|
29
|
-
const target = healthyTargets[this.currentIndex % healthyTargets.length];
|
|
30
|
-
this.currentIndex = (this.currentIndex + 1) % healthyTargets.length;
|
|
31
|
-
return target;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
markUnhealthy(target) {
|
|
35
|
-
this.targetHealth.set(target, false);
|
|
36
|
-
this.logger.warning(`Target marked as unhealthy: ${target}`);
|
|
37
|
-
}
|
|
38
|
-
markHealthy(target) {
|
|
39
|
-
this.targetHealth.set(target, true);
|
|
40
|
-
this.logger.verbose(`Target marked as healthy: ${target}`);
|
|
41
|
-
}
|
|
42
|
-
hashCode(str) {
|
|
43
|
-
let hash = 0;
|
|
44
|
-
for (let i = 0; i < str.length; i++) {
|
|
45
|
-
const char = str.charCodeAt(i);
|
|
46
|
-
hash = ((hash << 5) - hash) + char;
|
|
47
|
-
hash = hash & hash;
|
|
48
|
-
}
|
|
49
|
-
return hash;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
2
|
export class ProxyManager {
|
|
53
3
|
constructor(logger) {
|
|
54
4
|
this.logger = logger;
|
|
55
|
-
this.loadBalancers = new Map();
|
|
56
5
|
}
|
|
57
6
|
createProxyMiddleware(route) {
|
|
58
7
|
const options = {
|
|
59
8
|
pathFilter: route.path,
|
|
9
|
+
target: route.target,
|
|
60
10
|
changeOrigin: route.changeOrigin ?? true,
|
|
61
11
|
ws: route.ws ?? false,
|
|
62
12
|
pathRewrite: route.pathRewrite,
|
|
63
|
-
timeout: route.options?.timeout,
|
|
64
|
-
followRedirects: route.options?.followRedirects,
|
|
65
|
-
// Let http-proxy-middleware handle responses naturally
|
|
66
13
|
preserveHeaderKeyCase: true,
|
|
67
14
|
autoRewrite: true,
|
|
68
|
-
// Increase limits to handle large responses from dev servers
|
|
69
|
-
proxyTimeout: route.options?.timeout || 30000,
|
|
70
15
|
on: {
|
|
71
16
|
proxyReq: (proxyReq, req) => {
|
|
72
|
-
if (route.options?.headers) {
|
|
73
|
-
Object.entries(route.options.headers).forEach(([key, value]) => {
|
|
74
|
-
proxyReq.setHeader(key, value);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
if (route.transform?.request?.headers?.add) {
|
|
78
|
-
Object.entries(route.transform.request.headers.add).forEach(([key, value]) => {
|
|
79
|
-
proxyReq.setHeader(key, value);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
if (route.transform?.request?.headers?.remove) {
|
|
83
|
-
route.transform.request.headers.remove.forEach(header => {
|
|
84
|
-
proxyReq.removeHeader(header);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
17
|
this.logger.verbose('Proxy request', {
|
|
88
18
|
method: req.method,
|
|
89
19
|
path: req.url,
|
|
90
|
-
target:
|
|
20
|
+
target: route.target
|
|
91
21
|
});
|
|
92
22
|
},
|
|
93
23
|
proxyRes: (proxyRes, req, res) => {
|
|
94
|
-
if (route.transform?.response?.headers?.add) {
|
|
95
|
-
Object.entries(route.transform.response.headers.add).forEach(([key, value]) => {
|
|
96
|
-
proxyRes.headers[key.toLowerCase()] = value;
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
if (route.transform?.response?.headers?.remove) {
|
|
100
|
-
route.transform.response.headers.remove.forEach(header => {
|
|
101
|
-
delete proxyRes.headers[header.toLowerCase()];
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
24
|
this.logger.verbose('Proxy response', {
|
|
105
25
|
statusCode: proxyRes.statusCode,
|
|
106
26
|
path: req.url
|
|
107
27
|
});
|
|
108
28
|
},
|
|
109
29
|
error: (err, req, res) => {
|
|
110
|
-
// Ignore CONTENT_LENGTH_MISMATCH errors - they're usually benign with streaming responses
|
|
111
|
-
if (err.message && err.message.includes('CONTENT_LENGTH_MISMATCH')) {
|
|
112
|
-
this.logger.verbose(`Ignoring CONTENT_LENGTH_MISMATCH for ${req.url}`);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
30
|
this.logger.error(`Proxy error: ${err.message}`);
|
|
116
31
|
const serverRes = res;
|
|
117
32
|
if (!serverRes.headersSent) {
|
|
@@ -124,22 +39,8 @@ export class ProxyManager {
|
|
|
124
39
|
}
|
|
125
40
|
}
|
|
126
41
|
};
|
|
127
|
-
|
|
128
|
-
const balancer = new LoadBalancer(route.targets, route.strategy || 'round-robin', this.logger);
|
|
129
|
-
this.loadBalancers.set(route.path, balancer);
|
|
130
|
-
options.router = (req) => {
|
|
131
|
-
return balancer.getTarget(req);
|
|
132
|
-
};
|
|
133
|
-
this.logger.info(`Load balancer configured for ${route.path} with ${route.targets.length} targets (${route.strategy || 'round-robin'})`);
|
|
134
|
-
}
|
|
135
|
-
else if (route.target) {
|
|
136
|
-
options.target = route.target;
|
|
137
|
-
this.logger.info(`Proxy configured: ${route.path} -> ${route.target}`);
|
|
138
|
-
}
|
|
42
|
+
this.logger.info(`Proxy configured: ${route.path} -> ${route.target}`);
|
|
139
43
|
return createProxyMiddleware(options);
|
|
140
44
|
}
|
|
141
|
-
getLoadBalancer(path) {
|
|
142
|
-
return this.loadBalancers.get(path);
|
|
143
|
-
}
|
|
144
45
|
}
|
|
145
46
|
//# sourceMappingURL=proxy.js.map
|
package/dist/utils/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/utils/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAW,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/utils/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAW,MAAM,uBAAuB,CAAC;AAKvE,MAAM,OAAO,YAAY;IACvB,YAAoB,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAEtC,qBAAqB,CAAC,KAAkB;QACtC,MAAM,OAAO,GAAY;YACvB,UAAU,EAAE,KAAK,CAAC,IAAI;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;YACxC,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,KAAK;YACrB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,qBAAqB,EAAE,IAAI;YAC3B,WAAW,EAAE,IAAI;YAEjB,EAAE,EAAE;gBACF,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;oBAC1B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE;wBACnC,MAAM,EAAE,GAAG,CAAC,MAAM;wBAClB,IAAI,EAAE,GAAG,CAAC,GAAG;wBACb,MAAM,EAAE,KAAK,CAAC,MAAM;qBACrB,CAAC,CAAC;gBACL,CAAC;gBAED,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;oBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE;wBACpC,UAAU,EAAE,QAAQ,CAAC,UAAU;wBAC/B,IAAI,EAAE,GAAG,CAAC,GAAG;qBACd,CAAC,CAAC;gBACL,CAAC;gBAED,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;oBACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;oBACjD,MAAM,SAAS,GAAG,GAAqB,CAAC;oBACxC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;wBAC3B,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBACnE,CAAC;oBACD,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;wBAC3B,KAAK,EAAE,aAAa;wBACpB,OAAO,EAAE,yBAAyB;qBACnC,CAAC,CAAC,CAAC;gBACN,CAAC;aACF;SACF,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACvE,OAAO,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;CACF"}
|
package/package.json
CHANGED
package/sample/revx.simple.yaml
CHANGED
package/sample/revx.yaml
CHANGED
|
@@ -11,9 +11,6 @@ server:
|
|
|
11
11
|
|
|
12
12
|
# Global configuration
|
|
13
13
|
global:
|
|
14
|
-
# Request timeout in milliseconds
|
|
15
|
-
timeout: 30000
|
|
16
|
-
|
|
17
14
|
# Max concurrent sockets (can also be set here)
|
|
18
15
|
# maxSockets: 256
|
|
19
16
|
|
|
@@ -28,7 +25,6 @@ global:
|
|
|
28
25
|
logging:
|
|
29
26
|
enabled: true
|
|
30
27
|
format: "combined" # combined | dev | common | short | tiny
|
|
31
|
-
level: "info" # error | warn | info | debug
|
|
32
28
|
|
|
33
29
|
# Route definitions
|
|
34
30
|
routes:
|
|
@@ -38,64 +34,19 @@ routes:
|
|
|
38
34
|
changeOrigin: true
|
|
39
35
|
pathRewrite:
|
|
40
36
|
"^/api": ""
|
|
41
|
-
options:
|
|
42
|
-
timeout: 5000
|
|
43
|
-
headers:
|
|
44
|
-
X-Forwarded-Host: "${HOST}"
|
|
45
37
|
|
|
46
|
-
# Example 2: WebSocket proxy
|
|
38
|
+
# Example 2: WebSocket proxy (critical for HMR)
|
|
47
39
|
- path: "/ws"
|
|
48
40
|
target: "ws://localhost:5000"
|
|
49
41
|
ws: true
|
|
50
42
|
changeOrigin: true
|
|
51
43
|
|
|
52
|
-
# Example 3:
|
|
53
|
-
- path: "
|
|
54
|
-
|
|
55
|
-
- "http://localhost:8001"
|
|
56
|
-
- "http://localhost:8002"
|
|
57
|
-
- "http://localhost:8003"
|
|
58
|
-
strategy: "round-robin" # round-robin | random | ip-hash
|
|
59
|
-
pathRewrite:
|
|
60
|
-
"^/balanced": ""
|
|
61
|
-
healthCheck:
|
|
62
|
-
enabled: true
|
|
63
|
-
interval: 30000
|
|
64
|
-
path: "/health"
|
|
65
|
-
timeout: 3000
|
|
66
|
-
|
|
67
|
-
# Example 4: Request/Response transformation
|
|
68
|
-
- path: "/transform/*"
|
|
69
|
-
target: "http://localhost:6000"
|
|
70
|
-
changeOrigin: true
|
|
71
|
-
transform:
|
|
72
|
-
request:
|
|
73
|
-
headers:
|
|
74
|
-
add:
|
|
75
|
-
X-API-Version: "v2"
|
|
76
|
-
remove:
|
|
77
|
-
- "Cookie"
|
|
78
|
-
response:
|
|
79
|
-
headers:
|
|
80
|
-
add:
|
|
81
|
-
X-Proxy-Server: "revx"
|
|
82
|
-
remove:
|
|
83
|
-
- "Server"
|
|
84
|
-
|
|
85
|
-
# Middleware configuration
|
|
86
|
-
middleware:
|
|
87
|
-
# Request ID middleware
|
|
88
|
-
- type: "requestId"
|
|
89
|
-
enabled: true
|
|
90
|
-
headerName: "X-Request-ID"
|
|
91
|
-
|
|
92
|
-
# Compression middleware
|
|
93
|
-
- type: "compression"
|
|
94
|
-
enabled: true
|
|
95
|
-
threshold: 1024
|
|
44
|
+
# Example 3: Static file serving (for Vite build output)
|
|
45
|
+
- path: "/*"
|
|
46
|
+
static: "./dist"
|
|
96
47
|
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
48
|
+
# Example 4: Proxy to Vite dev server
|
|
49
|
+
# - path: "/*"
|
|
50
|
+
# target: "http://localhost:5173"
|
|
51
|
+
# ws: true # Enable WebSocket for HMR
|
|
52
|
+
# changeOrigin: true
|