servcraft 0.1.0 → 0.1.3
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/.claude/settings.local.json +30 -0
- package/.github/CODEOWNERS +18 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
- package/.github/dependabot.yml +59 -0
- package/.github/workflows/ci.yml +188 -0
- package/.github/workflows/release.yml +195 -0
- package/AUDIT.md +602 -0
- package/LICENSE +21 -0
- package/README.md +1102 -1
- package/dist/cli/index.cjs +2026 -2168
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2026 -2168
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +595 -616
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -52
- package/dist/index.d.ts +114 -52
- package/dist/index.js +595 -616
- package/dist/index.js.map +1 -1
- package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
- package/docs/DATABASE_MULTI_ORM.md +399 -0
- package/docs/PHASE1_BREAKDOWN.md +346 -0
- package/docs/PROGRESS.md +550 -0
- package/docs/modules/ANALYTICS.md +226 -0
- package/docs/modules/API-VERSIONING.md +252 -0
- package/docs/modules/AUDIT.md +192 -0
- package/docs/modules/AUTH.md +431 -0
- package/docs/modules/CACHE.md +346 -0
- package/docs/modules/EMAIL.md +254 -0
- package/docs/modules/FEATURE-FLAG.md +291 -0
- package/docs/modules/I18N.md +294 -0
- package/docs/modules/MEDIA-PROCESSING.md +281 -0
- package/docs/modules/MFA.md +266 -0
- package/docs/modules/NOTIFICATION.md +311 -0
- package/docs/modules/OAUTH.md +237 -0
- package/docs/modules/PAYMENT.md +804 -0
- package/docs/modules/QUEUE.md +540 -0
- package/docs/modules/RATE-LIMIT.md +339 -0
- package/docs/modules/SEARCH.md +288 -0
- package/docs/modules/SECURITY.md +327 -0
- package/docs/modules/SESSION.md +382 -0
- package/docs/modules/SWAGGER.md +305 -0
- package/docs/modules/UPLOAD.md +296 -0
- package/docs/modules/USER.md +505 -0
- package/docs/modules/VALIDATION.md +294 -0
- package/docs/modules/WEBHOOK.md +270 -0
- package/docs/modules/WEBSOCKET.md +691 -0
- package/package.json +53 -38
- package/prisma/schema.prisma +395 -1
- package/src/cli/commands/add-module.ts +520 -87
- package/src/cli/commands/db.ts +3 -4
- package/src/cli/commands/docs.ts +256 -6
- package/src/cli/commands/generate.ts +12 -19
- package/src/cli/commands/init.ts +384 -214
- package/src/cli/index.ts +0 -4
- package/src/cli/templates/repository.ts +6 -1
- package/src/cli/templates/routes.ts +6 -21
- package/src/cli/utils/docs-generator.ts +6 -7
- package/src/cli/utils/env-manager.ts +717 -0
- package/src/cli/utils/field-parser.ts +16 -7
- package/src/cli/utils/interactive-prompt.ts +223 -0
- package/src/cli/utils/template-manager.ts +346 -0
- package/src/config/database.config.ts +183 -0
- package/src/config/env.ts +0 -10
- package/src/config/index.ts +0 -14
- package/src/core/server.ts +1 -1
- package/src/database/adapters/mongoose.adapter.ts +132 -0
- package/src/database/adapters/prisma.adapter.ts +118 -0
- package/src/database/connection.ts +190 -0
- package/src/database/interfaces/database.interface.ts +85 -0
- package/src/database/interfaces/index.ts +7 -0
- package/src/database/interfaces/repository.interface.ts +129 -0
- package/src/database/models/mongoose/index.ts +7 -0
- package/src/database/models/mongoose/payment.schema.ts +347 -0
- package/src/database/models/mongoose/user.schema.ts +154 -0
- package/src/database/prisma.ts +1 -4
- package/src/database/redis.ts +101 -0
- package/src/database/repositories/mongoose/index.ts +7 -0
- package/src/database/repositories/mongoose/payment.repository.ts +380 -0
- package/src/database/repositories/mongoose/user.repository.ts +255 -0
- package/src/database/seed.ts +6 -1
- package/src/index.ts +9 -20
- package/src/middleware/security.ts +2 -6
- package/src/modules/analytics/analytics.routes.ts +80 -0
- package/src/modules/analytics/analytics.service.ts +364 -0
- package/src/modules/analytics/index.ts +18 -0
- package/src/modules/analytics/types.ts +180 -0
- package/src/modules/api-versioning/index.ts +15 -0
- package/src/modules/api-versioning/types.ts +86 -0
- package/src/modules/api-versioning/versioning.middleware.ts +120 -0
- package/src/modules/api-versioning/versioning.routes.ts +54 -0
- package/src/modules/api-versioning/versioning.service.ts +189 -0
- package/src/modules/audit/audit.repository.ts +206 -0
- package/src/modules/audit/audit.service.ts +27 -59
- package/src/modules/auth/auth.controller.ts +2 -2
- package/src/modules/auth/auth.middleware.ts +3 -9
- package/src/modules/auth/auth.routes.ts +10 -107
- package/src/modules/auth/auth.service.ts +126 -23
- package/src/modules/auth/index.ts +3 -4
- package/src/modules/cache/cache.service.ts +367 -0
- package/src/modules/cache/index.ts +10 -0
- package/src/modules/cache/types.ts +44 -0
- package/src/modules/email/email.service.ts +3 -10
- package/src/modules/email/templates.ts +2 -8
- package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
- package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
- package/src/modules/feature-flag/feature-flag.service.ts +566 -0
- package/src/modules/feature-flag/index.ts +20 -0
- package/src/modules/feature-flag/types.ts +192 -0
- package/src/modules/i18n/i18n.middleware.ts +186 -0
- package/src/modules/i18n/i18n.routes.ts +191 -0
- package/src/modules/i18n/i18n.service.ts +456 -0
- package/src/modules/i18n/index.ts +18 -0
- package/src/modules/i18n/types.ts +118 -0
- package/src/modules/media-processing/index.ts +17 -0
- package/src/modules/media-processing/media-processing.routes.ts +111 -0
- package/src/modules/media-processing/media-processing.service.ts +245 -0
- package/src/modules/media-processing/types.ts +156 -0
- package/src/modules/mfa/index.ts +20 -0
- package/src/modules/mfa/mfa.repository.ts +206 -0
- package/src/modules/mfa/mfa.routes.ts +595 -0
- package/src/modules/mfa/mfa.service.ts +572 -0
- package/src/modules/mfa/totp.ts +150 -0
- package/src/modules/mfa/types.ts +57 -0
- package/src/modules/notification/index.ts +20 -0
- package/src/modules/notification/notification.repository.ts +356 -0
- package/src/modules/notification/notification.service.ts +483 -0
- package/src/modules/notification/types.ts +119 -0
- package/src/modules/oauth/index.ts +20 -0
- package/src/modules/oauth/oauth.repository.ts +219 -0
- package/src/modules/oauth/oauth.routes.ts +446 -0
- package/src/modules/oauth/oauth.service.ts +293 -0
- package/src/modules/oauth/providers/apple.provider.ts +250 -0
- package/src/modules/oauth/providers/facebook.provider.ts +181 -0
- package/src/modules/oauth/providers/github.provider.ts +248 -0
- package/src/modules/oauth/providers/google.provider.ts +189 -0
- package/src/modules/oauth/providers/twitter.provider.ts +214 -0
- package/src/modules/oauth/types.ts +94 -0
- package/src/modules/payment/index.ts +19 -0
- package/src/modules/payment/payment.repository.ts +733 -0
- package/src/modules/payment/payment.routes.ts +390 -0
- package/src/modules/payment/payment.service.ts +354 -0
- package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
- package/src/modules/payment/providers/paypal.provider.ts +190 -0
- package/src/modules/payment/providers/stripe.provider.ts +215 -0
- package/src/modules/payment/types.ts +140 -0
- package/src/modules/queue/cron.ts +438 -0
- package/src/modules/queue/index.ts +87 -0
- package/src/modules/queue/queue.routes.ts +600 -0
- package/src/modules/queue/queue.service.ts +842 -0
- package/src/modules/queue/types.ts +222 -0
- package/src/modules/queue/workers.ts +366 -0
- package/src/modules/rate-limit/index.ts +59 -0
- package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
- package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
- package/src/modules/rate-limit/rate-limit.service.ts +348 -0
- package/src/modules/rate-limit/stores/memory.store.ts +165 -0
- package/src/modules/rate-limit/stores/redis.store.ts +322 -0
- package/src/modules/rate-limit/types.ts +153 -0
- package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
- package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
- package/src/modules/search/adapters/memory.adapter.ts +278 -0
- package/src/modules/search/index.ts +21 -0
- package/src/modules/search/search.service.ts +234 -0
- package/src/modules/search/types.ts +214 -0
- package/src/modules/security/index.ts +40 -0
- package/src/modules/security/sanitize.ts +223 -0
- package/src/modules/security/security-audit.service.ts +388 -0
- package/src/modules/security/security.middleware.ts +398 -0
- package/src/modules/session/index.ts +3 -0
- package/src/modules/session/session.repository.ts +159 -0
- package/src/modules/session/session.service.ts +340 -0
- package/src/modules/session/types.ts +38 -0
- package/src/modules/swagger/index.ts +7 -1
- package/src/modules/swagger/schema-builder.ts +16 -4
- package/src/modules/swagger/swagger.service.ts +9 -10
- package/src/modules/swagger/types.ts +0 -2
- package/src/modules/upload/index.ts +14 -0
- package/src/modules/upload/types.ts +83 -0
- package/src/modules/upload/upload.repository.ts +199 -0
- package/src/modules/upload/upload.routes.ts +311 -0
- package/src/modules/upload/upload.service.ts +448 -0
- package/src/modules/user/index.ts +3 -3
- package/src/modules/user/user.controller.ts +15 -9
- package/src/modules/user/user.repository.ts +237 -113
- package/src/modules/user/user.routes.ts +39 -164
- package/src/modules/user/user.service.ts +4 -3
- package/src/modules/validation/validator.ts +12 -17
- package/src/modules/webhook/index.ts +91 -0
- package/src/modules/webhook/retry.ts +196 -0
- package/src/modules/webhook/signature.ts +135 -0
- package/src/modules/webhook/types.ts +181 -0
- package/src/modules/webhook/webhook.repository.ts +358 -0
- package/src/modules/webhook/webhook.routes.ts +442 -0
- package/src/modules/webhook/webhook.service.ts +457 -0
- package/src/modules/websocket/features.ts +504 -0
- package/src/modules/websocket/index.ts +106 -0
- package/src/modules/websocket/middlewares.ts +298 -0
- package/src/modules/websocket/types.ts +181 -0
- package/src/modules/websocket/websocket.service.ts +692 -0
- package/src/utils/errors.ts +7 -0
- package/src/utils/pagination.ts +4 -1
- package/tests/helpers/db-check.ts +79 -0
- package/tests/integration/auth-redis.test.ts +94 -0
- package/tests/integration/cache-redis.test.ts +387 -0
- package/tests/integration/mongoose-repositories.test.ts +410 -0
- package/tests/integration/payment-prisma.test.ts +637 -0
- package/tests/integration/queue-bullmq.test.ts +417 -0
- package/tests/integration/user-prisma.test.ts +441 -0
- package/tests/integration/websocket-socketio.test.ts +552 -0
- package/tests/setup.ts +11 -9
- package/vitest.config.ts +3 -8
- package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
- package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
- package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
- package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
- package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction, RequestHandler } from 'express';
|
|
2
|
+
import type { RateLimitConfig } from './types.js';
|
|
3
|
+
import { RateLimitService } from './rate-limit.service.js';
|
|
4
|
+
|
|
5
|
+
interface RequestWithUser extends Request {
|
|
6
|
+
user?: {
|
|
7
|
+
id?: string;
|
|
8
|
+
role?: string;
|
|
9
|
+
};
|
|
10
|
+
rateLimiter?: RateLimitService;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create rate limiting middleware
|
|
15
|
+
*/
|
|
16
|
+
export function createRateLimiter(
|
|
17
|
+
config: RateLimitConfig,
|
|
18
|
+
service?: RateLimitService
|
|
19
|
+
): RequestHandler {
|
|
20
|
+
const rateLimiter = service || new RateLimitService(config);
|
|
21
|
+
|
|
22
|
+
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
23
|
+
try {
|
|
24
|
+
const reqWithUser = req as RequestWithUser;
|
|
25
|
+
|
|
26
|
+
// Generate key for this request
|
|
27
|
+
const key = config.keyGenerator
|
|
28
|
+
? config.keyGenerator({
|
|
29
|
+
ip: req.ip || 'unknown',
|
|
30
|
+
method: req.method,
|
|
31
|
+
url: req.url,
|
|
32
|
+
path: req.path,
|
|
33
|
+
userId: reqWithUser.user?.id,
|
|
34
|
+
userRole: reqWithUser.user?.role,
|
|
35
|
+
headers: req.headers as Record<string, string | string[] | undefined>,
|
|
36
|
+
})
|
|
37
|
+
: req.ip || 'unknown';
|
|
38
|
+
|
|
39
|
+
// Check for custom limits based on endpoint or user role
|
|
40
|
+
const customMax = config.max;
|
|
41
|
+
const customWindowMs = config.windowMs;
|
|
42
|
+
|
|
43
|
+
// Check rate limit
|
|
44
|
+
const result = await rateLimiter.check(key, {
|
|
45
|
+
max: customMax,
|
|
46
|
+
windowMs: customWindowMs,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Set rate limit headers
|
|
50
|
+
res.setHeader('X-RateLimit-Limit', result.limit.toString());
|
|
51
|
+
res.setHeader('X-RateLimit-Remaining', result.remaining.toString());
|
|
52
|
+
res.setHeader('X-RateLimit-Reset', new Date(result.resetAt).toISOString());
|
|
53
|
+
|
|
54
|
+
if (!result.allowed) {
|
|
55
|
+
if (result.retryAfter) {
|
|
56
|
+
res.setHeader('Retry-After', result.retryAfter.toString());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Default response
|
|
60
|
+
res.status(429).json({
|
|
61
|
+
error: 'Too Many Requests',
|
|
62
|
+
message: 'Rate limit exceeded. Please try again later.',
|
|
63
|
+
limit: result.limit,
|
|
64
|
+
remaining: result.remaining,
|
|
65
|
+
resetAt: new Date(result.resetAt).toISOString(),
|
|
66
|
+
retryAfter: result.retryAfter,
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Store rate limiter service on request for later use
|
|
72
|
+
reqWithUser.rateLimiter = rateLimiter;
|
|
73
|
+
|
|
74
|
+
next();
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error('[RateLimitMiddleware] Error:', error);
|
|
77
|
+
// On error, allow the request to proceed
|
|
78
|
+
next();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create endpoint-specific rate limiter
|
|
85
|
+
*/
|
|
86
|
+
export function rateLimitEndpoint(
|
|
87
|
+
max: number,
|
|
88
|
+
windowMs: number,
|
|
89
|
+
keyGenerator?: (req: Request) => string
|
|
90
|
+
): RequestHandler {
|
|
91
|
+
const config: RateLimitConfig = {
|
|
92
|
+
max,
|
|
93
|
+
windowMs,
|
|
94
|
+
keyGenerator: keyGenerator
|
|
95
|
+
? (reqData): string => keyGenerator(reqData as unknown as Request)
|
|
96
|
+
: undefined,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return createRateLimiter(config);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Pre-configured rate limiters for common use cases
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
// Strict - for sensitive operations
|
|
107
|
+
export const strictRateLimit = rateLimitEndpoint(5, 60 * 1000); // 5 requests per minute
|
|
108
|
+
|
|
109
|
+
// Standard - for normal API endpoints
|
|
110
|
+
export const standardRateLimit = rateLimitEndpoint(100, 60 * 1000); // 100 requests per minute
|
|
111
|
+
|
|
112
|
+
// Relaxed - for public endpoints
|
|
113
|
+
export const relaxedRateLimit = rateLimitEndpoint(1000, 60 * 1000); // 1000 requests per minute
|
|
114
|
+
|
|
115
|
+
// Auth endpoints - for login/register
|
|
116
|
+
export const authRateLimit = rateLimitEndpoint(5, 15 * 60 * 1000); // 5 requests per 15 minutes
|
|
117
|
+
|
|
118
|
+
// By IP
|
|
119
|
+
export const ipRateLimit = (max: number, windowMs: number): RequestHandler =>
|
|
120
|
+
rateLimitEndpoint(max, windowMs, (req) => req.ip || 'unknown');
|
|
121
|
+
|
|
122
|
+
// By User ID
|
|
123
|
+
export const userRateLimit = (max: number, windowMs: number): RequestHandler =>
|
|
124
|
+
rateLimitEndpoint(max, windowMs, (req) => {
|
|
125
|
+
const user = (req as RequestWithUser).user;
|
|
126
|
+
return user?.id || req.ip || 'unknown';
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// By API Key
|
|
130
|
+
export const apiKeyRateLimit = (max: number, windowMs: number): RequestHandler =>
|
|
131
|
+
rateLimitEndpoint(max, windowMs, (req) => {
|
|
132
|
+
const apiKey = req.headers['x-api-key'] as string;
|
|
133
|
+
return apiKey || req.ip || 'unknown';
|
|
134
|
+
});
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
import { Router, type Request } from 'express';
|
|
3
|
+
import type { RateLimitService } from './rate-limit.service.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create admin routes for rate limit management
|
|
7
|
+
* These routes should be protected with authentication/authorization middleware
|
|
8
|
+
*/
|
|
9
|
+
export function createRateLimitRoutes(service: RateLimitService): Router {
|
|
10
|
+
const router = Router();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* GET /rate-limit/info/:key
|
|
14
|
+
* Get rate limit info for a specific key
|
|
15
|
+
*/
|
|
16
|
+
router.get('/info/:key', async (req: Request, res: Response): Promise<void> => {
|
|
17
|
+
try {
|
|
18
|
+
const key = req.params.key;
|
|
19
|
+
if (!key) {
|
|
20
|
+
res.status(400).json({
|
|
21
|
+
error: 'Bad Request',
|
|
22
|
+
message: 'Key is required',
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const info = await service.getInfo(key);
|
|
27
|
+
|
|
28
|
+
if (!info) {
|
|
29
|
+
res.status(404).json({
|
|
30
|
+
error: 'Not Found',
|
|
31
|
+
message: 'No rate limit data found for this key',
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
res.json({
|
|
37
|
+
success: true,
|
|
38
|
+
data: info,
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('[RateLimitRoutes] Error getting info:', error);
|
|
42
|
+
res.status(500).json({
|
|
43
|
+
error: 'Internal Server Error',
|
|
44
|
+
message: 'Failed to get rate limit info',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* POST /rate-limit/reset/:key
|
|
51
|
+
* Reset rate limit for a specific key
|
|
52
|
+
*/
|
|
53
|
+
router.post('/reset/:key', async (req: Request, res: Response): Promise<void> => {
|
|
54
|
+
try {
|
|
55
|
+
const key = req.params.key;
|
|
56
|
+
if (!key) {
|
|
57
|
+
res.status(400).json({
|
|
58
|
+
error: 'Bad Request',
|
|
59
|
+
message: 'Key is required',
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await service.reset(key);
|
|
64
|
+
|
|
65
|
+
res.json({
|
|
66
|
+
success: true,
|
|
67
|
+
message: `Rate limit reset for key: ${key}`,
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('[RateLimitRoutes] Error resetting:', error);
|
|
71
|
+
res.status(500).json({
|
|
72
|
+
error: 'Internal Server Error',
|
|
73
|
+
message: 'Failed to reset rate limit',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* GET /rate-limit/config
|
|
80
|
+
* Get current rate limit configuration
|
|
81
|
+
*/
|
|
82
|
+
router.get('/config', async (req: Request, res: Response) => {
|
|
83
|
+
try {
|
|
84
|
+
const config = service.getConfig();
|
|
85
|
+
|
|
86
|
+
res.json({
|
|
87
|
+
success: true,
|
|
88
|
+
data: {
|
|
89
|
+
max: config.max,
|
|
90
|
+
windowMs: config.windowMs,
|
|
91
|
+
algorithm: config.algorithm,
|
|
92
|
+
whitelistCount: config.whitelist?.length || 0,
|
|
93
|
+
blacklistCount: config.blacklist?.length || 0,
|
|
94
|
+
customLimitsCount: Object.keys(config.customLimits || {}).length,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('[RateLimitRoutes] Error getting config:', error);
|
|
99
|
+
res.status(500).json({
|
|
100
|
+
error: 'Internal Server Error',
|
|
101
|
+
message: 'Failed to get configuration',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* POST /rate-limit/whitelist
|
|
108
|
+
* Add IP to whitelist
|
|
109
|
+
*/
|
|
110
|
+
router.post('/whitelist', async (req: Request, res: Response): Promise<void> => {
|
|
111
|
+
try {
|
|
112
|
+
const { ip } = req.body as { ip?: string };
|
|
113
|
+
|
|
114
|
+
if (!ip) {
|
|
115
|
+
res.status(400).json({
|
|
116
|
+
error: 'Bad Request',
|
|
117
|
+
message: 'IP address is required',
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
service.addToWhitelist(ip);
|
|
123
|
+
|
|
124
|
+
res.json({
|
|
125
|
+
success: true,
|
|
126
|
+
message: `IP ${ip} added to whitelist`,
|
|
127
|
+
});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('[RateLimitRoutes] Error adding to whitelist:', error);
|
|
130
|
+
res.status(500).json({
|
|
131
|
+
error: 'Internal Server Error',
|
|
132
|
+
message: 'Failed to add IP to whitelist',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* DELETE /rate-limit/whitelist/:ip
|
|
139
|
+
* Remove IP from whitelist
|
|
140
|
+
*/
|
|
141
|
+
router.delete('/whitelist/:ip', async (req: Request, res: Response): Promise<void> => {
|
|
142
|
+
try {
|
|
143
|
+
const ip = req.params.ip;
|
|
144
|
+
if (!ip) {
|
|
145
|
+
res.status(400).json({
|
|
146
|
+
error: 'Bad Request',
|
|
147
|
+
message: 'IP is required',
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
service.removeFromWhitelist(ip);
|
|
152
|
+
|
|
153
|
+
res.json({
|
|
154
|
+
success: true,
|
|
155
|
+
message: `IP ${ip} removed from whitelist`,
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error('[RateLimitRoutes] Error removing from whitelist:', error);
|
|
159
|
+
res.status(500).json({
|
|
160
|
+
error: 'Internal Server Error',
|
|
161
|
+
message: 'Failed to remove IP from whitelist',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* POST /rate-limit/blacklist
|
|
168
|
+
* Add IP to blacklist
|
|
169
|
+
*/
|
|
170
|
+
router.post('/blacklist', async (req: Request, res: Response): Promise<void> => {
|
|
171
|
+
try {
|
|
172
|
+
const { ip } = req.body as { ip?: string };
|
|
173
|
+
|
|
174
|
+
if (!ip) {
|
|
175
|
+
res.status(400).json({
|
|
176
|
+
error: 'Bad Request',
|
|
177
|
+
message: 'IP address is required',
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
service.addToBlacklist(ip);
|
|
183
|
+
|
|
184
|
+
res.json({
|
|
185
|
+
success: true,
|
|
186
|
+
message: `IP ${ip} added to blacklist`,
|
|
187
|
+
});
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('[RateLimitRoutes] Error adding to blacklist:', error);
|
|
190
|
+
res.status(500).json({
|
|
191
|
+
error: 'Internal Server Error',
|
|
192
|
+
message: 'Failed to add IP to blacklist',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* DELETE /rate-limit/blacklist/:ip
|
|
199
|
+
* Remove IP from blacklist
|
|
200
|
+
*/
|
|
201
|
+
router.delete('/blacklist/:ip', async (req: Request, res: Response): Promise<void> => {
|
|
202
|
+
try {
|
|
203
|
+
const ip = req.params.ip;
|
|
204
|
+
if (!ip) {
|
|
205
|
+
res.status(400).json({
|
|
206
|
+
error: 'Bad Request',
|
|
207
|
+
message: 'IP is required',
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
service.removeFromBlacklist(ip);
|
|
212
|
+
|
|
213
|
+
res.json({
|
|
214
|
+
success: true,
|
|
215
|
+
message: `IP ${ip} removed from blacklist`,
|
|
216
|
+
});
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error('[RateLimitRoutes] Error removing from blacklist:', error);
|
|
219
|
+
res.status(500).json({
|
|
220
|
+
error: 'Internal Server Error',
|
|
221
|
+
message: 'Failed to remove IP from blacklist',
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* POST /rate-limit/clear
|
|
228
|
+
* Clear all rate limit data
|
|
229
|
+
*/
|
|
230
|
+
router.post('/clear', async (req: Request, res: Response) => {
|
|
231
|
+
try {
|
|
232
|
+
await service.clear();
|
|
233
|
+
|
|
234
|
+
res.json({
|
|
235
|
+
success: true,
|
|
236
|
+
message: 'All rate limit data cleared',
|
|
237
|
+
});
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.error('[RateLimitRoutes] Error clearing data:', error);
|
|
240
|
+
res.status(500).json({
|
|
241
|
+
error: 'Internal Server Error',
|
|
242
|
+
message: 'Failed to clear rate limit data',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* POST /rate-limit/cleanup
|
|
249
|
+
* Cleanup expired entries
|
|
250
|
+
*/
|
|
251
|
+
router.post('/cleanup', async (req: Request, res: Response) => {
|
|
252
|
+
try {
|
|
253
|
+
await service.cleanup();
|
|
254
|
+
|
|
255
|
+
res.json({
|
|
256
|
+
success: true,
|
|
257
|
+
message: 'Expired entries cleaned up',
|
|
258
|
+
});
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error('[RateLimitRoutes] Error cleaning up:', error);
|
|
261
|
+
res.status(500).json({
|
|
262
|
+
error: 'Internal Server Error',
|
|
263
|
+
message: 'Failed to cleanup expired entries',
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
return router;
|
|
269
|
+
}
|