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.
- package/README.md +2 -0
- package/cli/index.js +2 -0
- package/cli.js +206 -0
- package/core/detector/detectAxios.js +107 -0
- package/core/detector/detectFetch.js +148 -0
- package/core/detector/detectForms.js +55 -0
- package/core/detector/detectSocket.js +341 -0
- package/core/generator/generateControllers.js +17 -0
- package/core/generator/generateModels.js +25 -0
- package/core/generator/generateRoutes.js +17 -0
- package/core/generator/generateServer.js +18 -0
- package/core/generator/generateSocket.js +160 -0
- package/core/index.js +14 -0
- package/core/ir/IRTypes.js +25 -0
- package/core/ir/buildIR.js +83 -0
- package/core/parser/parseJS.js +26 -0
- package/core/parser/parseTS.js +27 -0
- package/core/rules/relationRules.js +38 -0
- package/core/rules/resourceRules.js +32 -0
- package/core/rules/schemaInference.js +26 -0
- package/core/scanner/scanProject.js +58 -0
- package/deploy/cloudflare.js +41 -0
- package/deploy/cloudflareWorker.js +122 -0
- package/deploy/connect.js +198 -0
- package/deploy/flyio.js +51 -0
- package/deploy/index.js +322 -0
- package/deploy/netlify.js +29 -0
- package/deploy/railway.js +215 -0
- package/deploy/render.js +195 -0
- package/deploy/utils.js +383 -0
- package/deploy/vercel.js +29 -0
- package/index.js +18 -0
- package/lib/generator/advancedCrudGenerator.js +475 -0
- package/lib/generator/crudCodeGenerator.js +486 -0
- package/lib/generator/irBasedGenerator.js +360 -0
- package/lib/ir-builder/index.js +16 -0
- package/lib/ir-builder/irBuilder.js +330 -0
- package/lib/ir-builder/rulesEngine.js +353 -0
- package/lib/ir-builder/templateEngine.js +193 -0
- package/lib/ir-builder/templates/index.js +14 -0
- package/lib/ir-builder/templates/model.template.js +47 -0
- package/lib/ir-builder/templates/routes-generic.template.js +66 -0
- package/lib/ir-builder/templates/routes-user.template.js +105 -0
- package/lib/ir-builder/templates/routes.template.js +102 -0
- package/lib/ir-builder/templates/validation.template.js +15 -0
- package/lib/ir-integration.js +349 -0
- package/lib/modes/benchmark.js +162 -0
- package/lib/modes/configBasedGenerator.js +2258 -0
- package/lib/modes/connect.js +1125 -0
- package/lib/modes/doctorAi.js +172 -0
- package/lib/modes/generateApi.js +435 -0
- package/lib/modes/interactiveSetup.js +548 -0
- package/lib/modes/offline.clean.js +14 -0
- package/lib/modes/offline.enhanced.js +787 -0
- package/lib/modes/offline.js +295 -0
- package/lib/modes/offline.v2.js +13 -0
- package/lib/modes/sync.js +629 -0
- package/lib/scanner/apiEndpointExtractor.js +387 -0
- package/lib/scanner/authPatternDetector.js +54 -0
- package/lib/scanner/frontendScanner.js +642 -0
- package/lib/utils/apiClientGenerator.js +242 -0
- package/lib/utils/apiScanner.js +95 -0
- package/lib/utils/codeInjector.js +350 -0
- package/lib/utils/doctor.js +381 -0
- package/lib/utils/envGenerator.js +36 -0
- package/lib/utils/loadTester.js +61 -0
- package/lib/utils/performanceAnalyzer.js +298 -0
- package/lib/utils/resourceDetector.js +281 -0
- package/package.json +20 -0
- package/templates/.env.template +31 -0
- package/templates/advanced.model.template.js +201 -0
- package/templates/advanced.route.template.js +341 -0
- package/templates/auth.middleware.template.js +87 -0
- package/templates/auth.routes.template.js +238 -0
- package/templates/auth.user.model.template.js +78 -0
- package/templates/cache.middleware.js +34 -0
- package/templates/chat.models.template.js +260 -0
- package/templates/chat.routes.template.js +478 -0
- package/templates/compression.middleware.js +19 -0
- package/templates/database.config.js +74 -0
- package/templates/errorHandler.middleware.js +54 -0
- package/templates/express/controller.ejs +26 -0
- package/templates/express/model.ejs +9 -0
- package/templates/express/route.ejs +18 -0
- package/templates/express/server.ejs +16 -0
- package/templates/frontend.env.template +14 -0
- package/templates/model.template.js +86 -0
- package/templates/package.production.json +51 -0
- package/templates/package.template.json +41 -0
- package/templates/pagination.utility.js +110 -0
- package/templates/production.server.template.js +233 -0
- package/templates/rateLimiter.middleware.js +36 -0
- package/templates/requestLogger.middleware.js +19 -0
- package/templates/response.helper.js +179 -0
- package/templates/route.template.js +130 -0
- package/templates/security.middleware.js +78 -0
- package/templates/server.template.js +91 -0
- package/templates/socket.server.template.js +433 -0
- package/templates/utils.helper.js +157 -0
- package/templates/validation.middleware.js +63 -0
- package/templates/validation.schema.js +128 -0
- package/utils/fileWriter.js +15 -0
- package/utils/logger.js +18 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Helper Utility
|
|
3
|
+
* Standardizes API responses across the application
|
|
4
|
+
*/
|
|
5
|
+
export class ResponseHelper {
|
|
6
|
+
/**
|
|
7
|
+
* Success response with data
|
|
8
|
+
*/
|
|
9
|
+
static success(res, data, message = 'Success', statusCode = 200) {
|
|
10
|
+
return res.status(statusCode).json({
|
|
11
|
+
success: true,
|
|
12
|
+
message,
|
|
13
|
+
data
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Paginated response
|
|
19
|
+
*/
|
|
20
|
+
static paginated(res, data, pagination, message = 'Data retrieved successfully', statusCode = 200) {
|
|
21
|
+
return res.status(statusCode).json({
|
|
22
|
+
success: true,
|
|
23
|
+
message,
|
|
24
|
+
data,
|
|
25
|
+
pagination
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Error response
|
|
31
|
+
*/
|
|
32
|
+
static error(res, message = 'Internal server error', statusCode = 500, errors = null) {
|
|
33
|
+
const response = {
|
|
34
|
+
success: false,
|
|
35
|
+
message
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (errors) {
|
|
39
|
+
response.errors = errors;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return res.status(statusCode).json(response);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validation error response
|
|
47
|
+
*/
|
|
48
|
+
static validationError(res, errors, message = 'Validation failed') {
|
|
49
|
+
return res.status(400).json({
|
|
50
|
+
success: false,
|
|
51
|
+
message,
|
|
52
|
+
errors
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Not found response
|
|
58
|
+
*/
|
|
59
|
+
static notFound(res, message = 'Resource not found') {
|
|
60
|
+
return res.status(404).json({
|
|
61
|
+
success: false,
|
|
62
|
+
message
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Unauthorized response
|
|
68
|
+
*/
|
|
69
|
+
static unauthorized(res, message = 'Unauthorized access') {
|
|
70
|
+
return res.status(401).json({
|
|
71
|
+
success: false,
|
|
72
|
+
message
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Forbidden response
|
|
78
|
+
*/
|
|
79
|
+
static forbidden(res, message = 'Access forbidden') {
|
|
80
|
+
return res.status(403).json({
|
|
81
|
+
success: false,
|
|
82
|
+
message
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Created response (201)
|
|
88
|
+
*/
|
|
89
|
+
static created(res, data, message = 'Resource created successfully') {
|
|
90
|
+
return res.status(201).json({
|
|
91
|
+
success: true,
|
|
92
|
+
message,
|
|
93
|
+
data
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Updated response
|
|
99
|
+
*/
|
|
100
|
+
static updated(res, data, message = 'Resource updated successfully') {
|
|
101
|
+
return res.status(200).json({
|
|
102
|
+
success: true,
|
|
103
|
+
message,
|
|
104
|
+
data
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Deleted response
|
|
110
|
+
*/
|
|
111
|
+
static deleted(res, message = 'Resource deleted successfully') {
|
|
112
|
+
return res.status(200).json({
|
|
113
|
+
success: true,
|
|
114
|
+
message
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Bad request response
|
|
120
|
+
*/
|
|
121
|
+
static badRequest(res, message = 'Bad request', errors = null) {
|
|
122
|
+
const response = {
|
|
123
|
+
success: false,
|
|
124
|
+
message
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
if (errors) {
|
|
128
|
+
response.errors = errors;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return res.status(400).json(response);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Server error response
|
|
136
|
+
*/
|
|
137
|
+
static serverError(res, error = null, message = 'Internal server error') {
|
|
138
|
+
const response = {
|
|
139
|
+
success: false,
|
|
140
|
+
message
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Only include error details in development
|
|
144
|
+
if (process.env.NODE_ENV === 'development' && error) {
|
|
145
|
+
response.error = {
|
|
146
|
+
message: error.message,
|
|
147
|
+
stack: error.stack
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return res.status(500).json(response);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Format error details for response
|
|
156
|
+
*/
|
|
157
|
+
static formatErrors(errorArray) {
|
|
158
|
+
return errorArray.map(err => ({
|
|
159
|
+
field: err.field || err.param,
|
|
160
|
+
message: err.message,
|
|
161
|
+
value: err.value
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Bulk success response
|
|
167
|
+
*/
|
|
168
|
+
static bulkSuccess(res, successCount, failureCount, message = 'Bulk operation completed') {
|
|
169
|
+
return res.status(200).json({
|
|
170
|
+
success: true,
|
|
171
|
+
message,
|
|
172
|
+
data: {
|
|
173
|
+
total: successCount + failureCount,
|
|
174
|
+
successful: successCount,
|
|
175
|
+
failed: failureCount
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { body, param } from 'express-validator';
|
|
3
|
+
import { validateErrors } from '../middleware/validation.js';
|
|
4
|
+
import { authenticateToken } from '../middleware/auth.js';
|
|
5
|
+
import __MODEL_NAME__ from '../models/__MODEL_NAME__.model.js';
|
|
6
|
+
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
|
|
9
|
+
// ========== GET - Fetch All / By Filter ==========
|
|
10
|
+
router.get('/', async (req, res, next) => {
|
|
11
|
+
try {
|
|
12
|
+
const data = await __MODEL_NAME__.find().limit(100);
|
|
13
|
+
|
|
14
|
+
if (!data || data.length === 0) {
|
|
15
|
+
return res.status(404).json({
|
|
16
|
+
success: false,
|
|
17
|
+
message: '__MODEL_NAME__ not found'
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
res.status(200).json({
|
|
22
|
+
success: true,
|
|
23
|
+
count: data.length,
|
|
24
|
+
data
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
next(error);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ========== GET SINGLE ==========
|
|
32
|
+
router.get('/:id', async (req, res, next) => {
|
|
33
|
+
try {
|
|
34
|
+
const data = await __MODEL_NAME__.findById(req.params.id);
|
|
35
|
+
|
|
36
|
+
if (!data) {
|
|
37
|
+
return res.status(404).json({
|
|
38
|
+
success: false,
|
|
39
|
+
message: '__MODEL_NAME__ not found'
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
res.status(200).json({
|
|
44
|
+
success: true,
|
|
45
|
+
data
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
next(error);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ========== POST - Create New ==========
|
|
53
|
+
router.post('/', authenticateToken, async (req, res, next) => {
|
|
54
|
+
try {
|
|
55
|
+
const newData = new __MODEL_NAME__(req.body);
|
|
56
|
+
const saved = await newData.save();
|
|
57
|
+
|
|
58
|
+
res.status(201).json({
|
|
59
|
+
success: true,
|
|
60
|
+
message: '__MODEL_NAME__ created successfully',
|
|
61
|
+
data: saved
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error.name === 'ValidationError') {
|
|
65
|
+
return res.status(400).json({
|
|
66
|
+
success: false,
|
|
67
|
+
message: 'Validation Error',
|
|
68
|
+
errors: Object.values(error.errors).map(e => e.message)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
next(error);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ========== PUT - Update Entire Document ==========
|
|
76
|
+
router.put('/:id', authenticateToken, async (req, res, next) => {
|
|
77
|
+
try {
|
|
78
|
+
const updated = await __MODEL_NAME__.findByIdAndUpdate(
|
|
79
|
+
req.params.id,
|
|
80
|
+
req.body,
|
|
81
|
+
{ new: true, runValidators: true }
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (!updated) {
|
|
85
|
+
return res.status(404).json({
|
|
86
|
+
success: false,
|
|
87
|
+
message: '__MODEL_NAME__ not found'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
res.status(200).json({
|
|
92
|
+
success: true,
|
|
93
|
+
message: '__MODEL_NAME__ updated successfully',
|
|
94
|
+
data: updated
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error.name === 'ValidationError') {
|
|
98
|
+
return res.status(400).json({
|
|
99
|
+
success: false,
|
|
100
|
+
message: 'Validation Error',
|
|
101
|
+
errors: Object.values(error.errors).map(e => e.message)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
next(error);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ========== DELETE ==========
|
|
109
|
+
router.delete('/:id', async (req, res, next) => {
|
|
110
|
+
try {
|
|
111
|
+
const deleted = await __MODEL_NAME__.findByIdAndDelete(req.params.id);
|
|
112
|
+
|
|
113
|
+
if (!deleted) {
|
|
114
|
+
return res.status(404).json({
|
|
115
|
+
success: false,
|
|
116
|
+
message: '__MODEL_NAME__ not found'
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
res.status(200).json({
|
|
121
|
+
success: true,
|
|
122
|
+
message: '__MODEL_NAME__ deleted successfully',
|
|
123
|
+
data: deleted
|
|
124
|
+
});
|
|
125
|
+
} catch (error) {
|
|
126
|
+
next(error);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
export default router;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Middleware
|
|
3
|
+
* Implements security best practices
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import helmet from 'helmet';
|
|
7
|
+
import mongoSanitize from 'express-mongo-sanitize';
|
|
8
|
+
import { body, validationResult } from 'express-validator';
|
|
9
|
+
|
|
10
|
+
// Helmet security headers
|
|
11
|
+
export const securityHeaders = helmet({
|
|
12
|
+
contentSecurityPolicy: {
|
|
13
|
+
directives: {
|
|
14
|
+
defaultSrc: ["'self'"],
|
|
15
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
16
|
+
scriptSrc: ["'self'"],
|
|
17
|
+
imgSrc: ["'self'", 'data:', 'https:']
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
hsts: {
|
|
21
|
+
maxAge: 31536000, // 1 year
|
|
22
|
+
includeSubDomains: true,
|
|
23
|
+
preload: true
|
|
24
|
+
},
|
|
25
|
+
frameguard: {
|
|
26
|
+
action: 'deny'
|
|
27
|
+
},
|
|
28
|
+
referrerPolicy: {
|
|
29
|
+
policy: 'strict-origin-when-cross-origin'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// MongoDB injection prevention
|
|
34
|
+
export const sanitizeData = (req, res, next) => {
|
|
35
|
+
// Sanitize req.body
|
|
36
|
+
if (req.body) {
|
|
37
|
+
for (const key in req.body) {
|
|
38
|
+
if (typeof req.body[key] === 'string') {
|
|
39
|
+
req.body[key] = mongoSanitize.sanitize(req.body[key]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Sanitize req.query
|
|
45
|
+
if (req.query) {
|
|
46
|
+
for (const key in req.query) {
|
|
47
|
+
if (typeof req.query[key] === 'string') {
|
|
48
|
+
req.query[key] = mongoSanitize.sanitize(req.query[key]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
next();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Validation error handler
|
|
57
|
+
export const validateErrors = (req, res, next) => {
|
|
58
|
+
const errors = validationResult(req);
|
|
59
|
+
if (!errors.isEmpty()) {
|
|
60
|
+
return res.status(400).json({
|
|
61
|
+
success: false,
|
|
62
|
+
errors: errors.array()
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
next();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Common validation chains
|
|
69
|
+
export const validators = {
|
|
70
|
+
email: body('email').isEmail().normalizeEmail(),
|
|
71
|
+
password: body('password').isLength({ min: 8 }).withMessage('Password must be at least 8 characters'),
|
|
72
|
+
url: body('url').isURL(),
|
|
73
|
+
id: body('id').isMongoId().withMessage('Invalid ID'),
|
|
74
|
+
phone: body('phone').isMobilePhone().optional(),
|
|
75
|
+
date: body('date').isISO8601().optional()
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default { securityHeaders, sanitizeData, validateErrors, validators };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import cors from 'cors';
|
|
4
|
+
import helmet from 'helmet';
|
|
5
|
+
import mongoose from 'mongoose';
|
|
6
|
+
import errorHandler from './middleware/errorHandler.js';
|
|
7
|
+
import requestLogger from './middleware/requestLogger.js';
|
|
8
|
+
|
|
9
|
+
const app = express();
|
|
10
|
+
const PORT = process.env.PORT || 5000;
|
|
11
|
+
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/__DB_NAME__';
|
|
12
|
+
|
|
13
|
+
// ============ MIDDLEWARE ============
|
|
14
|
+
app.use(express.json({ limit: '10mb' }));
|
|
15
|
+
app.use(express.urlencoded({ limit: '10mb', extended: true }));
|
|
16
|
+
app.use(cors());
|
|
17
|
+
app.use(requestLogger);
|
|
18
|
+
|
|
19
|
+
// ============ ROUTES ============
|
|
20
|
+
// Routes should be registered with /api prefix in __ROUTES__
|
|
21
|
+
// Example: app.use(`/api/users`, usersRoutes);
|
|
22
|
+
//
|
|
23
|
+
// Frontend Configuration:
|
|
24
|
+
// ā
CORRECT: VITE_API_URL = http://localhost:5000
|
|
25
|
+
// ā WRONG: VITE_API_URL = http://localhost:5000/api
|
|
26
|
+
// __ROUTES__
|
|
27
|
+
|
|
28
|
+
// ============ HEALTH CHECK ============
|
|
29
|
+
const healthHandler = (req, res) => {
|
|
30
|
+
res.status(200).json({
|
|
31
|
+
status: 'OK',
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
database: mongoose.connection.readyState ? 'Connected' : 'Disconnected'
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
app.get('/health', healthHandler);
|
|
38
|
+
app.get(`/api/health`, healthHandler);
|
|
39
|
+
|
|
40
|
+
// ============ ERROR HANDLER ============
|
|
41
|
+
app.use(errorHandler);
|
|
42
|
+
|
|
43
|
+
// ============ 404 HANDLER ============
|
|
44
|
+
app.use((req, res) => {
|
|
45
|
+
res.status(404).json({
|
|
46
|
+
error: 'Not Found',
|
|
47
|
+
path: req.path,
|
|
48
|
+
method: req.method
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ============ DATABASE CONNECTION ============
|
|
53
|
+
async function connectDB() {
|
|
54
|
+
try {
|
|
55
|
+
await mongoose.connect(MONGODB_URI, {
|
|
56
|
+
useNewUrlParser: true,
|
|
57
|
+
useUnifiedTopology: true,
|
|
58
|
+
serverSelectionTimeoutMS: 5000
|
|
59
|
+
});
|
|
60
|
+
console.log('ā
MongoDB Connected');
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('ā MongoDB Connection Failed:', error.message);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============ START SERVER ============
|
|
68
|
+
async function startServer() {
|
|
69
|
+
await connectDB();
|
|
70
|
+
|
|
71
|
+
app.listen(PORT, () => {
|
|
72
|
+
console.log(`\nš Backend Server Running`);
|
|
73
|
+
console.log(` URL: http://localhost:${PORT}`);
|
|
74
|
+
console.log(` Health: http://localhost:${PORT}/health`);
|
|
75
|
+
console.log(` API Health: http://localhost:${PORT}/api/health\n`);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============ GRACEFUL SHUTDOWN ============
|
|
80
|
+
process.on('SIGINT', async () => {
|
|
81
|
+
console.log('\nā Shutting down...');
|
|
82
|
+
await mongoose.disconnect();
|
|
83
|
+
process.exit(0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
startServer().catch(err => {
|
|
87
|
+
console.error(err);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export default app;
|