@gagandeep023/api-gateway 0.4.1 → 0.5.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/GUIDE.md +422 -0
- package/dist/backend/index.d.mts +23 -3
- package/dist/backend/index.d.ts +23 -3
- package/dist/backend/index.js +164 -35
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/index.mjs +149 -21
- package/dist/backend/index.mjs.map +1 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +166 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +150 -21
- package/dist/index.mjs.map +1 -1
- package/dist/types/index.d.mts +26 -1
- package/dist/types/index.d.ts +26 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -2
package/GUIDE.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# @gagandeep023/api-gateway - Complete Guide
|
|
2
|
+
|
|
3
|
+
A type-safe Express API gateway with IP-based rate limiting, real-time analytics, device authentication, and a React dashboard. No database required.
|
|
4
|
+
|
|
5
|
+
- **npm**: `npm install @gagandeep023/api-gateway`
|
|
6
|
+
- **GitHub**: [github.com/Gagandeep023/api-gateway](https://github.com/Gagandeep023/api-gateway)
|
|
7
|
+
- **npm page**: [npmjs.com/package/@gagandeep023/api-gateway](https://www.npmjs.com/package/@gagandeep023/api-gateway)
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Why This Package](#why-this-package)
|
|
12
|
+
- [Setup Guide](#setup-guide)
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Backend Setup](#backend-setup)
|
|
15
|
+
- [Frontend Dashboard](#frontend-dashboard)
|
|
16
|
+
- [Custom Configuration](#custom-configuration)
|
|
17
|
+
- [Device Authentication (TOTP)](#device-authentication-totp)
|
|
18
|
+
- [Architecture](#architecture)
|
|
19
|
+
- [Rate Limiting Algorithms](#rate-limiting-algorithms)
|
|
20
|
+
- [Technical Challenges](#technical-challenges)
|
|
21
|
+
- [Learnings](#learnings)
|
|
22
|
+
- [API Reference](#api-reference)
|
|
23
|
+
- [Theming](#theming)
|
|
24
|
+
- [TypeScript Support](#typescript-support)
|
|
25
|
+
- [Contributing](#contributing)
|
|
26
|
+
|
|
27
|
+
## Why This Package
|
|
28
|
+
|
|
29
|
+
Most rate limiting solutions require Redis, Memcached, or some external store. That is the right call for horizontally scaled production systems. But many Express apps run as a single process, where external dependencies add operational complexity without proportional benefit.
|
|
30
|
+
|
|
31
|
+
This package gives you:
|
|
32
|
+
|
|
33
|
+
- **Three rate limiting algorithms** (Token Bucket, Sliding Window, Fixed Window) with configurable tiers, so you can pick the right algorithm for each use case instead of settling for one.
|
|
34
|
+
- **Zero external dependencies** beyond Express. Rate limit state, analytics, and device registry all run in-memory. No Redis, no database, no cache layer to manage.
|
|
35
|
+
- **A real-time dashboard** out of the box. SSE-powered analytics with request charts, error rates, and API key management. Drop in a React component and you have a monitoring panel.
|
|
36
|
+
- **Device-level TOTP authentication** for browser-based access control without manual API key entry.
|
|
37
|
+
- **Full TypeScript support** with exported types, subpath exports, and strict type checking.
|
|
38
|
+
|
|
39
|
+
**Use this if:**
|
|
40
|
+
- You run a single-instance Express app and want rate limiting without Redis
|
|
41
|
+
- You want a drop-in monitoring dashboard for your API
|
|
42
|
+
- You are learning system design and want to see real implementations of rate limiting algorithms
|
|
43
|
+
- You need configurable per-tier rate limits with different algorithms per tier
|
|
44
|
+
- You want device-based auth without a full OAuth setup
|
|
45
|
+
|
|
46
|
+
**Do not use this if:**
|
|
47
|
+
- You run multiple server instances behind a load balancer (rate limit state is per-process)
|
|
48
|
+
- You need persistent analytics that survive server restarts
|
|
49
|
+
- You need sub-millisecond rate limiting at massive scale (use Redis + Lua scripts)
|
|
50
|
+
|
|
51
|
+
## Setup Guide
|
|
52
|
+
|
|
53
|
+
### Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install @gagandeep023/api-gateway
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Peer dependencies** (install the ones you need):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Required for backend
|
|
63
|
+
npm install express
|
|
64
|
+
|
|
65
|
+
# Required for frontend dashboard
|
|
66
|
+
npm install react react-dom recharts
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**TypeScript requirement**: Your `tsconfig.json` must use `module: "Node16"` or `"NodeNext"` for subpath exports to resolve correctly.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"compilerOptions": {
|
|
74
|
+
"module": "Node16",
|
|
75
|
+
"moduleResolution": "Node16"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Backend Setup
|
|
81
|
+
|
|
82
|
+
Minimal setup with default configuration:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import express from 'express';
|
|
86
|
+
import { createGatewayMiddleware, createGatewayRoutes } from '@gagandeep023/api-gateway/backend';
|
|
87
|
+
|
|
88
|
+
const app = express();
|
|
89
|
+
app.use(express.json());
|
|
90
|
+
|
|
91
|
+
// Create gateway with defaults (free tier: 100 req/min token bucket)
|
|
92
|
+
const gateway = createGatewayMiddleware();
|
|
93
|
+
|
|
94
|
+
// Mount management routes FIRST (bypasses rate limiting)
|
|
95
|
+
app.use('/api/gateway', createGatewayRoutes({
|
|
96
|
+
rateLimiterService: gateway.rateLimiterService,
|
|
97
|
+
analyticsService: gateway.analyticsService,
|
|
98
|
+
config: gateway.config,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
// Apply rate limiting to all /api routes
|
|
102
|
+
app.use('/api', gateway.middleware);
|
|
103
|
+
|
|
104
|
+
// Your routes go after the middleware
|
|
105
|
+
app.get('/api/hello', (req, res) => {
|
|
106
|
+
res.json({ message: 'Hello, world!' });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
app.listen(3001, () => {
|
|
110
|
+
console.log('Server running on port 3001');
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The management routes (`/api/gateway/*`) are mounted before the middleware intentionally. If the rate limiter blocked the analytics endpoint, the dashboard would become unusable during a rate limit storm.
|
|
115
|
+
|
|
116
|
+
### Frontend Dashboard
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { GatewayDashboard } from '@gagandeep023/api-gateway/frontend';
|
|
120
|
+
import '@gagandeep023/api-gateway/frontend/styles.css';
|
|
121
|
+
|
|
122
|
+
function App() {
|
|
123
|
+
return (
|
|
124
|
+
<GatewayDashboard
|
|
125
|
+
apiBaseUrl="http://localhost:3001/api"
|
|
126
|
+
apiKey="optional-api-key"
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The dashboard connects via SSE for live updates every 5 seconds. It shows:
|
|
133
|
+
- Requests per minute (line chart, last 20 data points)
|
|
134
|
+
- Top endpoints (bar chart)
|
|
135
|
+
- Stats grid: total requests, RPM, error rate, response time, rate limit hits, active IPs, key sessions
|
|
136
|
+
- Recent requests table (paginated, 20 per page)
|
|
137
|
+
- API key management: create, revoke, copy keys
|
|
138
|
+
- Gateway configuration display
|
|
139
|
+
|
|
140
|
+
### Custom Configuration
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const gateway = createGatewayMiddleware({
|
|
144
|
+
rateLimits: {
|
|
145
|
+
tiers: {
|
|
146
|
+
// Token Bucket: smooth, burst-friendly
|
|
147
|
+
free: {
|
|
148
|
+
algorithm: 'tokenBucket',
|
|
149
|
+
maxRequests: 100,
|
|
150
|
+
windowMs: 60000,
|
|
151
|
+
refillRate: 10,
|
|
152
|
+
},
|
|
153
|
+
// Sliding Window: accurate, no boundary exploits
|
|
154
|
+
pro: {
|
|
155
|
+
algorithm: 'slidingWindow',
|
|
156
|
+
maxRequests: 1000,
|
|
157
|
+
windowMs: 60000,
|
|
158
|
+
},
|
|
159
|
+
// No rate limiting
|
|
160
|
+
unlimited: {
|
|
161
|
+
algorithm: 'none',
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
defaultTier: 'free',
|
|
165
|
+
globalLimit: {
|
|
166
|
+
maxRequests: 10000,
|
|
167
|
+
windowMs: 60000,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
ipRules: {
|
|
171
|
+
allowlist: [],
|
|
172
|
+
blocklist: ['10.0.0.1', '192.168.1.100'],
|
|
173
|
+
mode: 'blocklist', // or 'allowlist'
|
|
174
|
+
},
|
|
175
|
+
apiKeys: {
|
|
176
|
+
keys: [
|
|
177
|
+
{
|
|
178
|
+
id: 'key_001',
|
|
179
|
+
key: 'gw_live_your_secret_key',
|
|
180
|
+
name: 'Production App',
|
|
181
|
+
tier: 'pro',
|
|
182
|
+
createdAt: new Date().toISOString(),
|
|
183
|
+
active: true,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Device Authentication (TOTP)
|
|
191
|
+
|
|
192
|
+
Enable browser-level authentication without manual API key entry:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { createGatewayMiddleware, createGatewayRoutes, createDeviceAuthRoutes } from '@gagandeep023/api-gateway/backend';
|
|
196
|
+
|
|
197
|
+
// Mount device auth routes
|
|
198
|
+
app.use('/api/auth', createDeviceAuthRoutes({
|
|
199
|
+
rateLimiterService: gateway.rateLimiterService,
|
|
200
|
+
}));
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**How it works:**
|
|
204
|
+
|
|
205
|
+
1. Browser generates a UUID (`browserId`) and sends `POST /api/auth/register`
|
|
206
|
+
2. Server stores the device and returns a shared secret
|
|
207
|
+
3. Browser computes TOTP codes using HMAC-SHA256 with 1-hour windows
|
|
208
|
+
4. Requests include `X-API-Key: totp_<browserId>_<code>`
|
|
209
|
+
5. Server validates the code against current and previous window (clock skew tolerance)
|
|
210
|
+
|
|
211
|
+
**Security features:**
|
|
212
|
+
- Timing-safe comparison (crypto.timingSafeEqual) prevents timing attacks
|
|
213
|
+
- Registration rate limiting: 10 per minute per IP, max 30 per IP total
|
|
214
|
+
- Automatic device expiration after 1 week
|
|
215
|
+
- Debounced file persistence (2-second batching)
|
|
216
|
+
|
|
217
|
+
## Architecture
|
|
218
|
+
|
|
219
|
+
### Middleware Pipeline
|
|
220
|
+
|
|
221
|
+
Every request passes through four stages sequentially. Each stage can short-circuit with an HTTP error.
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
Request --> [Logger] --> [API Key Auth] --> [IP Filter] --> [Rate Limiter] --> Route Handler
|
|
225
|
+
| | | |
|
|
226
|
+
| 401 403 429
|
|
227
|
+
| (bad key) (blocked IP) (limit hit)
|
|
228
|
+
|
|
|
229
|
+
v
|
|
230
|
+
[res.finish callback] --> Analytics circular buffer
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Why this order matters:**
|
|
234
|
+
1. **Logger first**: Every request is captured, including rejected ones
|
|
235
|
+
2. **Auth second**: Rate limiter needs client identity and tier
|
|
236
|
+
3. **IP filter third**: Reject blocked IPs before consuming rate limit tokens
|
|
237
|
+
4. **Rate limiter last**: Needs all context from prior stages
|
|
238
|
+
|
|
239
|
+
### State Architecture
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
IN-MEMORY (ephemeral) CONFIGURABLE
|
|
243
|
+
+----------------------------+ +----------------------------+
|
|
244
|
+
| Rate Limiter State | | Tier definitions |
|
|
245
|
+
| Per-IP token counts | | Algorithm per tier |
|
|
246
|
+
| Sliding window logs | | Global limit settings |
|
|
247
|
+
| Fixed window counters | | API keys + tiers |
|
|
248
|
+
| | | IP allowlist/blocklist |
|
|
249
|
+
| Analytics Circular Buffer | +----------------------------+
|
|
250
|
+
| 10,000 entries max |
|
|
251
|
+
| ~2 MB memory cap | FILE-BASED (persistent)
|
|
252
|
+
| | +----------------------------+
|
|
253
|
+
| Device Registry | | devices.json |
|
|
254
|
+
| Active browser devices | | Debounced writes (2s) |
|
|
255
|
+
+----------------------------+ +----------------------------+
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Rate limit state resets on server restart. This is by design: a restart gives every client a fresh allowance.
|
|
259
|
+
|
|
260
|
+
## Rate Limiting Algorithms
|
|
261
|
+
|
|
262
|
+
### Token Bucket (recommended for most use cases)
|
|
263
|
+
|
|
264
|
+
- Tokens refill at a constant rate; each request consumes one token
|
|
265
|
+
- Allows controlled bursts up to bucket capacity
|
|
266
|
+
- O(1) memory per client (16 bytes: tokens + lastRefill)
|
|
267
|
+
- Lazy evaluation: zero CPU between requests
|
|
268
|
+
- Used by: AWS API Gateway, Stripe, GitHub API
|
|
269
|
+
|
|
270
|
+
### Sliding Window Log (highest accuracy)
|
|
271
|
+
|
|
272
|
+
- Stores timestamp of every request in a rolling window
|
|
273
|
+
- No boundary problem: true rolling count
|
|
274
|
+
- O(N) memory per client where N = max requests per window
|
|
275
|
+
- Best for: billing systems, compliance-sensitive APIs
|
|
276
|
+
- Trade-off: higher memory usage at scale
|
|
277
|
+
|
|
278
|
+
### Fixed Window Counter (used for global limits)
|
|
279
|
+
|
|
280
|
+
- Simple counter that resets at fixed intervals
|
|
281
|
+
- O(1) memory per client (16 bytes: count + windowStart)
|
|
282
|
+
- Known boundary problem: 2x burst possible at window edges
|
|
283
|
+
- Best for: global rate limits where approximate enforcement is acceptable
|
|
284
|
+
|
|
285
|
+
### Two-Level Enforcement
|
|
286
|
+
|
|
287
|
+
Every request must pass both a global limit (Fixed Window, shared across all clients) and a per-tier limit (algorithm depends on tier config). The global limit prevents infrastructure overload; tier limits enforce SLA boundaries.
|
|
288
|
+
|
|
289
|
+
## Technical Challenges
|
|
290
|
+
|
|
291
|
+
### TypeScript Subpath Exports
|
|
292
|
+
|
|
293
|
+
The package uses Node.js subpath exports (`./backend`, `./frontend`, `./types`) to separate concerns. TypeScript only resolves these correctly with `module: "Node16"` or `"NodeNext"` in the consumer's tsconfig. Older module settings silently fail to find types. This is a common pain point with npm packages that use subpath exports and is documented as a requirement.
|
|
294
|
+
|
|
295
|
+
### SSE Through Nginx
|
|
296
|
+
|
|
297
|
+
Server-Sent Events work perfectly in development but break behind Nginx with default buffering. Events accumulate in the proxy buffer instead of streaming. The fix requires specific Nginx configuration:
|
|
298
|
+
|
|
299
|
+
```nginx
|
|
300
|
+
location /api/gateway/analytics/live {
|
|
301
|
+
proxy_pass http://localhost:3001;
|
|
302
|
+
proxy_buffering off;
|
|
303
|
+
proxy_set_header X-Accel-Buffering no;
|
|
304
|
+
proxy_set_header Cache-Control no-cache;
|
|
305
|
+
proxy_set_header Connection '';
|
|
306
|
+
proxy_http_version 1.1;
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### EventSource Cannot Send Headers
|
|
311
|
+
|
|
312
|
+
The browser's native `EventSource` API does not support custom headers. This breaks API key authentication on SSE connections. The fix was replacing EventSource with a fetch-based SSE reader using `ReadableStream` and `TextDecoder` to manually parse the `text/event-stream` format while maintaining full header control.
|
|
313
|
+
|
|
314
|
+
### Circular Buffer Read Order
|
|
315
|
+
|
|
316
|
+
When a circular buffer wraps around, the oldest entry is at the head pointer, not index 0. Reading in chronological order requires slicing the buffer into two segments (head-to-end, then 0-to-head) and concatenating them. Off-by-one errors in this logic caused out-of-order display in the dashboard's recent requests table.
|
|
317
|
+
|
|
318
|
+
### TOTP Timing Safety
|
|
319
|
+
|
|
320
|
+
Standard string comparison (`===`) leaks timing information. An attacker can brute-force TOTP codes character by character by measuring response times. The fix uses `crypto.timingSafeEqual`, which always compares all bytes regardless of mismatch position. Both inputs must be normalized to fixed-length Buffers before comparison.
|
|
321
|
+
|
|
322
|
+
### Peer Dependency Version Ranges
|
|
323
|
+
|
|
324
|
+
React 18 and 19, and Recharts 2 and 3, have different internal APIs. The package supports both major versions (`react ^18.0.0 || ^19.0.0`, `recharts ^2.0.0 || ^3.0.0`) by avoiding APIs that changed between versions.
|
|
325
|
+
|
|
326
|
+
## Learnings
|
|
327
|
+
|
|
328
|
+
### Algorithms Are Simple, Systems Are Hard
|
|
329
|
+
|
|
330
|
+
Token Bucket is two numbers and a time delta. Sliding Window is an array filter. Fixed Window is a counter with a timestamp. The real complexity is middleware ordering, state isolation per client, HTTP header conventions, and making all pieces work together.
|
|
331
|
+
|
|
332
|
+
### Memory Bounds Matter
|
|
333
|
+
|
|
334
|
+
Without a circular buffer, the analytics service would grow linearly with traffic until the Node.js process runs out of memory. The 10,000-entry cap guarantees ~2 MB regardless of request volume. This same principle applies to rate limit state: Token Bucket's O(1) per client scales to 100k clients in 1.6 MB, while Sliding Window's O(N) would consume 80 MB for 10k pro-tier clients.
|
|
335
|
+
|
|
336
|
+
### Package Distribution Is Its Own Skill
|
|
337
|
+
|
|
338
|
+
Writing code that works in your own project is different from writing code that works in someone else's project. Subpath exports, peer dependencies, TypeScript module resolution, CSS bundling, dual CJS/ESM output, and version compatibility all require careful configuration that has nothing to do with the actual business logic.
|
|
339
|
+
|
|
340
|
+
### SSE > WebSockets for Unidirectional Streams
|
|
341
|
+
|
|
342
|
+
SSE is simpler, works over standard HTTP, and has browser-native reconnection. Unless you need bidirectional communication, SSE is the better choice. The only gotcha is proxy buffering and the EventSource header limitation, both solvable.
|
|
343
|
+
|
|
344
|
+
### Fail Open, Not Closed
|
|
345
|
+
|
|
346
|
+
A broken rate limiter should not become a denial-of-service against your own users. If the rate limiting logic throws an error, the middleware should allow the request through, log the error, and alert. Failing closed means your own bug takes down your service.
|
|
347
|
+
|
|
348
|
+
## API Reference
|
|
349
|
+
|
|
350
|
+
### Management Endpoints
|
|
351
|
+
|
|
352
|
+
| Method | Path | Description |
|
|
353
|
+
|--------|------|-------------|
|
|
354
|
+
| GET | `/gateway/analytics` | Current analytics snapshot |
|
|
355
|
+
| GET | `/gateway/analytics/live` | SSE stream (5s updates) |
|
|
356
|
+
| GET | `/gateway/config` | Current gateway configuration |
|
|
357
|
+
| GET | `/gateway/logs?limit=20&offset=0` | Paginated request logs |
|
|
358
|
+
| POST | `/gateway/keys` | Create API key (body: `{name, tier}`) |
|
|
359
|
+
| DELETE | `/gateway/keys/:keyId` | Revoke an API key |
|
|
360
|
+
|
|
361
|
+
### Device Auth Endpoints
|
|
362
|
+
|
|
363
|
+
| Method | Path | Description |
|
|
364
|
+
|--------|------|-------------|
|
|
365
|
+
| POST | `/auth/register` | Register browser device (body: `{browserId}`) |
|
|
366
|
+
| GET | `/auth/status/:browserId` | Check device status |
|
|
367
|
+
| DELETE | `/auth/:browserId` | Revoke device |
|
|
368
|
+
| GET | `/auth/stats` | Device registry statistics |
|
|
369
|
+
|
|
370
|
+
### Rate Limit Response Headers
|
|
371
|
+
|
|
372
|
+
| Header | Description |
|
|
373
|
+
|--------|-------------|
|
|
374
|
+
| `X-RateLimit-Limit` | Request quota for the tier |
|
|
375
|
+
| `X-RateLimit-Remaining` | Remaining requests in window |
|
|
376
|
+
| `X-RateLimit-Reset` | Seconds until limit resets |
|
|
377
|
+
|
|
378
|
+
## Theming
|
|
379
|
+
|
|
380
|
+
Override CSS custom properties to match your app's theme:
|
|
381
|
+
|
|
382
|
+
```css
|
|
383
|
+
.gw-dashboard {
|
|
384
|
+
--gw-bg-card: #1a1a2e;
|
|
385
|
+
--gw-accent: #00d4ff;
|
|
386
|
+
--gw-text-primary: #ffffff;
|
|
387
|
+
--gw-text-muted: #8888aa;
|
|
388
|
+
--gw-border: #2a2a4a;
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## TypeScript Support
|
|
393
|
+
|
|
394
|
+
All types are exported from the `./types` subpath:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
import type {
|
|
398
|
+
RateLimitConfig,
|
|
399
|
+
GatewayAnalytics,
|
|
400
|
+
GatewayConfig,
|
|
401
|
+
RequestLog,
|
|
402
|
+
ApiKey,
|
|
403
|
+
IpRules,
|
|
404
|
+
TierConfig,
|
|
405
|
+
} from '@gagandeep023/api-gateway/types';
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Contributing
|
|
409
|
+
|
|
410
|
+
Issues and PRs are welcome at [github.com/Gagandeep023/api-gateway](https://github.com/Gagandeep023/api-gateway).
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
git clone https://github.com/Gagandeep023/api-gateway.git
|
|
414
|
+
cd api-gateway
|
|
415
|
+
npm install
|
|
416
|
+
npm run build
|
|
417
|
+
npm test
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## License
|
|
421
|
+
|
|
422
|
+
MIT
|
package/dist/backend/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router, Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { RateLimitConfig, RequestLog, GatewayAnalytics, DeviceEntry, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.mjs';
|
|
2
|
+
import { RateLimitConfig, LogConfig, RequestLog, GatewayAnalytics, DeviceEntry, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.mjs';
|
|
3
3
|
|
|
4
4
|
declare class RateLimiterService {
|
|
5
5
|
private tokenBucket;
|
|
@@ -23,7 +23,10 @@ declare class AnalyticsService {
|
|
|
23
23
|
private logs;
|
|
24
24
|
private head;
|
|
25
25
|
private count;
|
|
26
|
+
private fileLogWriter;
|
|
27
|
+
constructor(logConfig?: LogConfig);
|
|
26
28
|
addLog(log: RequestLog): void;
|
|
29
|
+
destroy(): void;
|
|
27
30
|
getRecentLogs(limit?: number, offset?: number): RequestLog[];
|
|
28
31
|
getAnalytics(rateLimitHits: number): GatewayAnalytics;
|
|
29
32
|
private getOrderedLogs;
|
|
@@ -67,7 +70,7 @@ interface GatewayInstances {
|
|
|
67
70
|
analyticsService: AnalyticsService;
|
|
68
71
|
deviceRegistry?: DeviceRegistryService;
|
|
69
72
|
middleware: Router;
|
|
70
|
-
config: Required<GatewayMiddlewareConfig>;
|
|
73
|
+
config: Required<Omit<GatewayMiddlewareConfig, 'logConfig'>> & Pick<GatewayMiddlewareConfig, 'logConfig'>;
|
|
71
74
|
}
|
|
72
75
|
declare function createGatewayMiddleware(userConfig?: GatewayMiddlewareConfig): GatewayInstances;
|
|
73
76
|
|
|
@@ -83,6 +86,23 @@ interface DeviceAuthRoutesOptions {
|
|
|
83
86
|
}
|
|
84
87
|
declare function createDeviceAuthRoutes(options: DeviceAuthRoutesOptions): Router;
|
|
85
88
|
|
|
89
|
+
declare class FileLogWriter {
|
|
90
|
+
private logDir;
|
|
91
|
+
private appName;
|
|
92
|
+
private maxLines;
|
|
93
|
+
private currentDate;
|
|
94
|
+
private currentLines;
|
|
95
|
+
private fileIndex;
|
|
96
|
+
private currentFilePath;
|
|
97
|
+
private destroyed;
|
|
98
|
+
constructor(config: LogConfig);
|
|
99
|
+
write(log: RequestLog): void;
|
|
100
|
+
destroy(): void;
|
|
101
|
+
/** Scan existing log files for a date to determine the next incremental number. */
|
|
102
|
+
private scanExistingFiles;
|
|
103
|
+
private rotateFile;
|
|
104
|
+
}
|
|
105
|
+
|
|
86
106
|
declare function createApiKeyAuth(getKeys: () => ApiKeysConfig, deviceRegistry?: DeviceRegistryService): (req: Request, res: Response, next: NextFunction) => void;
|
|
87
107
|
|
|
88
108
|
declare function createIpFilter(getRules: () => IpRules): (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -100,4 +120,4 @@ declare function parseKey(key: string): {
|
|
|
100
120
|
} | null;
|
|
101
121
|
declare function generateSecret(): string;
|
|
102
122
|
|
|
103
|
-
export { AnalyticsService, type DeviceAuthRoutesOptions, DeviceRegistryService, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createDeviceAuthRoutes, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger, formatKey, generateSecret, generateTOTP, parseKey, validateTOTP };
|
|
123
|
+
export { AnalyticsService, type DeviceAuthRoutesOptions, DeviceRegistryService, FileLogWriter, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createDeviceAuthRoutes, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger, formatKey, generateSecret, generateTOTP, parseKey, validateTOTP };
|
package/dist/backend/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router, Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { RateLimitConfig, RequestLog, GatewayAnalytics, DeviceEntry, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.js';
|
|
2
|
+
import { RateLimitConfig, LogConfig, RequestLog, GatewayAnalytics, DeviceEntry, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.js';
|
|
3
3
|
|
|
4
4
|
declare class RateLimiterService {
|
|
5
5
|
private tokenBucket;
|
|
@@ -23,7 +23,10 @@ declare class AnalyticsService {
|
|
|
23
23
|
private logs;
|
|
24
24
|
private head;
|
|
25
25
|
private count;
|
|
26
|
+
private fileLogWriter;
|
|
27
|
+
constructor(logConfig?: LogConfig);
|
|
26
28
|
addLog(log: RequestLog): void;
|
|
29
|
+
destroy(): void;
|
|
27
30
|
getRecentLogs(limit?: number, offset?: number): RequestLog[];
|
|
28
31
|
getAnalytics(rateLimitHits: number): GatewayAnalytics;
|
|
29
32
|
private getOrderedLogs;
|
|
@@ -67,7 +70,7 @@ interface GatewayInstances {
|
|
|
67
70
|
analyticsService: AnalyticsService;
|
|
68
71
|
deviceRegistry?: DeviceRegistryService;
|
|
69
72
|
middleware: Router;
|
|
70
|
-
config: Required<GatewayMiddlewareConfig>;
|
|
73
|
+
config: Required<Omit<GatewayMiddlewareConfig, 'logConfig'>> & Pick<GatewayMiddlewareConfig, 'logConfig'>;
|
|
71
74
|
}
|
|
72
75
|
declare function createGatewayMiddleware(userConfig?: GatewayMiddlewareConfig): GatewayInstances;
|
|
73
76
|
|
|
@@ -83,6 +86,23 @@ interface DeviceAuthRoutesOptions {
|
|
|
83
86
|
}
|
|
84
87
|
declare function createDeviceAuthRoutes(options: DeviceAuthRoutesOptions): Router;
|
|
85
88
|
|
|
89
|
+
declare class FileLogWriter {
|
|
90
|
+
private logDir;
|
|
91
|
+
private appName;
|
|
92
|
+
private maxLines;
|
|
93
|
+
private currentDate;
|
|
94
|
+
private currentLines;
|
|
95
|
+
private fileIndex;
|
|
96
|
+
private currentFilePath;
|
|
97
|
+
private destroyed;
|
|
98
|
+
constructor(config: LogConfig);
|
|
99
|
+
write(log: RequestLog): void;
|
|
100
|
+
destroy(): void;
|
|
101
|
+
/** Scan existing log files for a date to determine the next incremental number. */
|
|
102
|
+
private scanExistingFiles;
|
|
103
|
+
private rotateFile;
|
|
104
|
+
}
|
|
105
|
+
|
|
86
106
|
declare function createApiKeyAuth(getKeys: () => ApiKeysConfig, deviceRegistry?: DeviceRegistryService): (req: Request, res: Response, next: NextFunction) => void;
|
|
87
107
|
|
|
88
108
|
declare function createIpFilter(getRules: () => IpRules): (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -100,4 +120,4 @@ declare function parseKey(key: string): {
|
|
|
100
120
|
} | null;
|
|
101
121
|
declare function generateSecret(): string;
|
|
102
122
|
|
|
103
|
-
export { AnalyticsService, type DeviceAuthRoutesOptions, DeviceRegistryService, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createDeviceAuthRoutes, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger, formatKey, generateSecret, generateTOTP, parseKey, validateTOTP };
|
|
123
|
+
export { AnalyticsService, type DeviceAuthRoutesOptions, DeviceRegistryService, FileLogWriter, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createDeviceAuthRoutes, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger, formatKey, generateSecret, generateTOTP, parseKey, validateTOTP };
|