ninja-reverse-proxy 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +90 -384
  2. package/package.json +11 -5
package/README.md CHANGED
@@ -1,27 +1,14 @@
1
1
  # Ninja Reverse Proxy
2
2
 
3
- A production-grade, backend-agnostic **Layer 7 Reverse Proxy** built from scratch in **TypeScript** and **Node.js**.
3
+ A production-grade, backend-agnostic **Layer 7 Reverse Proxy** built from scratch in TypeScript and Node.js.
4
4
 
5
- Designed in the same philosophy as **Nginx**, **Traefik**, and **HAProxy** — configure it once, point it at any backend, and it handles everything else.
6
-
7
- ```
8
- Developer → GitHub → Jenkins CI/CD → Docker → Any Backend
9
- ```
5
+ Designed in the same philosophy as Nginx, Traefik, and HAProxy — configure it once, point it at any backend, and it handles everything else.
10
6
 
11
7
  ---
12
8
 
13
- ## What it is
9
+ ## Works with any HTTP backend
14
10
 
15
- Ninja Reverse Proxy is a fully configurable, self-contained reverse proxy that works with **any HTTP backend**:
16
-
17
- - Express · Fastify · NestJS
18
- - Django · FastAPI · Flask
19
- - Spring Boot · Quarkus
20
- - Go (net/http, Gin, Echo)
21
- - ASP.NET Core
22
- - Kubernetes Services
23
- - Docker Compose services
24
- - Any service that speaks HTTP
11
+ Express · Fastify · NestJS · Django · FastAPI · Flask · Spring Boot · Go · ASP.NET Core · Kubernetes Services · Docker Compose services · **Any service that speaks HTTP**
25
12
 
26
13
  The proxy never cares what technology runs behind the URLs. You configure upstreams in `config.yaml` and the proxy routes, balances, caches, rate-limits, and health-checks them automatically.
27
14
 
@@ -31,447 +18,166 @@ The proxy never cares what technology runs behind the URLs. You configure upstre
31
18
 
32
19
  | Feature | Details |
33
20
  |---|---|
34
- | **Cluster Architecture** | Master/Worker pattern via Node.js `cluster` — uses all CPU cores |
35
- | **Round-Robin Load Balancing** | Equal request distribution across healthy upstreams |
36
- | **Circuit Breaker** | Marks upstreams DOWN after configurable failure threshold |
37
- | **Redis Response Cache** | GET response caching with configurable TTL; auto-invalidated on writes |
38
- | **Per-Route Rate Limiting** | Sliding window rate limiter per client IP per route |
39
- | **Service Registry** | Backends self-register, deregister, and send heartbeats |
40
- | **Continuous Health Checks** | Every 10 seconds — auto-removes and auto-recovers upstreams |
41
- | **HTTPS / TLS Termination** | Full SSL at the proxy; all HTTP auto-redirected (301) |
42
- | **Auto Scaling** | Optionally spawns/kills upstream servers dynamically based on load |
43
- | **Retry Logic** | Up to 2 retries on upstream failure, each on a different worker |
44
- | **Graceful Shutdown** | Drains all connections on SIGTERM / SIGINT |
45
- | **Admin API** | Live stats for load balancer, cache, registry, auto scaler |
46
- | **YAML Configuration** | One file, fully validated with Zod no source code changes needed |
47
- | **Docker + Kubernetes** | Ships with Compose and K8s manifests out of the box |
48
- | **Jenkins CI/CD** | Declarative pipeline included |
49
- | **SonarQube** | `sonar-project.properties` included for static analysis |
50
-
51
- ---
52
-
53
- ## Architecture
54
-
55
- ```
56
- ┌─────────────────────────────────────────────────┐
57
- │ Ninja Reverse Proxy │
58
- │ │
59
- Client ──HTTPS──► │ Master Process │
60
- │ ├── Rate Limiter (per-IP, per-route) │
61
- │ ├── Redis Cache (GET responses) │
62
- │ ├── Load Balancer (round-robin / ip-hash / …) │
63
- │ ├── Health Checker (every 10s) │
64
- │ ├── Service Registry │
65
- │ └── Auto Scaler (optional) │
66
- │ │
67
- │ Worker Processes (one per CPU core) │
68
- │ └── Forward requests via keepAlive TCP │
69
- └───────────┬─────────────────────────────────────┘
70
-
71
- ┌───────────────┼───────────────┐
72
- ▼ ▼ ▼
73
- backend-a backend-b backend-c
74
- (Express) (Django) (Spring Boot)
75
- ```
76
-
77
- ### CI/CD Flow
78
-
79
- ```
80
- Developer
81
- │ edits code / config
82
-
83
- GitHub
84
-
85
- Jenkins (Jenkinsfile)
86
- ├── Setup (npm ci)
87
- ├── Static Analysis — Lint + npm audit (parallel)
88
- ├── Unit Tests
89
- ├── Build Artifact (tsc)
90
- └── Deploy to Production (main branch, prod env)
91
- ```
21
+ | Cluster Architecture | Master/Worker pattern via Node.js cluster — uses all CPU cores |
22
+ | Round-Robin Load Balancing | Equal request distribution across healthy upstreams |
23
+ | Circuit Breaker | Marks upstreams DOWN after configurable failure threshold |
24
+ | Redis Response Cache | GET response caching with configurable TTL |
25
+ | Per-Route Rate Limiting | Sliding window rate limiter per client IP per route |
26
+ | Service Registry | Backends self-register, deregister, and send heartbeats |
27
+ | Continuous Health Checks | Every 10 seconds — auto-removes and auto-recovers upstreams |
28
+ | HTTPS / TLS Termination | Full SSL at the proxy; all HTTP auto-redirected (301) |
29
+ | Sticky Sessions | Cookie-based session affinity per upstream |
30
+ | Retry Logic | Up to 2 retries on upstream failure, each on a different upstream |
31
+ | Graceful Shutdown | Drains all connections on SIGTERM / SIGINT |
32
+ | Hot Reload | Watches `config.yaml` and `config.d/` zero-downtime config updates |
33
+ | Admin API | Live stats for load balancer, cache, registry, auto scaler |
34
+ | YAML Configuration | One file, fully validated with Zod no source code changes needed |
92
35
 
93
36
  ---
94
37
 
95
38
  ## Quick Start
96
39
 
97
- ### 1. Clone and generate TLS certificates
40
+ ### 1. Install globally
98
41
 
99
42
  ```bash
100
- git clone https://github.com/praveenkumar-co/reverse-proxy.git
101
- cd reverse-proxy
102
-
103
- # Generate a self-signed certificate (for development)
104
- openssl req -x509 -newkey rsa:4096 \
105
- -keyout key.pem -out cert.pem \
106
- -days 365 -nodes
43
+ npm install -g ninja-reverse-proxy
107
44
  ```
108
45
 
109
- ### 2. Configure your backends
46
+ ### 2. Generate TLS certificates
110
47
 
111
48
  ```bash
112
- cp config.example.yaml config.yaml
113
- ```
114
-
115
- Edit `config.yaml` — replace the example upstreams with your real backend URLs:
116
-
117
- ```yaml
118
- upstreams:
119
- - id: my-api
120
- url: http://my-api:8000
121
-
122
- - id: my-frontend
123
- url: http://my-frontend:3000
124
- ```
125
-
126
- ### 3. Run
127
-
128
- ```bash
129
- # Proxy + Redis only (you bring your own backends)
130
- docker-compose up --build
131
-
132
- # Or run the built-in demo (backend-a + backend-b included)
133
- cd examples/docker-compose && docker-compose up --build
134
- ```
135
-
136
- ### 4. Test
137
-
138
- ```bash
139
- curl -k https://localhost:8443/
140
- curl -k https://localhost:8443/__lb-stats
141
- curl -k https://localhost:8443/__registry
49
+ openssl req -x509 -newkey rsa:4096 \
50
+ -keyout key.pem -out cert.pem \
51
+ -days 365 -nodes \
52
+ -subj "/CN=localhost"
142
53
  ```
143
54
 
144
- ---
145
-
146
- ## Configuration Reference
147
-
148
- `config.example.yaml` is the fully commented template. Copy it to `config.yaml` to get started.
55
+ ### 3. Create your config file
149
56
 
150
57
  ```yaml
58
+ # config.yaml
151
59
  server:
152
-
153
- listen: 8080 # HTTP port — redirects all traffic to HTTPS
154
- httpsPort: 8443 # HTTPS port — main entry point
155
- workers: 2 # Set to your CPU core count (run: nproc)
60
+ listen: 8080
61
+ httpsPort: 8443
62
+ workers: 2
156
63
 
157
64
  loadBalancing:
158
- strategy: round-robin # round-robin | least-connections | ip-hash | random
159
- failureThreshold: 3 # Mark upstream DOWN after 3 consecutive failures
160
- recoveryTimeMs: 15000 # Retry a DOWN upstream after 15 seconds
161
-
162
- autoScaling:
163
- enabled: false # false → static backends (like Nginx)
164
- # true → dynamic server spawning based on load
165
- minServers: 2
166
- maxServers: 10
167
- scaleUpAt: 10 # Spawn a server when connections exceed this
168
- scaleDownAt: 2 # Kill a server when connections drop below this
169
- cooldownMs: 60000
170
- startPort: 9000
171
- proxyPort: 8080
172
-
173
- cache:
174
- enabled: false # true → cache GET responses in Redis
175
- host: redis
176
- port: 6379
177
- ttlSeconds: 60
65
+ strategy: round-robin
66
+ failureThreshold: 3
67
+ recoveryTimeMs: 15000
68
+ retry:
69
+ maxAttempts: 2
70
+ statusCodes: [502, 503, 504]
178
71
 
179
72
  upstreams:
180
- - id: backend-a # Any name — used to reference in paths
181
- url: http://backend-a:3001 # Any HTTP URL — any technology
182
-
183
- - id: backend-b
184
- url: http://backend-b:3002
73
+ - id: my-api
74
+ url: http://localhost:5000
75
+ - id: my-api-2
76
+ url: http://localhost:5001
185
77
 
186
78
  paths:
187
- - path: / # Route all traffic to both backends
188
- upstream:
189
- - backend-a
190
- - backend-b
191
- rateLimit:
192
- windowMs: 60000 # 1-minute window
193
- maxRequests: 100000 # per client IP
194
-
195
- - path: /api # Route /api to a specific backend only
196
- upstream:
197
- - backend-a
79
+ - path: /
80
+ upstream: [my-api, my-api-2]
198
81
 
199
82
  headers:
200
83
  - key: X-Forwarded-For
201
84
  value: client_ip
202
- - key: X-Real-IP
203
- value: client_ip
204
- ```
205
-
206
- ---
207
-
208
- ## Docker
209
85
 
210
- ### Build the proxy image
211
-
212
- ```bash
213
- docker build -t ninja-reverse-proxy:latest .
86
+ cache:
87
+ enabled: false
88
+ host: redis
89
+ port: 6379
90
+ ttlSeconds: 60
214
91
  ```
215
92
 
216
- ### Run with Docker Compose (proxy + Redis only)
93
+ ### 4. Start the proxy
217
94
 
218
95
  ```bash
219
- # Uses docker-compose.yml in project root
220
- docker-compose up --build
96
+ ninja-proxy --config config.yaml
221
97
  ```
222
98
 
223
- Your own backend services connect to the `proxy-network` and are listed in `config.yaml`.
224
-
225
- ### Run the full built-in demo
99
+ ### 5. Test it
226
100
 
227
101
  ```bash
228
- # Spins up proxy + redis + backend-a + backend-b
229
- cd examples/docker-compose
230
- docker-compose up --build
231
- ```
232
-
233
- ### Connect your real app
234
-
235
- Add your app as a service in your own `docker-compose.yml` and connect it to the proxy network:
236
-
237
- ```yaml
238
- services:
239
-
240
- my-express-app:
241
- image: my-express-app:latest
242
- networks:
243
- - proxy-network
244
-
245
- networks:
246
- proxy-network:
247
- external: true
248
- name: reverse-proxy_proxy-network
249
- ```
250
-
251
- Then add it to `config.yaml`:
252
-
253
- ```yaml
254
- upstreams:
255
- - id: my-express-app
256
- url: http://my-express-app:3000
102
+ curl -k https://localhost:8443/
103
+ curl -k https://localhost:8443/__lb-stats
257
104
  ```
258
105
 
259
106
  ---
260
107
 
261
- ## Kubernetes
262
-
263
- See [`k8s/README.md`](k8s/README.md) for the full deployment guide.
108
+ ## Dynamic Service Registry
264
109
 
265
- Quick overview:
110
+ Backends can self-register at runtime without touching `config.yaml`:
266
111
 
267
112
  ```bash
268
- # Apply all manifests
269
- kubectl apply -f k8s/
113
+ # Register a new upstream dynamically
114
+ curl -X POST -H "Content-Type: application/json" \
115
+ -d '{"id":"my-service","url":"http://localhost:3000"}' \
116
+ http://localhost:8080/__registry/register
270
117
 
271
- # Port-forward for local testing
272
- kubectl port-forward svc/ninja-reverse-proxy-svc 8080:8080 8443:8443
118
+ # Send a heartbeat
119
+ curl -X PUT http://localhost:8080/__registry/heartbeat/my-service
273
120
 
274
- # Verify
275
- curl -k https://localhost:8443/__lb-stats
121
+ # Deregister
122
+ curl -X DELETE http://localhost:8080/__registry/deregister/my-service
276
123
  ```
277
124
 
278
- The proxy config is mounted as a Kubernetes ConfigMap — change it without rebuilding the image.
279
-
280
125
  ---
281
126
 
282
- ## Auto Scaler
127
+ ## Multi-Tenant Config (Nginx-style)
283
128
 
284
- The Auto Scaler is **fully optional** and controlled entirely by `config.yaml`.
129
+ Drop extra config files into a `config.d/` folder the proxy merges them automatically with zero downtime:
285
130
 
286
- ### Static mode (default — works like Nginx)
287
-
288
- ```yaml
289
- autoScaling:
290
- enabled: false
291
- ```
292
-
293
- The proxy uses only the upstream servers listed in `config.yaml`. This is appropriate for production setups where you manage your own backend services.
294
-
295
- ### Dynamic mode
131
+ ```bash
132
+ mkdir config.d
296
133
 
297
- ```yaml
298
- autoScaling:
299
- enabled: true
300
- minServers: 2
301
- maxServers: 10
302
- scaleUpAt: 10 # spawn a new server when total active connections exceed this
303
- scaleDownAt: 2 # kill the oldest server when connections drop below this
304
- cooldownMs: 60000 # minimum wait between scale events
134
+ # Each tenant/app gets their own isolated file
135
+ echo "
136
+ server:
137
+ upstreams:
138
+ - id: tenant-a
139
+ url: http://localhost:4000
140
+ paths:
141
+ - path: /tenant-a
142
+ upstream: [tenant-a]
143
+ " > config.d/tenant-a.yaml
305
144
  ```
306
145
 
307
- When enabled, the proxy dynamically spawns (`server-template.js`) and kills backend servers based on active connection count. See `examples/docker-compose/` for a working demonstration.
146
+ The proxy hot-reloads `config.d/` automatically whenever a file is added or changed.
308
147
 
309
148
  ---
310
149
 
311
150
  ## Admin API
312
151
 
313
- All admin endpoints are available on the HTTPS port.
314
-
315
152
  | Endpoint | Method | Description |
316
153
  |---|---|---|
317
154
  | `/__lb-stats` | GET | Load balancer stats + healthy upstreams |
318
155
  | `/__cache-stats` | GET | Redis cache hit/miss stats |
319
156
  | `/__registry` | GET | All registered services |
320
- | `/__autoscaler-stats` | GET | Auto scaler status |
321
157
  | `/__registry/register` | POST | Register a new upstream |
322
158
  | `/__registry/deregister/:id` | DELETE | Deregister an upstream |
323
159
  | `/__registry/heartbeat/:id` | PUT | Upstream heartbeat ping |
324
160
 
325
- ```bash
326
- curl -k https://localhost:8443/__lb-stats
327
- curl -k https://localhost:8443/__cache-stats
328
- curl -k https://localhost:8443/__registry
329
- curl -k https://localhost:8443/__autoscaler-stats
330
- ```
331
-
332
- ---
333
-
334
- ## Jenkins CI/CD Pipeline
335
-
336
- A declarative `Jenkinsfile` is included in the project root.
337
-
338
- **Pipeline stages:**
339
-
340
- 1. **Setup** — `npm ci` (deterministic install)
341
- 2. **Static Analysis** — Lint + `npm audit` (parallel)
342
- 3. **Unit Tests** — conditional on `RUN_TESTS` parameter
343
- 4. **Build Artifact** — `npm run build` (TypeScript → JavaScript)
344
- 5. **Deploy to Production** — branch `main` + `ENV_TYPE=prod` + manual approval gate
345
-
346
- **Global options:** 1-hour timeout · last 10 builds retained · no concurrent builds · timestamps on every log line.
347
-
348
161
  ---
349
162
 
350
- ## SonarQube
351
-
352
- `sonar-project.properties` is included. To run a scan:
353
-
354
- ```bash
355
- sonar-scanner \
356
- -Dsonar.host.url=http://localhost:9000 \
357
- -Dsonar.login=YOUR_SONAR_TOKEN
358
- ```
359
-
360
- Scans the `src/` directory. Excludes `node_modules/`, `dist/`, certificates, and CI files.
361
-
362
- ---
363
-
364
- ## Security Scanning (Trivy)
365
-
366
- ```bash
367
- # Scan dependencies
368
- trivy fs .
369
-
370
- # Scan the Docker image
371
- docker build -t ninja-reverse-proxy:latest .
372
- trivy image ninja-reverse-proxy:latest
373
- ```
374
-
375
- ---
376
-
377
- ## Project Structure
378
-
379
- ```
380
- ninja-reverse-proxy/
381
- ├── src/ ← Proxy source (TypeScript)
382
- │ ├── index.ts → CLI entry point
383
- │ ├── server.ts → Master + Worker proxy engine
384
- │ ├── loadBalancer.ts → Round-robin with circuit breaker
385
- │ ├── auto-scaler.ts → Dynamic server scaling
386
- │ ├── health.ts → Health checker (initial + continuous)
387
- │ ├── Serviceregistry.ts → Service registry (register/heartbeat/deregister)
388
- │ ├── cache.ts → Redis cache (get/set/invalidate/stats)
389
- │ ├── rate-limiter.ts → Sliding window rate limiter
390
- │ ├── config-schema.ts → Zod config validation schema
391
- │ ├── config.ts → YAML parser
392
- │ └── server-schema.ts → Worker IPC message schema
393
-
394
- ├── k8s/ ← Kubernetes manifests
395
- │ ├── configmap.yaml
396
- │ ├── tls-secret.yaml
397
- │ ├── proxy-deployment.yaml
398
- │ ├── proxy-service.yaml
399
- │ └── README.md
400
-
401
- ├── examples/ ← Integration examples (NOT part of the proxy)
402
- │ ├── docker-compose/ ← Full demo stack (backend-a + backend-b)
403
- │ │ ├── server-template.js → Minimal demo backend
404
- │ │ ├── Dockerfile.server → Demo backend image
405
- │ │ ├── docker-compose.yml → Full demo stack
406
- │ │ ├── config.yaml → Demo config
407
- │ │ └── README.md
408
- │ └── express/ ← Express.js integration example
409
- │ ├── server.js
410
- │ └── README.md
411
-
412
- ├── Dockerfile ← Proxy image (multi-stage build)
413
- ├── Jenkinsfile ← CI/CD declarative pipeline
414
- ├── sonar-project.properties ← SonarQube config
415
- ├── docker-compose.yml ← Proxy + Redis only
416
- ├── config.example.yaml ← Fully commented configuration template
417
- ├── .dockerignore
418
- ├── .gitignore
419
- ├── package.json
420
- ├── tsconfig.json
421
- └── README.md
422
- ```
423
-
424
- ---
425
-
426
- ## Tech Stack
427
-
428
- | Technology | Purpose |
429
- |---|---|
430
- | TypeScript | Full type safety |
431
- | Node.js Cluster | Master/Worker multi-process architecture |
432
- | Redis | Response caching |
433
- | Zod | Schema validation for config and worker messages |
434
- | YAML | Human-readable configuration |
435
- | Commander | CLI entry point |
436
- | Docker Compose | Orchestration |
437
- | Kubernetes | Production cluster deployment |
438
- | Jenkins | CI/CD pipeline |
439
- | SonarQube | Static code analysis |
440
- | Trivy | Container security scanning |
441
-
442
- ---
443
-
444
- ## Request Lifecycle
445
-
446
- ```
447
- Client → :8080 HTTP → 301 redirect to HTTPS
448
- Client → :8443 HTTPS
449
- 1. Rate limiter checks client IP — 429 if exceeded
450
- 2. GET requests → Redis cache checked — HIT returns instantly
451
- 3. Write requests (POST/PUT/PATCH/DELETE) → cache invalidated
452
- 4. Request body assembled from chunks
453
- 5. Load balancer picks a healthy upstream
454
- 6. Worker process forwards request over keepAlive TCP
455
- 7. Upstream responds → reply sent back via IPC
456
- 8. Master sends response to client + caches (GET)
457
- 9. On failure → circuit breaker records it, retry up to 2 times
458
- ```
459
-
460
- ---
461
-
462
- ## Performance Tips
163
+ ## Configuration Reference
463
164
 
464
- - Set `workers` to `nproc` never exceed your CPU core count
465
- - Set `cache.enabled: true` for read-heavy APIs — reduces upstream load significantly
466
- - Keep `autoScaling.maxServers` realistic for your hardware (3–4 for a 4-core machine)
467
- - `keepAlive: true` is set by default avoids TCP handshake overhead per request
468
- - Monitor `/__lb-stats` in production to see which upstreams are under load
165
+ | Field | Default | Description |
166
+ |---|---|---|
167
+ | `server.listen` | `8080` | HTTP port redirects all traffic to HTTPS |
168
+ | `server.httpsPort` | `8443` | HTTPS portmain entry point |
169
+ | `server.workers` | `2` | Set to your CPU core count |
170
+ | `loadBalancing.strategy` | `round-robin` | `round-robin` \| `least-connections` \| `ip-hash` \| `random` |
171
+ | `loadBalancing.failureThreshold` | `3` | Mark upstream DOWN after N consecutive failures |
172
+ | `loadBalancing.recoveryTimeMs` | `15000` | Retry a DOWN upstream after N ms |
173
+ | `cache.enabled` | `false` | Enable Redis GET response caching |
174
+ | `cache.ttlSeconds` | `60` | Cache TTL in seconds |
469
175
 
470
176
  ---
471
177
 
472
- ## Contributing
178
+ ## Repository
473
179
 
474
- Pull requests are welcome. Please open an issue first to discuss significant changes.
180
+ [github.com/praveenkumar-co/reverse-proxy](https://github.com/praveenkumar-co/reverse-proxy)
475
181
 
476
182
  ---
477
183
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ninja-reverse-proxy",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A production-grade reverse proxy with load balancing, circuit breaker, sticky sessions, and dynamic service registry.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,7 +13,16 @@
13
13
  "dev": "tsc-watch --onSuccess \"node dist/index.js --config config.yaml\"",
14
14
  "test": "tsc -p tsconfig.test.json && node --test dist-test/test"
15
15
  },
16
- "keywords": ["reverse-proxy", "load-balancer", "circuit-breaker", "nginx", "gateway", "sticky-session", "rate-limiter", "proxy"],
16
+ "keywords": [
17
+ "reverse-proxy",
18
+ "load-balancer",
19
+ "circuit-breaker",
20
+ "nginx",
21
+ "gateway",
22
+ "sticky-session",
23
+ "rate-limiter",
24
+ "proxy"
25
+ ],
17
26
  "author": "praveenkumar-co",
18
27
  "license": "ISC",
19
28
  "repository": {
@@ -29,10 +38,7 @@
29
38
  },
30
39
  "dependencies": {
31
40
  "commander": "^14.0.3",
32
- "express": "^5.2.1",
33
- "node-utils-kit": "^1.2.4",
34
41
  "redis": "^5.11.0",
35
- "scalable-bloom-kit": "^1.0.6",
36
42
  "yaml": "^2.8.2",
37
43
  "zod": "^4.3.6"
38
44
  }