offbyt 1.0.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.
Files changed (103) hide show
  1. package/README.md +2 -0
  2. package/cli/index.js +2 -0
  3. package/cli.js +206 -0
  4. package/core/detector/detectAxios.js +107 -0
  5. package/core/detector/detectFetch.js +148 -0
  6. package/core/detector/detectForms.js +55 -0
  7. package/core/detector/detectSocket.js +341 -0
  8. package/core/generator/generateControllers.js +17 -0
  9. package/core/generator/generateModels.js +25 -0
  10. package/core/generator/generateRoutes.js +17 -0
  11. package/core/generator/generateServer.js +18 -0
  12. package/core/generator/generateSocket.js +160 -0
  13. package/core/index.js +14 -0
  14. package/core/ir/IRTypes.js +25 -0
  15. package/core/ir/buildIR.js +83 -0
  16. package/core/parser/parseJS.js +26 -0
  17. package/core/parser/parseTS.js +27 -0
  18. package/core/rules/relationRules.js +38 -0
  19. package/core/rules/resourceRules.js +32 -0
  20. package/core/rules/schemaInference.js +26 -0
  21. package/core/scanner/scanProject.js +58 -0
  22. package/deploy/cloudflare.js +41 -0
  23. package/deploy/cloudflareWorker.js +122 -0
  24. package/deploy/connect.js +198 -0
  25. package/deploy/flyio.js +51 -0
  26. package/deploy/index.js +322 -0
  27. package/deploy/netlify.js +29 -0
  28. package/deploy/railway.js +215 -0
  29. package/deploy/render.js +195 -0
  30. package/deploy/utils.js +383 -0
  31. package/deploy/vercel.js +29 -0
  32. package/index.js +18 -0
  33. package/lib/generator/advancedCrudGenerator.js +475 -0
  34. package/lib/generator/crudCodeGenerator.js +486 -0
  35. package/lib/generator/irBasedGenerator.js +360 -0
  36. package/lib/ir-builder/index.js +16 -0
  37. package/lib/ir-builder/irBuilder.js +330 -0
  38. package/lib/ir-builder/rulesEngine.js +353 -0
  39. package/lib/ir-builder/templateEngine.js +193 -0
  40. package/lib/ir-builder/templates/index.js +14 -0
  41. package/lib/ir-builder/templates/model.template.js +47 -0
  42. package/lib/ir-builder/templates/routes-generic.template.js +66 -0
  43. package/lib/ir-builder/templates/routes-user.template.js +105 -0
  44. package/lib/ir-builder/templates/routes.template.js +102 -0
  45. package/lib/ir-builder/templates/validation.template.js +15 -0
  46. package/lib/ir-integration.js +349 -0
  47. package/lib/modes/benchmark.js +162 -0
  48. package/lib/modes/configBasedGenerator.js +2258 -0
  49. package/lib/modes/connect.js +1125 -0
  50. package/lib/modes/doctorAi.js +172 -0
  51. package/lib/modes/generateApi.js +435 -0
  52. package/lib/modes/interactiveSetup.js +548 -0
  53. package/lib/modes/offline.clean.js +14 -0
  54. package/lib/modes/offline.enhanced.js +787 -0
  55. package/lib/modes/offline.js +295 -0
  56. package/lib/modes/offline.v2.js +13 -0
  57. package/lib/modes/sync.js +629 -0
  58. package/lib/scanner/apiEndpointExtractor.js +387 -0
  59. package/lib/scanner/authPatternDetector.js +54 -0
  60. package/lib/scanner/frontendScanner.js +642 -0
  61. package/lib/utils/apiClientGenerator.js +242 -0
  62. package/lib/utils/apiScanner.js +95 -0
  63. package/lib/utils/codeInjector.js +350 -0
  64. package/lib/utils/doctor.js +381 -0
  65. package/lib/utils/envGenerator.js +36 -0
  66. package/lib/utils/loadTester.js +61 -0
  67. package/lib/utils/performanceAnalyzer.js +298 -0
  68. package/lib/utils/resourceDetector.js +281 -0
  69. package/package.json +20 -0
  70. package/templates/.env.template +31 -0
  71. package/templates/advanced.model.template.js +201 -0
  72. package/templates/advanced.route.template.js +341 -0
  73. package/templates/auth.middleware.template.js +87 -0
  74. package/templates/auth.routes.template.js +238 -0
  75. package/templates/auth.user.model.template.js +78 -0
  76. package/templates/cache.middleware.js +34 -0
  77. package/templates/chat.models.template.js +260 -0
  78. package/templates/chat.routes.template.js +478 -0
  79. package/templates/compression.middleware.js +19 -0
  80. package/templates/database.config.js +74 -0
  81. package/templates/errorHandler.middleware.js +54 -0
  82. package/templates/express/controller.ejs +26 -0
  83. package/templates/express/model.ejs +9 -0
  84. package/templates/express/route.ejs +18 -0
  85. package/templates/express/server.ejs +16 -0
  86. package/templates/frontend.env.template +14 -0
  87. package/templates/model.template.js +86 -0
  88. package/templates/package.production.json +51 -0
  89. package/templates/package.template.json +41 -0
  90. package/templates/pagination.utility.js +110 -0
  91. package/templates/production.server.template.js +233 -0
  92. package/templates/rateLimiter.middleware.js +36 -0
  93. package/templates/requestLogger.middleware.js +19 -0
  94. package/templates/response.helper.js +179 -0
  95. package/templates/route.template.js +130 -0
  96. package/templates/security.middleware.js +78 -0
  97. package/templates/server.template.js +91 -0
  98. package/templates/socket.server.template.js +433 -0
  99. package/templates/utils.helper.js +157 -0
  100. package/templates/validation.middleware.js +63 -0
  101. package/templates/validation.schema.js +128 -0
  102. package/utils/fileWriter.js +15 -0
  103. package/utils/logger.js +18 -0
@@ -0,0 +1,86 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const __MODEL_NAME__Schema = new mongoose.Schema(
4
+ {
5
+ /* __FIELDS__ */
6
+ isActive: {
7
+ type: Boolean,
8
+ default: true,
9
+ index: true
10
+ },
11
+ isDeleted: {
12
+ type: Boolean,
13
+ default: false,
14
+ index: true
15
+ },
16
+ createdBy: {
17
+ type: mongoose.Schema.Types.ObjectId,
18
+ ref: 'User'
19
+ },
20
+ metadata: {
21
+ updatedBy: mongoose.Schema.Types.ObjectId,
22
+ version: { type: Number, default: 1 },
23
+ tags: [String]
24
+ }
25
+ },
26
+ {
27
+ timestamps: true,
28
+ collection: '__COLLECTION_NAME__'
29
+ }
30
+ );
31
+
32
+ // ============================================================
33
+ // INDEXES FOR PERFORMANCE
34
+ // ============================================================
35
+ __MODEL_NAME__Schema.index({ createdAt: -1 });
36
+ __MODEL_NAME__Schema.index({ updatedAt: -1 });
37
+ __MODEL_NAME__Schema.index({ isActive: 1 });
38
+ __MODEL_NAME__Schema.index({ isDeleted: 1, createdAt: -1 });
39
+
40
+ // ============================================================
41
+ // HOOKS
42
+ // ============================================================
43
+ __MODEL_NAME__Schema.pre('save', function(next) {
44
+ if (this.isModified()) {
45
+ this.metadata.version = (this.metadata.version || 0) + 1;
46
+ }
47
+ next();
48
+ });
49
+
50
+ // ============================================================
51
+ // QUERY HELPERS
52
+ // ============================================================
53
+ __MODEL_NAME__Schema.query.active = function() {
54
+ return this.find({ isActive: true, isDeleted: false });
55
+ };
56
+
57
+ // ============================================================
58
+ // STATIC METHODS
59
+ // ============================================================
60
+ __MODEL_NAME__Schema.statics.findAllActive = async function(options = {}) {
61
+ return this.find({ isActive: true, isDeleted: false })
62
+ .sort(options.sort || { createdAt: -1 })
63
+ .limit(options.limit || 100)
64
+ .skip(options.skip || 0);
65
+ };
66
+
67
+ __MODEL_NAME__Schema.statics.softDelete = async function(id) {
68
+ return this.findByIdAndUpdate(
69
+ id,
70
+ { isDeleted: true },
71
+ { new: true }
72
+ );
73
+ };
74
+
75
+ // ============================================================
76
+ // INSTANCE METHODS
77
+ // ============================================================
78
+ __MODEL_NAME__Schema.methods.toJSON = function() {
79
+ const obj = this.toObject();
80
+ delete obj.__v;
81
+ return obj;
82
+ };
83
+
84
+ const __MODEL_NAME__ = mongoose.model('__MODEL_NAME__', __MODEL_NAME__Schema);
85
+
86
+ export default __MODEL_NAME__;
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "backend",
3
+ "version": "1.0.0",
4
+ "description": "Production-Ready Backend API - Generated by offbyt",
5
+ "type": "module",
6
+ "main": "server.js",
7
+ "scripts": {
8
+ "start": "node server.js",
9
+ "dev": "NODE_ENV=development node --watch server.js",
10
+ "test": "NODE_ENV=test echo \"Test suite coming soon\"",
11
+ "build": "echo \"Build complete\"",
12
+ "lint": "echo \"Linting enabled in production setup\""
13
+ },
14
+ "keywords": [
15
+ "backend",
16
+ "express",
17
+ "mongoose",
18
+ "rest-api",
19
+ "mongodb",
20
+ "production",
21
+ "api",
22
+ "full-stack"
23
+ ],
24
+ "author": "offbyt Generator",
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "axios": "^1.6.5",
28
+ "bcryptjs": "^2.4.3",
29
+ "chalk": "^5.3.0",
30
+ "compression": "^1.7.4",
31
+ "cors": "^2.8.5",
32
+ "dotenv": "^16.3.1",
33
+ "express": "^4.18.2",
34
+ "express-async-errors": "^3.1.1",
35
+ "express-rate-limit": "^7.1.5",
36
+ "express-validator": "^7.0.0",
37
+ "helmet": "^7.1.0",
38
+ "jsonwebtoken": "^9.0.2",
39
+ "express-mongo-sanitize": "^2.2.0",
40
+ "mongoose": "^8.0.3",
41
+ "ora": "^8.0.1",
42
+ "validator": "^13.11.0"
43
+ },
44
+ "devDependencies": {
45
+ "nodemon": "^3.0.2"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0",
49
+ "npm": ">=9.0.0"
50
+ }
51
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "backend",
3
+ "version": "1.0.0",
4
+ "description": "Backend Server - Generated by offbyt",
5
+ "type": "module",
6
+ "main": "server.js",
7
+ "scripts": {
8
+ "start": "node server.js",
9
+ "dev": "NODE_ENV=development node --watch server.js",
10
+ "dev:ai": "offbyt doctor-ai",
11
+ "test": "echo \"Error: no test specified\" && exit 0"
12
+ },
13
+ "keywords": [
14
+ "backend",
15
+ "express",
16
+ "mongoose",
17
+ "rest-api",
18
+ "mongodb"
19
+ ],
20
+ "author": "offbyt Generator",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "axios": "^1.6.0",
24
+ "bcryptjs": "^2.4.3",
25
+ "cors": "^2.8.5",
26
+ "dotenv": "^16.3.1",
27
+ "express": "^4.18.2",
28
+ "express-async-errors": "^3.1.1",
29
+ "jsonwebtoken": "^9.0.2",
30
+ "express-mongo-sanitize": "^2.2.0",
31
+ "mongoose": "^8.0.3",
32
+ "validator": "^13.11.0"
33
+ },
34
+ "devDependencies": {
35
+ "nodemon": "^3.0.1"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0",
39
+ "npm": ">=9.0.0"
40
+ }
41
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Pagination Utility
3
+ * Provides pagination, filtering, sorting for queries
4
+ */
5
+
6
+ export class PaginationHelper {
7
+ static getPaginationParams(req) {
8
+ const page = Math.max(1, parseInt(req.query.page) || 1);
9
+ const limit = Math.min(100, parseInt(req.query.limit) || 10);
10
+ const skip = (page - 1) * limit;
11
+
12
+ return { page, limit, skip };
13
+ }
14
+
15
+ static async paginate(model, query = {}, options = {}) {
16
+ const {
17
+ page = 1,
18
+ limit = 10,
19
+ skip = 0,
20
+ sort = { createdAt: -1 },
21
+ select = null,
22
+ populate = []
23
+ } = options;
24
+
25
+ const total = await model.countDocuments(query);
26
+ const data = await model
27
+ .find(query)
28
+ .sort(sort)
29
+ .skip(skip)
30
+ .limit(limit)
31
+ .select(select);
32
+
33
+ if (populate.length > 0) {
34
+ for (const field of populate) {
35
+ await model.populate(data, field);
36
+ }
37
+ }
38
+
39
+ return {
40
+ data,
41
+ pagination: {
42
+ page,
43
+ limit,
44
+ total,
45
+ pages: Math.ceil(total / limit),
46
+ hasMore: page * limit < total,
47
+ skip
48
+ }
49
+ };
50
+ }
51
+
52
+ static getSortObject(sortParam) {
53
+ if (!sortParam) return { createdAt: -1 };
54
+
55
+ const sorts = {};
56
+ const fields = sortParam.split(',');
57
+
58
+ for (const field of fields) {
59
+ if (field.startsWith('-')) {
60
+ sorts[field.substring(1)] = -1;
61
+ } else {
62
+ sorts[field] = 1;
63
+ }
64
+ }
65
+
66
+ return sorts;
67
+ }
68
+
69
+ static buildFilterQuery(req, allowedFields = []) {
70
+ const query = {};
71
+
72
+ for (const field of allowedFields) {
73
+ if (req.query[field]) {
74
+ const value = req.query[field];
75
+
76
+ // Handle range queries
77
+ if (value.includes('..')) {
78
+ const [min, max] = value.split('..');
79
+ query[field] = { $gte: isNaN(min) ? min : parseFloat(min), $lte: isNaN(max) ? max : parseFloat(max) };
80
+ }
81
+ // Handle multiple values
82
+ else if (value.includes(',')) {
83
+ query[field] = { $in: value.split(',') };
84
+ }
85
+ // Handle regex search
86
+ else if (value.includes('*')) {
87
+ query[field] = { $regex: value.replace(/\*/g, '.*'), $options: 'i' };
88
+ }
89
+ // Direct match
90
+ else {
91
+ query[field] = value;
92
+ }
93
+ }
94
+ }
95
+
96
+ return query;
97
+ }
98
+
99
+ static buildSearchQuery(searchTerm, searchFields = []) {
100
+ if (!searchTerm || searchFields.length === 0) return {};
101
+
102
+ return {
103
+ $or: searchFields.map(field => ({
104
+ [field]: { $regex: searchTerm, $options: 'i' }
105
+ }))
106
+ };
107
+ }
108
+ }
109
+
110
+ export default PaginationHelper;
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Production Server Template
3
+ * Fully configured Express server with all middleware
4
+ * Generated by offbyt - Enterprise Ready
5
+ */
6
+
7
+ import express from 'express';
8
+ import 'dotenv/config';
9
+ import dns from 'dns';
10
+ import cors from 'cors';
11
+ import compression from 'compression';
12
+ import helmet from 'helmet';
13
+ import rateLimit from 'express-rate-limit';
14
+ import chalk from 'chalk';
15
+
16
+ // Import database
17
+ import { connectDatabase, disconnectDatabase, isDatabaseConnected } from './config/database.js';
18
+
19
+ // Import middleware
20
+ import requestLogger from './middleware/requestLogger.js';
21
+ import errorHandler from './middleware/errorHandler.js';
22
+ import { securityHeaders, sanitizeData } from './middleware/security.js';
23
+ import { globalLimiter, authLimiter } from './middleware/rateLimiter.js';
24
+ import { cacheMiddleware } from './middleware/cache.js';
25
+
26
+ // Import utilities
27
+ import { ResponseHelper, Logger } from './utils/helper.js';
28
+
29
+
30
+ const app = express();
31
+ const PORT = process.env.PORT || 5000;
32
+ const NODE_ENV = process.env.NODE_ENV || 'development';
33
+ const API_VERSION = 'v1';
34
+
35
+ // ============================================
36
+ // MIDDLEWARE SETUP
37
+ // ============================================
38
+
39
+ // Security Headers
40
+ app.use(securityHeaders);
41
+
42
+ // Body Parsing
43
+ app.use(express.json({ limit: '50mb' }));
44
+ app.use(express.urlencoded({ limit: '50mb', extended: true }));
45
+
46
+ // Compression
47
+ app.use(compression({
48
+ level: NODE_ENV === 'production' ? 6 : 3,
49
+ threshold: 1024
50
+ }));
51
+
52
+ // CORS Configuration
53
+ app.use(cors({
54
+ origin: process.env.CORS_ORIGIN?.split(',') || '*',
55
+ credentials: true,
56
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
57
+ allowedHeaders: ['Content-Type', 'Authorization'],
58
+ maxAge: 86400 // 24 hours
59
+ }));
60
+
61
+ // Rate Limiting
62
+ app.use(`/api/auth`, authLimiter);
63
+ app.use(`/api`, globalLimiter);
64
+
65
+ // Data Sanitization
66
+ app.use(sanitizeData);
67
+
68
+ // Request Logging
69
+ app.use(requestLogger);
70
+
71
+ // Cache Middleware (for GET requests)
72
+ app.use(cacheMiddleware(300)); // 5 minutes cache
73
+
74
+ // ============================================
75
+ // ROUTES SETUP
76
+ // ============================================
77
+
78
+ // Health Check & API Info
79
+ app.get('/health', (req, res) => {
80
+ res.status(200).json({
81
+ status: 'OK',
82
+ uptime: process.uptime(),
83
+ timestamp: new Date().toISOString(),
84
+ environment: NODE_ENV,
85
+ database: isDatabaseConnected() ? 'Connected' : 'Disconnected',
86
+ version: API_VERSION
87
+ });
88
+ });
89
+
90
+ app.get(`/api/health`, (req, res) => {
91
+ res.status(200).json({
92
+ status: 'OK',
93
+ uptime: process.uptime(),
94
+ timestamp: new Date().toISOString(),
95
+ environment: NODE_ENV,
96
+ database: isDatabaseConnected() ? 'Connected' : 'Disconnected',
97
+ version: API_VERSION
98
+ });
99
+ });
100
+
101
+ app.get(`/api`, (req, res) => {
102
+ res.status(200).json({
103
+ success: true,
104
+ message: 'Backend API Server',
105
+ version: API_VERSION,
106
+ environment: NODE_ENV,
107
+ endpoints: {
108
+ health: '/health',
109
+ documentation: `/api/docs`,
110
+ status: `/api/status`
111
+ }
112
+ });
113
+ });
114
+
115
+ app.get(`/api/status`, (req, res) => {
116
+ res.status(200).json({
117
+ success: true,
118
+ status: 'running',
119
+ database: isDatabaseConnected() ? 'connected' : 'disconnected',
120
+ memory: process.memoryUsage(),
121
+ uptime: process.uptime()
122
+ });
123
+ });
124
+
125
+ // Import all routes
126
+ // Routes will be auto-registered here
127
+ // __ROUTES__
128
+
129
+ // ============================================
130
+ // IMPORTANT: API URL Configuration
131
+ // ============================================
132
+ // Routes are registered with /api prefix (e.g., app.use(`/api/users`, usersRoutes))
133
+ // Frontend VITE_API_URL should be: http://localhost:5000 (WITHOUT /api)
134
+ // Frontend will then call: http://localhost:5000/api/users
135
+ //
136
+ // ❌ WRONG: VITE_API_URL = http://localhost:5000/api
137
+ // This would result in: http://localhost:5000/api/api/users (404!)
138
+ //
139
+ // ✅ CORRECT: VITE_API_URL = http://localhost:5000
140
+ // This results in: http://localhost:5000/api/users (✓ works!)
141
+ // ============================================
142
+
143
+ // ============================================
144
+ // 404 ERROR HANDLER
145
+ // ============================================
146
+ app.use((req, res) => {
147
+ res.status(404).json({
148
+ success: false,
149
+ message: 'Endpoint not found',
150
+ path: req.path,
151
+ method: req.method,
152
+ status: 404
153
+ });
154
+ });
155
+
156
+ // ============================================
157
+ // GLOBAL ERROR HANDLER
158
+ // ============================================
159
+ app.use(errorHandler);
160
+
161
+ // ============================================
162
+ // SERVER STARTUP
163
+ // ============================================
164
+
165
+ async function startServer() {
166
+ try {
167
+ // Connect to database
168
+ await connectDatabase();
169
+
170
+ // Start server
171
+ const server = app.listen(PORT, () => {
172
+ Logger.info('Server started successfully', {
173
+ port: PORT,
174
+ environment: NODE_ENV,
175
+ pid: process.pid
176
+ });
177
+
178
+ console.log(chalk.cyan('\n🚀 Backend Server Running\n'));
179
+ console.log(chalk.white(' URL: '), chalk.bold(`http://localhost:${PORT}`));
180
+ console.log(chalk.white(' API: '), chalk.bold(`http://localhost:${PORT}/api`));
181
+ console.log(chalk.white(' Health: '), chalk.bold(`http://localhost:${PORT}/health`));
182
+ console.log(chalk.white(' Status: '), chalk.bold(`http://localhost:${PORT}/api/status`));
183
+ console.log(chalk.white(' Env: '), chalk.bold(NODE_ENV));
184
+ console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
185
+ });
186
+
187
+ // Graceful shutdown
188
+ process.on('SIGINT', async () => {
189
+ console.log(chalk.yellow('\nâ›” Shutting down gracefully...\n'));
190
+
191
+ server.close(async () => {
192
+ await disconnectDatabase();
193
+ Logger.info('Server shut down successfully');
194
+ process.exit(0);
195
+ });
196
+
197
+ // Force shutdown after 10 seconds
198
+ setTimeout(() => {
199
+ console.error(chalk.red('Force shutting down...'));
200
+ process.exit(1);
201
+ }, 10000);
202
+ });
203
+
204
+ // Handle uncaught exceptions
205
+ process.on('uncaughtException', (error) => {
206
+ Logger.error('Uncaught Exception', error);
207
+ console.error(chalk.red('❌ Uncaught Exception:'), error.message);
208
+ process.exit(1);
209
+ });
210
+
211
+ // Handle unhandled promise rejections
212
+ process.on('unhandledRejection', (reason, promise) => {
213
+ Logger.error('Unhandled Rejection', reason);
214
+ console.error(chalk.red('❌ Unhandled Rejection:'), reason);
215
+ });
216
+
217
+ } catch (error) {
218
+ Logger.error('Server startup failed', error);
219
+ console.error(chalk.red('❌ Server startup failed:'), error.message);
220
+ process.exit(1);
221
+ }
222
+ }
223
+
224
+ // ============================================
225
+ // START APPLICATION
226
+ // ============================================
227
+
228
+ if (process.env.NODE_ENV !== 'test') {
229
+ startServer();
230
+ }
231
+
232
+ export default app;
233
+
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Rate Limiter Middleware
3
+ * Prevents abuse by limiting requests from IP addresses
4
+ */
5
+
6
+ import rateLimit from 'express-rate-limit';
7
+
8
+ // Global rate limiter
9
+ export const globalLimiter = rateLimit({
10
+ windowMs: 15 * 60 * 1000, // 15 minutes
11
+ max: 100, // limit each IP to 100 requests per windowMs
12
+ message: 'Too many requests from this IP, please try again later.',
13
+ standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
14
+ legacyHeaders: false, // Disable the `X-RateLimit-*` headers
15
+ skip: (req) => process.env.NODE_ENV === 'development', // Skip in development
16
+ keyGenerator: (req) => req.ip || req.socket.remoteAddress // Use IP address as key
17
+ });
18
+
19
+ // Strict rate limiter for auth endpoints
20
+ export const authLimiter = rateLimit({
21
+ windowMs: 15 * 60 * 1000, // 15 minutes
22
+ max: 5, // limit each IP to 5 requests per windowMs
23
+ message: 'Too many login attempts, please try again later.',
24
+ standardHeaders: true,
25
+ legacyHeaders: false,
26
+ skipSuccessfulRequests: true // Don't count successful requests
27
+ });
28
+
29
+ // API endpoint limiter
30
+ export const apiLimiter = rateLimit({
31
+ windowMs: 60 * 1000, // 1 minute
32
+ max: 30, // limit each IP to 30 requests per minute
33
+ message: 'Too many API requests, please try again later.'
34
+ });
35
+
36
+ export default globalLimiter;
@@ -0,0 +1,19 @@
1
+ // Request Logger Middleware
2
+ const requestLogger = (req, res, next) => {
3
+ const start = Date.now();
4
+
5
+ // Log request
6
+ console.log(`📨 ${req.method} ${req.path}`);
7
+
8
+ // Override res.json to log response
9
+ const originalJson = res.json.bind(res);
10
+ res.json = function(data) {
11
+ const duration = Date.now() - start;
12
+ console.log(`✅ ${res.statusCode} ${req.method} ${req.path} [${duration}ms]`);
13
+ return originalJson(data);
14
+ };
15
+
16
+ next();
17
+ };
18
+
19
+ export default requestLogger;