@gagandeep023/api-gateway 0.1.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.
@@ -0,0 +1,450 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/backend/index.ts
31
+ var backend_exports = {};
32
+ __export(backend_exports, {
33
+ AnalyticsService: () => AnalyticsService,
34
+ RateLimiterService: () => RateLimiterService,
35
+ createApiKeyAuth: () => createApiKeyAuth,
36
+ createGatewayMiddleware: () => createGatewayMiddleware,
37
+ createGatewayRoutes: () => createGatewayRoutes,
38
+ createIpFilter: () => createIpFilter,
39
+ createRateLimiter: () => createRateLimiter,
40
+ createRequestLogger: () => createRequestLogger
41
+ });
42
+ module.exports = __toCommonJS(backend_exports);
43
+
44
+ // src/backend/middleware/gateway.ts
45
+ var import_express = require("express");
46
+
47
+ // src/backend/services/RateLimiterService.ts
48
+ var TokenBucket = class {
49
+ buckets = /* @__PURE__ */ new Map();
50
+ tryConsume(ip, maxTokens, refillRate) {
51
+ const now = Date.now();
52
+ let bucket = this.buckets.get(ip);
53
+ if (!bucket) {
54
+ bucket = { tokens: maxTokens, lastRefill: now };
55
+ this.buckets.set(ip, bucket);
56
+ }
57
+ const elapsed = (now - bucket.lastRefill) / 1e3;
58
+ bucket.tokens = Math.min(maxTokens, bucket.tokens + elapsed * refillRate);
59
+ bucket.lastRefill = now;
60
+ if (bucket.tokens >= 1) {
61
+ bucket.tokens -= 1;
62
+ const resetMs2 = bucket.tokens <= 0 ? Math.ceil(1 / refillRate * 1e3) : 0;
63
+ return { allowed: true, remaining: Math.floor(bucket.tokens), resetMs: resetMs2 };
64
+ }
65
+ const resetMs = Math.ceil((1 - bucket.tokens) / refillRate * 1e3);
66
+ return { allowed: false, remaining: 0, resetMs };
67
+ }
68
+ };
69
+ var SlidingWindowLog = class {
70
+ windows = /* @__PURE__ */ new Map();
71
+ tryConsume(ip, maxRequests, windowMs) {
72
+ const now = Date.now();
73
+ let state = this.windows.get(ip);
74
+ if (!state) {
75
+ state = { timestamps: [] };
76
+ this.windows.set(ip, state);
77
+ }
78
+ state.timestamps = state.timestamps.filter((t) => now - t < windowMs);
79
+ if (state.timestamps.length < maxRequests) {
80
+ state.timestamps.push(now);
81
+ return {
82
+ allowed: true,
83
+ remaining: maxRequests - state.timestamps.length,
84
+ resetMs: state.timestamps.length > 0 ? windowMs - (now - state.timestamps[0]) : windowMs
85
+ };
86
+ }
87
+ const oldestInWindow = state.timestamps[0];
88
+ const resetMs = windowMs - (now - oldestInWindow);
89
+ return { allowed: false, remaining: 0, resetMs };
90
+ }
91
+ };
92
+ var FixedWindowCounter = class {
93
+ windows = /* @__PURE__ */ new Map();
94
+ tryConsume(ip, maxRequests, windowMs) {
95
+ const now = Date.now();
96
+ let state = this.windows.get(ip);
97
+ if (!state || now - state.windowStart >= windowMs) {
98
+ state = { count: 0, windowStart: now };
99
+ this.windows.set(ip, state);
100
+ }
101
+ const resetMs = windowMs - (now - state.windowStart);
102
+ if (state.count < maxRequests) {
103
+ state.count++;
104
+ return { allowed: true, remaining: maxRequests - state.count, resetMs };
105
+ }
106
+ return { allowed: false, remaining: 0, resetMs };
107
+ }
108
+ };
109
+ var RateLimiterService = class {
110
+ tokenBucket = new TokenBucket();
111
+ slidingWindow = new SlidingWindowLog();
112
+ fixedWindow = new FixedWindowCounter();
113
+ globalWindow = new FixedWindowCounter();
114
+ config;
115
+ _rateLimitHits = 0;
116
+ constructor(config) {
117
+ this.config = config;
118
+ }
119
+ get rateLimitHits() {
120
+ return this._rateLimitHits;
121
+ }
122
+ checkLimit(ip, tier) {
123
+ const globalResult = this.globalWindow.tryConsume(
124
+ "__global__",
125
+ this.config.globalLimit.maxRequests,
126
+ this.config.globalLimit.windowMs
127
+ );
128
+ if (!globalResult.allowed) {
129
+ this._rateLimitHits++;
130
+ return { allowed: false, remaining: 0, resetMs: globalResult.resetMs, limit: this.config.globalLimit.maxRequests };
131
+ }
132
+ const tierConfig = this.config.tiers[tier] || this.config.tiers[this.config.defaultTier];
133
+ if (!tierConfig || tierConfig.algorithm === "none") {
134
+ return { allowed: true, remaining: -1, resetMs: 0, limit: -1 };
135
+ }
136
+ let result;
137
+ switch (tierConfig.algorithm) {
138
+ case "tokenBucket":
139
+ result = this.tokenBucket.tryConsume(
140
+ ip,
141
+ tierConfig.maxRequests,
142
+ tierConfig.refillRate || 1
143
+ );
144
+ break;
145
+ case "slidingWindow":
146
+ result = this.slidingWindow.tryConsume(
147
+ ip,
148
+ tierConfig.maxRequests,
149
+ tierConfig.windowMs
150
+ );
151
+ break;
152
+ case "fixedWindow":
153
+ result = this.fixedWindow.tryConsume(
154
+ ip,
155
+ tierConfig.maxRequests,
156
+ tierConfig.windowMs
157
+ );
158
+ break;
159
+ default:
160
+ return { allowed: true, remaining: -1, resetMs: 0, limit: -1 };
161
+ }
162
+ if (!result.allowed) {
163
+ this._rateLimitHits++;
164
+ }
165
+ return { ...result, limit: tierConfig.maxRequests };
166
+ }
167
+ getConfig() {
168
+ return this.config;
169
+ }
170
+ };
171
+
172
+ // src/backend/services/AnalyticsService.ts
173
+ var MAX_LOG_SIZE = 1e4;
174
+ var ACTIVE_WINDOW_MS = 3e5;
175
+ var AnalyticsService = class {
176
+ logs = [];
177
+ head = 0;
178
+ count = 0;
179
+ addLog(log) {
180
+ if (this.count < MAX_LOG_SIZE) {
181
+ this.logs.push(log);
182
+ this.count++;
183
+ } else {
184
+ this.logs[this.head] = log;
185
+ this.head = (this.head + 1) % MAX_LOG_SIZE;
186
+ }
187
+ }
188
+ getRecentLogs(limit = 20, offset = 0) {
189
+ const ordered = this.getOrderedLogs();
190
+ return ordered.slice(offset, offset + limit);
191
+ }
192
+ getAnalytics(rateLimitHits) {
193
+ const now = Date.now();
194
+ const oneMinuteAgo = now - 6e4;
195
+ const activeWindowStart = now - ACTIVE_WINDOW_MS;
196
+ const ordered = this.getOrderedLogs();
197
+ const recentLogs = ordered.filter((l) => l.timestamp > oneMinuteAgo);
198
+ const requestsPerMinute = recentLogs.length;
199
+ const endpointCounts = /* @__PURE__ */ new Map();
200
+ for (const log of ordered) {
201
+ const current = endpointCounts.get(log.path) || 0;
202
+ endpointCounts.set(log.path, current + 1);
203
+ }
204
+ const topEndpoints = Array.from(endpointCounts.entries()).map(([path, count]) => ({ path, count })).sort((a, b) => b.count - a.count).slice(0, 5);
205
+ const errorCount = ordered.filter((l) => l.statusCode >= 400).length;
206
+ const errorRate = this.count > 0 ? errorCount / this.count * 100 : 0;
207
+ const totalResponseTime = ordered.reduce((sum, l) => sum + l.responseTime, 0);
208
+ const avgResponseTime = this.count > 0 ? totalResponseTime / this.count : 0;
209
+ const activeLogs = ordered.filter((l) => l.timestamp > activeWindowStart);
210
+ const uniqueIps = new Set(activeLogs.map((l) => l.ip));
211
+ const keyUsePairs = /* @__PURE__ */ new Set();
212
+ for (const log of activeLogs) {
213
+ if (log.apiKey) {
214
+ keyUsePairs.add(`${log.ip}::${log.apiKey}`);
215
+ }
216
+ }
217
+ return {
218
+ totalRequests: this.count,
219
+ requestsPerMinute,
220
+ topEndpoints,
221
+ errorRate: Math.round(errorRate * 100) / 100,
222
+ avgResponseTime: Math.round(avgResponseTime * 100) / 100,
223
+ activeClients: uniqueIps.size,
224
+ activeKeyUses: keyUsePairs.size,
225
+ rateLimitHits
226
+ };
227
+ }
228
+ getOrderedLogs() {
229
+ if (this.count < MAX_LOG_SIZE) {
230
+ return [...this.logs].reverse();
231
+ }
232
+ const tail = this.logs.slice(0, this.head);
233
+ const headPart = this.logs.slice(this.head);
234
+ return [...headPart, ...tail].reverse();
235
+ }
236
+ };
237
+
238
+ // src/config/defaults.ts
239
+ var DEFAULT_RATE_LIMIT_CONFIG = {
240
+ tiers: {
241
+ free: { algorithm: "tokenBucket", maxRequests: 100, windowMs: 6e4, refillRate: 10 },
242
+ pro: { algorithm: "slidingWindow", maxRequests: 1e3, windowMs: 6e4 },
243
+ unlimited: { algorithm: "none" }
244
+ },
245
+ defaultTier: "free",
246
+ globalLimit: { maxRequests: 1e4, windowMs: 6e4 }
247
+ };
248
+ var DEFAULT_IP_RULES = {
249
+ allowlist: [],
250
+ blocklist: [],
251
+ mode: "blocklist"
252
+ };
253
+ var DEFAULT_API_KEYS = {
254
+ keys: []
255
+ };
256
+
257
+ // src/backend/middleware/apiKeyAuth.ts
258
+ function createApiKeyAuth(getKeys) {
259
+ return function apiKeyAuth(req, res, next) {
260
+ const apiKey = req.header("X-API-Key") || req.query.apiKey;
261
+ if (!apiKey) {
262
+ req.clientId = req.ip || "unknown";
263
+ req.tier = "free";
264
+ next();
265
+ return;
266
+ }
267
+ const config = getKeys();
268
+ const keyEntry = config.keys.find((k) => k.key === apiKey && k.active);
269
+ if (!keyEntry) {
270
+ res.status(401).json({ error: "Invalid or revoked API key" });
271
+ return;
272
+ }
273
+ req.clientId = keyEntry.id;
274
+ req.tier = keyEntry.tier;
275
+ req.apiKeyValue = keyEntry.key;
276
+ next();
277
+ };
278
+ }
279
+
280
+ // src/backend/middleware/ipFilter.ts
281
+ function createIpFilter(getRules) {
282
+ return function ipFilter(req, res, next) {
283
+ const rules = getRules();
284
+ const clientIp = req.ip || req.socket.remoteAddress || "unknown";
285
+ if (rules.mode === "allowlist" && rules.allowlist.length > 0) {
286
+ if (!rules.allowlist.includes(clientIp)) {
287
+ res.status(403).json({ error: "IP not in allowlist" });
288
+ return;
289
+ }
290
+ }
291
+ if (rules.mode === "blocklist" && rules.blocklist.length > 0) {
292
+ if (rules.blocklist.includes(clientIp)) {
293
+ res.status(403).json({ error: "IP is blocked" });
294
+ return;
295
+ }
296
+ }
297
+ next();
298
+ };
299
+ }
300
+
301
+ // src/backend/middleware/rateLimiter.ts
302
+ function createRateLimiter(service) {
303
+ return function rateLimiter(req, res, next) {
304
+ const ip = req.ip || req.socket.remoteAddress || "unknown";
305
+ const tier = req.tier || "free";
306
+ const result = service.checkLimit(ip, tier);
307
+ if (result.limit > 0) {
308
+ res.setHeader("X-RateLimit-Limit", result.limit);
309
+ res.setHeader("X-RateLimit-Remaining", Math.max(0, result.remaining));
310
+ res.setHeader("X-RateLimit-Reset", Math.ceil(result.resetMs / 1e3));
311
+ }
312
+ if (!result.allowed) {
313
+ res.status(429).json({
314
+ error: "Rate limit exceeded",
315
+ retryAfter: Math.ceil(result.resetMs / 1e3)
316
+ });
317
+ return;
318
+ }
319
+ next();
320
+ };
321
+ }
322
+
323
+ // src/backend/middleware/requestLogger.ts
324
+ function createRequestLogger(analytics) {
325
+ return function requestLogger(req, res, next) {
326
+ const start = Date.now();
327
+ res.on("finish", () => {
328
+ const responseTime = Date.now() - start;
329
+ analytics.addLog({
330
+ timestamp: Date.now(),
331
+ method: req.method,
332
+ path: req.originalUrl,
333
+ statusCode: res.statusCode,
334
+ responseTime,
335
+ clientId: req.clientId || req.ip || "unknown",
336
+ ip: req.ip || req.socket.remoteAddress || "unknown",
337
+ apiKey: req.apiKeyValue || void 0
338
+ });
339
+ });
340
+ next();
341
+ };
342
+ }
343
+
344
+ // src/backend/middleware/gateway.ts
345
+ function createGatewayMiddleware(userConfig) {
346
+ const config = {
347
+ rateLimits: userConfig?.rateLimits ?? DEFAULT_RATE_LIMIT_CONFIG,
348
+ ipRules: userConfig?.ipRules ?? DEFAULT_IP_RULES,
349
+ apiKeys: userConfig?.apiKeys ?? DEFAULT_API_KEYS
350
+ };
351
+ const rateLimiterService = new RateLimiterService(config.rateLimits);
352
+ const analyticsService = new AnalyticsService();
353
+ const router = (0, import_express.Router)();
354
+ router.use(createRequestLogger(analyticsService));
355
+ router.use(createApiKeyAuth(() => config.apiKeys));
356
+ router.use(createIpFilter(() => config.ipRules));
357
+ router.use(createRateLimiter(rateLimiterService));
358
+ return {
359
+ rateLimiterService,
360
+ analyticsService,
361
+ middleware: router,
362
+ config
363
+ };
364
+ }
365
+
366
+ // src/backend/routes/gateway.ts
367
+ var import_express2 = require("express");
368
+ var import_crypto = __toESM(require("crypto"));
369
+ function createGatewayRoutes(options) {
370
+ const { rateLimiterService, analyticsService, config } = options;
371
+ const router = (0, import_express2.Router)();
372
+ router.get("/analytics", (_req, res) => {
373
+ const analytics = analyticsService.getAnalytics(rateLimiterService.rateLimitHits);
374
+ res.json(analytics);
375
+ });
376
+ router.get("/analytics/live", (_req, res) => {
377
+ res.setHeader("Content-Type", "text/event-stream");
378
+ res.setHeader("Cache-Control", "no-cache");
379
+ res.setHeader("Connection", "keep-alive");
380
+ res.setHeader("X-Accel-Buffering", "no");
381
+ res.setHeader("Access-Control-Allow-Origin", "*");
382
+ res.flushHeaders();
383
+ const send = () => {
384
+ const analytics = analyticsService.getAnalytics(rateLimiterService.rateLimitHits);
385
+ res.write(`data: ${JSON.stringify(analytics)}
386
+
387
+ `);
388
+ };
389
+ send();
390
+ const interval = setInterval(send, 5e3);
391
+ _req.on("close", () => {
392
+ clearInterval(interval);
393
+ });
394
+ });
395
+ router.get("/config", (_req, res) => {
396
+ const analytics = analyticsService.getAnalytics(rateLimiterService.rateLimitHits);
397
+ res.json({
398
+ rateLimits: config.rateLimits,
399
+ ipRules: config.ipRules,
400
+ activeKeys: config.apiKeys.keys.filter((k) => k.active).length,
401
+ activeKeyUses: analytics.activeKeyUses
402
+ });
403
+ });
404
+ router.post("/keys", (req, res) => {
405
+ const { name, tier } = req.body;
406
+ if (!name) {
407
+ res.status(400).json({ error: "Name is required" });
408
+ return;
409
+ }
410
+ const newKey = {
411
+ id: `key_${String(config.apiKeys.keys.length + 1).padStart(3, "0")}`,
412
+ key: `gw_live_${import_crypto.default.randomBytes(16).toString("hex")}`,
413
+ name,
414
+ tier: tier || "free",
415
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
416
+ active: true
417
+ };
418
+ config.apiKeys.keys.push(newKey);
419
+ res.status(201).json(newKey);
420
+ });
421
+ router.delete("/keys/:keyId", (req, res) => {
422
+ const { keyId } = req.params;
423
+ const key = config.apiKeys.keys.find((k) => k.id === keyId);
424
+ if (!key) {
425
+ res.status(404).json({ error: "API key not found" });
426
+ return;
427
+ }
428
+ key.active = false;
429
+ res.json({ message: "API key revoked", id: keyId });
430
+ });
431
+ router.get("/logs", (req, res) => {
432
+ const limit = parseInt(req.query.limit) || 20;
433
+ const offset = parseInt(req.query.offset) || 0;
434
+ const logs = analyticsService.getRecentLogs(limit, offset);
435
+ res.json({ logs, limit, offset });
436
+ });
437
+ return router;
438
+ }
439
+ // Annotate the CommonJS export names for ESM import in node:
440
+ 0 && (module.exports = {
441
+ AnalyticsService,
442
+ RateLimiterService,
443
+ createApiKeyAuth,
444
+ createGatewayMiddleware,
445
+ createGatewayRoutes,
446
+ createIpFilter,
447
+ createRateLimiter,
448
+ createRequestLogger
449
+ });
450
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/backend/index.ts","../../src/backend/middleware/gateway.ts","../../src/backend/services/RateLimiterService.ts","../../src/backend/services/AnalyticsService.ts","../../src/config/defaults.ts","../../src/backend/middleware/apiKeyAuth.ts","../../src/backend/middleware/ipFilter.ts","../../src/backend/middleware/rateLimiter.ts","../../src/backend/middleware/requestLogger.ts","../../src/backend/routes/gateway.ts"],"sourcesContent":["export { createGatewayMiddleware } from './middleware/gateway';\nexport type { GatewayInstances } from './middleware/gateway';\nexport { createGatewayRoutes } from './routes/gateway';\nexport type { GatewayRoutesOptions } from './routes/gateway';\nexport { RateLimiterService } from './services/RateLimiterService';\nexport { AnalyticsService } from './services/AnalyticsService';\nexport { createApiKeyAuth } from './middleware/apiKeyAuth';\nexport { createIpFilter } from './middleware/ipFilter';\nexport { createRateLimiter } from './middleware/rateLimiter';\nexport { createRequestLogger } from './middleware/requestLogger';\n","import { Router } from 'express';\nimport { RateLimiterService } from '../services/RateLimiterService';\nimport { AnalyticsService } from '../services/AnalyticsService';\nimport type { GatewayMiddlewareConfig } from '../../types';\nimport { DEFAULT_RATE_LIMIT_CONFIG, DEFAULT_IP_RULES, DEFAULT_API_KEYS } from '../../config/defaults';\nimport { createApiKeyAuth } from './apiKeyAuth';\nimport { createIpFilter } from './ipFilter';\nimport { createRateLimiter } from './rateLimiter';\nimport { createRequestLogger } from './requestLogger';\n\nexport interface GatewayInstances {\n rateLimiterService: RateLimiterService;\n analyticsService: AnalyticsService;\n middleware: Router;\n config: Required<GatewayMiddlewareConfig>;\n}\n\nexport function createGatewayMiddleware(userConfig?: GatewayMiddlewareConfig): GatewayInstances {\n const config: Required<GatewayMiddlewareConfig> = {\n rateLimits: userConfig?.rateLimits ?? DEFAULT_RATE_LIMIT_CONFIG,\n ipRules: userConfig?.ipRules ?? DEFAULT_IP_RULES,\n apiKeys: userConfig?.apiKeys ?? DEFAULT_API_KEYS,\n };\n\n const rateLimiterService = new RateLimiterService(config.rateLimits);\n const analyticsService = new AnalyticsService();\n\n const router = Router();\n router.use(createRequestLogger(analyticsService));\n router.use(createApiKeyAuth(() => config.apiKeys));\n router.use(createIpFilter(() => config.ipRules));\n router.use(createRateLimiter(rateLimiterService));\n\n return {\n rateLimiterService,\n analyticsService,\n middleware: router,\n config,\n };\n}\n","import type { BucketState, SlidingWindowState, FixedWindowState, TierConfig, RateLimitConfig } from '../../types';\n\nclass TokenBucket {\n private buckets = new Map<string, BucketState>();\n\n tryConsume(ip: string, maxTokens: number, refillRate: number): { allowed: boolean; remaining: number; resetMs: number } {\n const now = Date.now();\n let bucket = this.buckets.get(ip);\n\n if (!bucket) {\n bucket = { tokens: maxTokens, lastRefill: now };\n this.buckets.set(ip, bucket);\n }\n\n const elapsed = (now - bucket.lastRefill) / 1000;\n bucket.tokens = Math.min(maxTokens, bucket.tokens + elapsed * refillRate);\n bucket.lastRefill = now;\n\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n const resetMs = bucket.tokens <= 0 ? Math.ceil((1 / refillRate) * 1000) : 0;\n return { allowed: true, remaining: Math.floor(bucket.tokens), resetMs };\n }\n\n const resetMs = Math.ceil(((1 - bucket.tokens) / refillRate) * 1000);\n return { allowed: false, remaining: 0, resetMs };\n }\n}\n\nclass SlidingWindowLog {\n private windows = new Map<string, SlidingWindowState>();\n\n tryConsume(ip: string, maxRequests: number, windowMs: number): { allowed: boolean; remaining: number; resetMs: number } {\n const now = Date.now();\n let state = this.windows.get(ip);\n\n if (!state) {\n state = { timestamps: [] };\n this.windows.set(ip, state);\n }\n\n state.timestamps = state.timestamps.filter(t => now - t < windowMs);\n\n if (state.timestamps.length < maxRequests) {\n state.timestamps.push(now);\n return {\n allowed: true,\n remaining: maxRequests - state.timestamps.length,\n resetMs: state.timestamps.length > 0 ? windowMs - (now - state.timestamps[0]) : windowMs,\n };\n }\n\n const oldestInWindow = state.timestamps[0];\n const resetMs = windowMs - (now - oldestInWindow);\n return { allowed: false, remaining: 0, resetMs };\n }\n}\n\nclass FixedWindowCounter {\n private windows = new Map<string, FixedWindowState>();\n\n tryConsume(ip: string, maxRequests: number, windowMs: number): { allowed: boolean; remaining: number; resetMs: number } {\n const now = Date.now();\n let state = this.windows.get(ip);\n\n if (!state || now - state.windowStart >= windowMs) {\n state = { count: 0, windowStart: now };\n this.windows.set(ip, state);\n }\n\n const resetMs = windowMs - (now - state.windowStart);\n\n if (state.count < maxRequests) {\n state.count++;\n return { allowed: true, remaining: maxRequests - state.count, resetMs };\n }\n\n return { allowed: false, remaining: 0, resetMs };\n }\n}\n\nexport class RateLimiterService {\n private tokenBucket = new TokenBucket();\n private slidingWindow = new SlidingWindowLog();\n private fixedWindow = new FixedWindowCounter();\n private globalWindow = new FixedWindowCounter();\n private config: RateLimitConfig;\n private _rateLimitHits = 0;\n\n constructor(config: RateLimitConfig) {\n this.config = config;\n }\n\n get rateLimitHits(): number {\n return this._rateLimitHits;\n }\n\n checkLimit(ip: string, tier: string): { allowed: boolean; remaining: number; resetMs: number; limit: number } {\n const globalResult = this.globalWindow.tryConsume(\n '__global__',\n this.config.globalLimit.maxRequests,\n this.config.globalLimit.windowMs\n );\n\n if (!globalResult.allowed) {\n this._rateLimitHits++;\n return { allowed: false, remaining: 0, resetMs: globalResult.resetMs, limit: this.config.globalLimit.maxRequests };\n }\n\n const tierConfig = this.config.tiers[tier] || this.config.tiers[this.config.defaultTier];\n\n if (!tierConfig || tierConfig.algorithm === 'none') {\n return { allowed: true, remaining: -1, resetMs: 0, limit: -1 };\n }\n\n let result: { allowed: boolean; remaining: number; resetMs: number };\n\n switch (tierConfig.algorithm) {\n case 'tokenBucket':\n result = this.tokenBucket.tryConsume(\n ip,\n tierConfig.maxRequests!,\n tierConfig.refillRate || 1\n );\n break;\n case 'slidingWindow':\n result = this.slidingWindow.tryConsume(\n ip,\n tierConfig.maxRequests!,\n tierConfig.windowMs!\n );\n break;\n case 'fixedWindow':\n result = this.fixedWindow.tryConsume(\n ip,\n tierConfig.maxRequests!,\n tierConfig.windowMs!\n );\n break;\n default:\n return { allowed: true, remaining: -1, resetMs: 0, limit: -1 };\n }\n\n if (!result.allowed) {\n this._rateLimitHits++;\n }\n\n return { ...result, limit: tierConfig.maxRequests! };\n }\n\n getConfig(): RateLimitConfig {\n return this.config;\n }\n}\n","import type { RequestLog, GatewayAnalytics } from '../../types';\n\nconst MAX_LOG_SIZE = 10000;\nconst ACTIVE_WINDOW_MS = 300000; // 5 minutes\n\nexport class AnalyticsService {\n private logs: RequestLog[] = [];\n private head = 0;\n private count = 0;\n\n addLog(log: RequestLog): void {\n if (this.count < MAX_LOG_SIZE) {\n this.logs.push(log);\n this.count++;\n } else {\n this.logs[this.head] = log;\n this.head = (this.head + 1) % MAX_LOG_SIZE;\n }\n }\n\n getRecentLogs(limit = 20, offset = 0): RequestLog[] {\n const ordered = this.getOrderedLogs();\n return ordered.slice(offset, offset + limit);\n }\n\n getAnalytics(rateLimitHits: number): GatewayAnalytics {\n const now = Date.now();\n const oneMinuteAgo = now - 60000;\n const activeWindowStart = now - ACTIVE_WINDOW_MS;\n const ordered = this.getOrderedLogs();\n\n const recentLogs = ordered.filter(l => l.timestamp > oneMinuteAgo);\n const requestsPerMinute = recentLogs.length;\n\n // Top endpoints\n const endpointCounts = new Map<string, number>();\n for (const log of ordered) {\n const current = endpointCounts.get(log.path) || 0;\n endpointCounts.set(log.path, current + 1);\n }\n const topEndpoints = Array.from(endpointCounts.entries())\n .map(([path, count]) => ({ path, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 5);\n\n // Error rate\n const errorCount = ordered.filter(l => l.statusCode >= 400).length;\n const errorRate = this.count > 0 ? (errorCount / this.count) * 100 : 0;\n\n // Average response time\n const totalResponseTime = ordered.reduce((sum, l) => sum + l.responseTime, 0);\n const avgResponseTime = this.count > 0 ? totalResponseTime / this.count : 0;\n\n // Active clients: unique IPs in last 5 minutes\n const activeLogs = ordered.filter(l => l.timestamp > activeWindowStart);\n const uniqueIps = new Set(activeLogs.map(l => l.ip));\n\n // Active key uses: unique (IP + apiKey) pairs in last 5 minutes\n const keyUsePairs = new Set<string>();\n for (const log of activeLogs) {\n if (log.apiKey) {\n keyUsePairs.add(`${log.ip}::${log.apiKey}`);\n }\n }\n\n return {\n totalRequests: this.count,\n requestsPerMinute,\n topEndpoints,\n errorRate: Math.round(errorRate * 100) / 100,\n avgResponseTime: Math.round(avgResponseTime * 100) / 100,\n activeClients: uniqueIps.size,\n activeKeyUses: keyUsePairs.size,\n rateLimitHits,\n };\n }\n\n private getOrderedLogs(): RequestLog[] {\n if (this.count < MAX_LOG_SIZE) {\n return [...this.logs].reverse();\n }\n const tail = this.logs.slice(0, this.head);\n const headPart = this.logs.slice(this.head);\n return [...headPart, ...tail].reverse();\n }\n}\n","import type { RateLimitConfig, IpRules, ApiKeysConfig } from '../types';\n\nexport const DEFAULT_RATE_LIMIT_CONFIG: RateLimitConfig = {\n tiers: {\n free: { algorithm: 'tokenBucket', maxRequests: 100, windowMs: 60000, refillRate: 10 },\n pro: { algorithm: 'slidingWindow', maxRequests: 1000, windowMs: 60000 },\n unlimited: { algorithm: 'none' },\n },\n defaultTier: 'free',\n globalLimit: { maxRequests: 10000, windowMs: 60000 },\n};\n\nexport const DEFAULT_IP_RULES: IpRules = {\n allowlist: [],\n blocklist: [],\n mode: 'blocklist',\n};\n\nexport const DEFAULT_API_KEYS: ApiKeysConfig = {\n keys: [],\n};\n","import { Request, Response, NextFunction } from 'express';\nimport type { ApiKeysConfig } from '../../types';\n\nexport function createApiKeyAuth(getKeys: () => ApiKeysConfig) {\n return function apiKeyAuth(req: Request, res: Response, next: NextFunction): void {\n const apiKey = req.header('X-API-Key') || req.query.apiKey as string;\n\n if (!apiKey) {\n (req as any).clientId = req.ip || 'unknown';\n (req as any).tier = 'free';\n next();\n return;\n }\n\n const config = getKeys();\n const keyEntry = config.keys.find(k => k.key === apiKey && k.active);\n\n if (!keyEntry) {\n res.status(401).json({ error: 'Invalid or revoked API key' });\n return;\n }\n\n (req as any).clientId = keyEntry.id;\n (req as any).tier = keyEntry.tier;\n (req as any).apiKeyValue = keyEntry.key;\n next();\n };\n}\n","import { Request, Response, NextFunction } from 'express';\nimport type { IpRules } from '../../types';\n\nexport function createIpFilter(getRules: () => IpRules) {\n return function ipFilter(req: Request, res: Response, next: NextFunction): void {\n const rules = getRules();\n const clientIp = req.ip || req.socket.remoteAddress || 'unknown';\n\n if (rules.mode === 'allowlist' && rules.allowlist.length > 0) {\n if (!rules.allowlist.includes(clientIp)) {\n res.status(403).json({ error: 'IP not in allowlist' });\n return;\n }\n }\n\n if (rules.mode === 'blocklist' && rules.blocklist.length > 0) {\n if (rules.blocklist.includes(clientIp)) {\n res.status(403).json({ error: 'IP is blocked' });\n return;\n }\n }\n\n next();\n };\n}\n","import { Request, Response, NextFunction } from 'express';\nimport { RateLimiterService } from '../services/RateLimiterService';\n\nexport function createRateLimiter(service: RateLimiterService) {\n return function rateLimiter(req: Request, res: Response, next: NextFunction): void {\n const ip = req.ip || req.socket.remoteAddress || 'unknown';\n const tier = (req as any).tier || 'free';\n\n const result = service.checkLimit(ip, tier);\n\n if (result.limit > 0) {\n res.setHeader('X-RateLimit-Limit', result.limit);\n res.setHeader('X-RateLimit-Remaining', Math.max(0, result.remaining));\n res.setHeader('X-RateLimit-Reset', Math.ceil(result.resetMs / 1000));\n }\n\n if (!result.allowed) {\n res.status(429).json({\n error: 'Rate limit exceeded',\n retryAfter: Math.ceil(result.resetMs / 1000),\n });\n return;\n }\n\n next();\n };\n}\n","import { Request, Response, NextFunction } from 'express';\nimport { AnalyticsService } from '../services/AnalyticsService';\n\nexport function createRequestLogger(analytics: AnalyticsService) {\n return function requestLogger(req: Request, res: Response, next: NextFunction): void {\n const start = Date.now();\n\n res.on('finish', () => {\n const responseTime = Date.now() - start;\n analytics.addLog({\n timestamp: Date.now(),\n method: req.method,\n path: req.originalUrl,\n statusCode: res.statusCode,\n responseTime,\n clientId: (req as any).clientId || req.ip || 'unknown',\n ip: req.ip || req.socket.remoteAddress || 'unknown',\n apiKey: (req as any).apiKeyValue || undefined,\n });\n });\n\n next();\n };\n}\n","import { Router, Request, Response } from 'express';\nimport { RateLimiterService } from '../services/RateLimiterService';\nimport { AnalyticsService } from '../services/AnalyticsService';\nimport type { ApiKeysConfig, GatewayMiddlewareConfig } from '../../types';\nimport crypto from 'crypto';\n\nexport interface GatewayRoutesOptions {\n rateLimiterService: RateLimiterService;\n analyticsService: AnalyticsService;\n config: Required<GatewayMiddlewareConfig>;\n}\n\nexport function createGatewayRoutes(options: GatewayRoutesOptions): Router {\n const { rateLimiterService, analyticsService, config } = options;\n const router = Router();\n\n // GET /analytics - Returns current analytics snapshot\n router.get('/analytics', (_req: Request, res: Response) => {\n const analytics = analyticsService.getAnalytics(rateLimiterService.rateLimitHits);\n res.json(analytics);\n });\n\n // GET /analytics/live - SSE stream pushing analytics every 5 seconds\n router.get('/analytics/live', (_req: Request, res: Response) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.flushHeaders();\n\n const send = (): void => {\n const analytics = analyticsService.getAnalytics(rateLimiterService.rateLimitHits);\n res.write(`data: ${JSON.stringify(analytics)}\\n\\n`);\n };\n\n send();\n const interval = setInterval(send, 5000);\n\n _req.on('close', () => {\n clearInterval(interval);\n });\n });\n\n // GET /config - Returns current gateway config\n router.get('/config', (_req: Request, res: Response) => {\n const analytics = analyticsService.getAnalytics(rateLimiterService.rateLimitHits);\n res.json({\n rateLimits: config.rateLimits,\n ipRules: config.ipRules,\n activeKeys: config.apiKeys.keys.filter(k => k.active).length,\n activeKeyUses: analytics.activeKeyUses,\n });\n });\n\n // POST /keys - Create a new API key\n router.post('/keys', (req: Request, res: Response) => {\n const { name, tier } = req.body;\n\n if (!name) {\n res.status(400).json({ error: 'Name is required' });\n return;\n }\n\n const newKey = {\n id: `key_${String(config.apiKeys.keys.length + 1).padStart(3, '0')}`,\n key: `gw_live_${crypto.randomBytes(16).toString('hex')}`,\n name,\n tier: tier || 'free',\n createdAt: new Date().toISOString(),\n active: true,\n };\n\n config.apiKeys.keys.push(newKey);\n res.status(201).json(newKey);\n });\n\n // DELETE /keys/:keyId - Revoke an API key\n router.delete('/keys/:keyId', (req: Request, res: Response) => {\n const { keyId } = req.params;\n const key = config.apiKeys.keys.find(k => k.id === keyId);\n\n if (!key) {\n res.status(404).json({ error: 'API key not found' });\n return;\n }\n\n key.active = false;\n res.json({ message: 'API key revoked', id: keyId });\n });\n\n // GET /logs - Returns recent request logs (paginated)\n router.get('/logs', (req: Request, res: Response) => {\n const limit = parseInt(req.query.limit as string) || 20;\n const offset = parseInt(req.query.offset as string) || 0;\n const logs = analyticsService.getRecentLogs(limit, offset);\n res.json({ logs, limit, offset });\n });\n\n return router;\n}\n"],"mappings":"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","names":["resetMs","import_express","crypto"]}