@feardread/fear 1.2.1 → 2.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/FEAR.js CHANGED
@@ -1,10 +1,13 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
3
  const express = require("express");
4
+ const session = require('express-session');
4
5
  const compression = require("compression");
5
6
  const cookieParser = require("cookie-parser");
6
7
  const fileUpload = require("express-fileupload");
7
8
  const cors = require("cors");
9
+ const MongoStore = require('connect-mongo');
10
+ const passport = require('passport');
8
11
 
9
12
  module.exports = FEAR = (() => {
10
13
  // Private constants
@@ -14,7 +17,7 @@ module.exports = FEAR = (() => {
14
17
  const AGENT_ROUTE_PATH = '/fear/api/agent';
15
18
 
16
19
  // Constructor function
17
- const FEAR = function() {
20
+ const FEAR = function (config) {
18
21
  this.app = express();
19
22
  this.Router = express.Router;
20
23
  this.server = null;
@@ -29,100 +32,67 @@ module.exports = FEAR = (() => {
29
32
  this.logo = null;
30
33
  this.origins = [];
31
34
  this.corsConfig = null;
35
+ this.stripe = null;
36
+ this.paypal = null;
37
+ this.passport = null;
38
+ this.mailer = null;
39
+
32
40
 
33
- // Initialize
34
41
  this.setupEnvironment();
35
42
  this.setupDependencies();
43
+ if (this.env.ADD_PAYMENTS) this.setupProcessors();
44
+ this.setupMailer();
36
45
  this.setupMiddleware();
37
46
  this.corsConfig = this.getCorsConfig();
38
47
  this.setupRoutes();
39
48
  };
40
49
 
41
- // Consolidated prototype
50
+
42
51
  FEAR.prototype = {
43
52
  constructor: FEAR,
44
-
45
- /**
46
- * Get AI Agent service instance
47
- */
48
- getAiAgent() {
49
- return this.agentService;
53
+ getStripe() {
54
+ return this.stripe;
55
+ },
56
+ getPassport() {
57
+ return this.passport;
50
58
  },
51
-
52
- /**
53
- * Clear global FearRouter
54
- */
55
59
  clearGlobal() {
56
60
  delete global.FearRouter;
57
61
  return this;
58
62
  },
59
-
60
- /**
61
- * Get Express app instance
62
- */
63
63
  getApp() {
64
64
  return this.app;
65
65
  },
66
-
67
- /**
68
- * Get logger instance
69
- */
70
66
  getLogger() {
71
67
  return this.logger;
72
68
  },
73
-
74
- /**
75
- * Get database instance
76
- */
77
69
  getDatabase() {
78
70
  return this.db;
79
71
  },
80
-
81
- /**
82
- * Get environment configuration
83
- */
84
72
  getEnvironment() {
85
73
  return this.env;
86
74
  },
87
-
88
- /**
89
- * Get cloud service instance
90
- */
91
75
  getCloud() {
92
76
  return this.cloud;
93
77
  },
94
-
95
- /**
96
- * Get Express Router
97
- */
98
78
  getRouter() {
99
79
  return this.Router;
100
80
  },
101
-
102
- /**
103
- * Get validator instance
104
- */
105
81
  getValidator() {
106
82
  return this.validator;
107
83
  },
108
-
109
- /**
110
- * Get handler instance
111
- */
112
84
  getHandler() {
113
85
  return this.handler;
114
86
  },
115
-
116
- /**
117
- * Get CORS configuration
118
- */
87
+ getPaypal() {
88
+ return this.paypal;
89
+ },
90
+ getMailer() {
91
+ return this.mailer;
92
+ },
119
93
  getCorsConfigValue() {
120
94
  return this.corsConfig;
121
95
  },
122
-
123
- /**
124
- * Setup environment variables from .env file
125
- */
126
96
  setupEnvironment() {
127
97
  const envResult = require("dotenv").config({ path: ".env" });
128
98
 
@@ -138,6 +108,7 @@ module.exports = FEAR = (() => {
138
108
  */
139
109
  setupDependencies() {
140
110
  this.logger = require("./libs/logger");
111
+ this.passport = require('./libs/passport');
141
112
  this.morgan = require("./libs/logger/morgan");
142
113
  this.cloud = require("./libs/cloud");
143
114
  this.db = require("./libs/db");
@@ -147,26 +118,66 @@ module.exports = FEAR = (() => {
147
118
  this.origins = this.getAllowedOrigins();
148
119
  },
149
120
 
121
+ setupProcessors() {
122
+ const StripeHandler = require("./libs/stripe");
123
+ const PayPal = require('./libs/paypal');
124
+
125
+ if ( !this.env.PAYPAL_CLIENT_ID|| !this.env.STRIPE_API_KEY ) {
126
+ this.logger.warn('Missing environment values in .env file')
127
+ }
128
+
129
+ this.stripe = new StripeHandler(this);
130
+ this.paypal = new PayPal(this);
131
+ },
132
+
150
133
  /**
151
134
  * Setup Express middleware
152
135
  */
153
136
  setupMiddleware() {
154
137
  this.app.set("PORT", this.env.NODE_PORT || DEFAULT_PORT);
155
-
156
- this.app.use(this.morgan);
157
138
  this.app.use(express.json({ limit: DEFAULT_JSON_LIMIT }));
139
+ this.app.use(express.urlencoded({ extended: true }));
140
+ this.app.use(session({
141
+ secret: process.env.SESSION_SECRET,
142
+ resave: false,
143
+ saveUninitialized: false,
144
+ //store: MongoStore.create({
145
+ //mongoUrl: process.env.DB_LINK,
146
+ //touchAfter: 24 * 3600 // lazy session update (24 hours)
147
+ //}),
148
+ cookie: {
149
+ maxAge: 1000 * 60 * 60 * 24 * 7, // 1 week
150
+ httpOnly: true,
151
+ secure: process.env.NODE_ENV === 'production' // HTTPS only in production
152
+ }
153
+ }));
154
+ this.app.use(this.morgan);
158
155
  this.app.use(compression());
159
156
  this.app.use(fileUpload());
160
157
  this.app.use(cookieParser());
158
+ this.app.use(passport.initialize());
159
+ this.app.use(passport.session());
161
160
 
162
- // Request logging middleware
161
+ require('./libs/passport');
163
162
  this.app.use((req, res, next) => {
164
163
  this.logger.info(`FEAR API Query :: ${req.url}`);
165
- res.locals.user = req.user;
164
+ res.locals.user = req.user || null;
166
165
  next();
167
166
  });
168
167
  },
169
168
 
169
+ setupMailer() {
170
+ const mailService = (this.env.NODE_ENV === 'production') ? 'mailgun' : 'google';
171
+ const mailinfo = require('./libs/emailer/info');
172
+ const smtp = require('./libs/emailer/smtp')
173
+
174
+ mailinfo.service = mailService;
175
+ this.mailinfo = mailinfo;
176
+
177
+ if ( !this.mailer ) {
178
+ this.mailer = new smtp(this);
179
+ }
180
+ },
170
181
  /**
171
182
  * Parse allowed origins from environment
172
183
  */
@@ -287,6 +298,8 @@ module.exports = FEAR = (() => {
287
298
  router.getHandler = () => this.handler;
288
299
  router.getValidator = () => this.validator;
289
300
  router.getAiAgent = () => this.agentService;
301
+ router.getStripe = () => this.stripe;
302
+ router.getPaypal = () => this.paypal;
290
303
  //router.getAgentWebInterface = () => this.agentWebInterface;
291
304
 
292
305
  return router;
@@ -314,14 +327,14 @@ module.exports = FEAR = (() => {
314
327
  this.logger.error(`Failed to start server on port ${serverPort}:`, err);
315
328
  return reject(err);
316
329
  }
317
-
330
+
318
331
  this.logger.info(`FEAR server started on port ${serverPort}`);
319
-
332
+
320
333
  // Log agent interface status
321
334
  if (this.agentWebInterface) {
322
335
  this.logger.info(`Agent Web Interface available at ${AGENT_ROUTE_PATH}`);
323
336
  }
324
-
337
+
325
338
  resolve(this.server);
326
339
  });
327
340
  });
@@ -347,7 +360,7 @@ module.exports = FEAR = (() => {
347
360
  }
348
361
 
349
362
  // Shutdown agent web interface
350
- const agentShutdown = this.agentWebInterface &&
363
+ const agentShutdown = this.agentWebInterface &&
351
364
  typeof this.agentWebInterface.shutdown === 'function' ?
352
365
  this.agentWebInterface.shutdown() :
353
366
  Promise.resolve();
@@ -397,8 +410,8 @@ module.exports = FEAR = (() => {
397
410
  getDatabase: () => this.db,
398
411
  getEnvironment: () => this.env,
399
412
  getCloud: () => this.cloud,
400
- getAiAgent: () => this.agentService,
401
- getAgentWebInterface: () => this.agentWebInterface
413
+ getStripe: () => this.stripe,
414
+ initProcessors: this.setupProcessors
402
415
  };
403
416
  return this;
404
417
  }
package/FEARServer.js CHANGED
@@ -2,14 +2,15 @@
2
2
 
3
3
  const path = require('path');
4
4
  const express = require('express');
5
- require("dotenv").config();
5
+ const fs = require('fs');
6
+ const dotenv = require('dotenv');
6
7
 
7
8
  const FearServer = (function () {
8
9
  // Private constants
9
10
  const DEFAULT_PATHS = {
10
11
  root: path.resolve(),
11
- app: '/backend/dashboard/build',
12
- build: 'backend/dashboard/build'
12
+ app: 'backend/admin/build',
13
+ build: 'backend/admin/build'
13
14
  };
14
15
 
15
16
  const DEFAULT_PORT = 4000;
@@ -22,40 +23,170 @@ const FearServer = (function () {
22
23
  this.Router = null;
23
24
  this.isShuttingDown = false;
24
25
  this.rootDir = path.resolve();
26
+ this.reactApps = []; // Track multiple React apps
27
+ this.envLoaded = false;
25
28
  }
26
29
 
27
30
  FearServer.prototype = {
28
31
  constructor: FearServer,
29
32
 
33
+ /**
34
+ * Load environment variables from .env file
35
+ * @param {string|Object} envConfig - Path to .env file or dotenv config object
36
+ */
37
+ loadEnv(envConfig) {
38
+ let envPath;
39
+ let options = {};
40
+
41
+ // Handle different config formats
42
+ if (typeof envConfig === 'string') {
43
+ envPath = envConfig;
44
+ } else if (typeof envConfig === 'object') {
45
+ envPath = envConfig.path;
46
+ options = envConfig;
47
+ }
48
+
49
+ // Auto-detect .env file if not specified
50
+ if (!envPath) {
51
+ const possiblePaths = [
52
+ path.join(this.rootDir, '.env'),
53
+ path.join(this.rootDir, '.env.local'),
54
+ path.join(this.rootDir, '.env.development'),
55
+ path.join(this.rootDir, '.env.production'),
56
+ path.join(process.cwd(), '.env'),
57
+ ];
58
+
59
+ // Check NODE_ENV for environment-specific files
60
+ if (process.env.NODE_ENV) {
61
+ possiblePaths.unshift(
62
+ path.join(this.rootDir, `.env.${process.env.NODE_ENV}`),
63
+ path.join(this.rootDir, `.env.${process.env.NODE_ENV}.local`)
64
+ );
65
+ }
66
+
67
+ // Find first existing .env file
68
+ envPath = possiblePaths.find(p => fs.existsSync(p));
69
+ }
70
+
71
+ if (envPath && !fs.existsSync(envPath)) {
72
+ console.warn(`⚠️ Warning: .env file not found at ${envPath}`);
73
+ return false;
74
+ }
75
+
76
+ if (!envPath) {
77
+ console.warn('Warning: No .env file found in common locations');
78
+ console.warn(' Checked:', [
79
+ path.join(this.rootDir, '.env'),
80
+ path.join(this.rootDir, `.env.${process.env.NODE_ENV || 'development'}`),
81
+ path.join(process.cwd(), '.env')
82
+ ]);
83
+ return false;
84
+ }
85
+
86
+ // Load the .env file
87
+ const result = dotenv.config({ path: envPath, ...options });
88
+
89
+ if (result.error) {
90
+ console.error('Error loading .env file:', result.error);
91
+ return false;
92
+ }
93
+
94
+ this.envLoaded = true;
95
+ return true;
96
+ },
97
+
98
+ /**
99
+ * Validate that required files exist for React app
100
+ * @param {string} buildPath - Path to build directory
101
+ * @param {string} indexPath - Path to index.html
102
+ */
103
+ validateReactApp(buildPath, indexPath) {
104
+ if (!fs.existsSync(buildPath)) {
105
+ throw new Error(`Build directory not found: ${buildPath}`);
106
+ }
107
+
108
+ if (!fs.existsSync(indexPath)) {
109
+ throw new Error(`index.html not found: ${indexPath}`);
110
+ }
111
+
112
+ this.fear.getLogger().info('✓ React app files validated');
113
+ return true;
114
+ },
115
+
30
116
  /**
31
117
  * Configure static file serving for React SPA
32
118
  * @param {string} root - Root directory path
33
- * @param {string} app - App directory path
34
- * @param {string} build - Build directory path
35
- * @param {string} basePath - Base path for the app (e.g., '/fear/sites/ghap')
119
+ * @param {string} app - App directory path relative to root
120
+ * @param {string} build - Build directory path for index.html
121
+ * @param {string} basePath - Base path for the app (e.g., '/admin', '/dashboard')
36
122
  */
37
123
  setupStaticFiles(root, app, build, basePath = '') {
38
124
  this.rootDir = root || path.resolve();
39
- const buildPath = path.join(this.rootDir, app);
40
- const indexPath = path.resolve(this.rootDir, build, "index.html");
41
-
125
+
126
+ // Construct paths - handle both absolute and relative paths
127
+ const buildPath = path.isAbsolute(app)
128
+ ? app
129
+ : path.join(this.rootDir, app);
130
+
131
+ const indexPath = path.isAbsolute(build)
132
+ ? path.join(build, "index.html")
133
+ : path.join(this.rootDir, build, "index.html");
134
+
135
+ // Normalize base path
42
136
  const normalizedBasePath = basePath
43
137
  ? `/${basePath.replace(/^\/+|\/+$/g, '')}`
44
138
  : '';
45
139
 
46
-
47
- this.fear.getLogger().info(`Serving static files from: ${buildPath}`);
48
- this.fear.getLogger().info(`Base URL path: ${normalizedBasePath || '/'}`);
140
+ // Debug logging
141
+ this.fear.getLogger().info('═══════════════════════════════════════');
142
+ this.fear.getLogger().info('React App Path Resolution:');
143
+ this.fear.getLogger().info(`→ Build path: ${buildPath}`);
144
+ this.fear.getLogger().info(`→ Index path: ${indexPath}`);
145
+ this.fear.getLogger().info(`→ Base path: ${normalizedBasePath || '/'}`);
49
146
 
147
+ // Validate React app exists
148
+ try {
149
+ this.validateReactApp(buildPath, indexPath);
150
+ } catch (error) {
151
+ this.fear.getLogger().error('React app validation failed:', error.message);
152
+ throw error;
153
+ }
154
+
155
+ // Serve static files with caching headers
50
156
  this.fear.getApp().use(
51
157
  normalizedBasePath,
158
+ (req, res, next) => {
159
+ this.fear.getLogger().debug(`Static file request: ${req.path}`);
160
+ next();
161
+ },
52
162
  express.static(buildPath, {
53
163
  index: false,
54
- fallthrough: true
164
+ fallthrough: true,
165
+ maxAge: '1h', // Cache static assets
166
+ etag: true,
167
+ lastModified: true,
168
+ setHeaders: (res, filePath) => {
169
+ // Don't cache index.html
170
+ if (filePath.endsWith('index.html')) {
171
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
172
+ }
173
+ // Cache JS/CSS files aggressively
174
+ else if (filePath.match(/\.(js|css|woff2?|ttf|eot)$/)) {
175
+ res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
176
+ }
177
+ }
55
178
  })
56
179
  );
57
180
 
58
- this.fear.getApp().get(`${normalizedBasePath}/*`, (req, res) => {
181
+ this.fear.getApp().get(`${normalizedBasePath}/*`, (req, res, next) => {
182
+ this.fear.getLogger().debug(`Route request: ${req.path}`);
183
+
184
+ // Skip if it's an API route
185
+ if (req.path.startsWith('/fear/api')) {
186
+ this.fear.getLogger().debug('Skipping - API route');
187
+ return next();
188
+ }
189
+
59
190
  res.sendFile(indexPath, (err) => {
60
191
  if (err) {
61
192
  this.fear.getLogger().error('Error serving index.html:', err);
@@ -63,6 +194,85 @@ const FearServer = (function () {
63
194
  }
64
195
  });
65
196
  });
197
+
198
+ // Track registered React apps
199
+ this.reactApps.push({
200
+ basePath: normalizedBasePath || '/',
201
+ buildPath,
202
+ indexPath
203
+ });
204
+
205
+ this.fear.getLogger().info(`React app configured at: ${normalizedBasePath || '/'}`);
206
+ },
207
+
208
+ /**
209
+ * Add multiple React apps at different base paths
210
+ * @param {Array} apps - Array of app configurations
211
+ * Example: [{root: __dirname, app: '/build', build: 'build', basePath: '/admin'}]
212
+ */
213
+ setupMultipleReactApps(apps) {
214
+ if (!Array.isArray(apps)) {
215
+ throw new Error('setupMultipleReactApps expects an array of app configurations');
216
+ }
217
+
218
+ apps.forEach((appConfig, index) => {
219
+ this.fear.getLogger().info(`Setting up React app ${index + 1}/${apps.length}`);
220
+ this.setupStaticFiles(
221
+ appConfig.root,
222
+ appConfig.app,
223
+ appConfig.build,
224
+ appConfig.basePath
225
+ );
226
+ });
227
+ },
228
+
229
+ /**
230
+ * Setup CORS for React development
231
+ * @param {Object} options - CORS options
232
+ */
233
+ setupCORS(options = {}) {
234
+ const defaultOptions = {
235
+ origin: options.origin || '*',
236
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
237
+ allowedHeaders: ['Content-Type', 'Authorization'],
238
+ credentials: options.credentials || false
239
+ };
240
+
241
+ this.fear.getApp().use((req, res, next) => {
242
+ res.header('Access-Control-Allow-Origin', defaultOptions.origin);
243
+ res.header('Access-Control-Allow-Methods', defaultOptions.methods.join(', '));
244
+ res.header('Access-Control-Allow-Headers', defaultOptions.allowedHeaders.join(', '));
245
+
246
+ if (defaultOptions.credentials) {
247
+ res.header('Access-Control-Allow-Credentials', 'true');
248
+ }
249
+
250
+ // Handle preflight
251
+ if (req.method === 'OPTIONS') {
252
+ return res.sendStatus(200);
253
+ }
254
+
255
+ next();
256
+ });
257
+
258
+ this.fear.getLogger().info('CORS configured for React development');
259
+ },
260
+
261
+ /**
262
+ * Setup API routes prefix (useful to avoid conflicts with React routes)
263
+ * @param {string} prefix - API prefix (e.g., '/api')
264
+ */
265
+ setupAPIPrefix(prefix = '/fear/api') {
266
+ const normalizedPrefix = `/${prefix.replace(/^\/+|\/+$/g, '')}`;
267
+
268
+ this.fear.getApp().use(normalizedPrefix, (req, res, next) => {
269
+
270
+ req.isAPIRoute = true;
271
+ next();
272
+ });
273
+
274
+ this.fear.getLogger().info(`API routes configured with prefix: ${normalizedPrefix}`);
275
+ return normalizedPrefix;
66
276
  },
67
277
 
68
278
  /**
@@ -71,9 +281,9 @@ const FearServer = (function () {
71
281
  setupProcessHandlers() {
72
282
  // Handle unhandled promise rejections
73
283
  process.on("unhandledRejection", (reason, promise) => {
74
- console.log('Unhandled Rejection at:', promise, 'reason:', reason);
284
+ console.log('Unhandled Rejection at:', promise);
75
285
  this.fear.getLogger().error('Unhandled Rejection at:', promise, 'reason:', reason);
76
- //this.gracefulShutdown('unhandledRejection');
286
+ this.gracefulShutdown('unhandledRejection');
77
287
  });
78
288
 
79
289
  // Handle uncaught exceptions
@@ -183,15 +393,46 @@ const FearServer = (function () {
183
393
 
184
394
  /**
185
395
  * Initialize FEAR application
396
+ * @param {Object} paths - Path configuration
397
+ * @param {boolean} ADD_PAYMENTS - Whether to add payment routes
398
+ * @param {Object} reactConfig - Additional React configuration
399
+ * @param {string|Object} envConfig - Environment file configuration
186
400
  */
187
- initialize(paths = DEFAULT_PATHS) {
401
+ initialize(paths = DEFAULT_PATHS, ADD_PAYMENTS = false, reactConfig = {}, envConfig = null) {
188
402
  try {
403
+ // Set root directory first
404
+ this.rootDir = paths.root || path.resolve();
405
+
406
+ // Load environment variables if config provided
407
+ if (envConfig) {
408
+ this.loadEnv(envConfig);
409
+ } else if (!this.envLoaded) {
410
+ // Try auto-loading if not already loaded
411
+ this.loadEnv();
412
+ }
413
+
189
414
  // Import FEAR after dotenv is configured
190
415
  const FearFactory = require("./FEAR");
191
- this.fear = new FearFactory();
416
+ this.fear = new FearFactory({ADD_PAYMENTS});
192
417
  this.Router = this.fear.Router;
193
418
 
194
- this.setupStaticFiles(paths.root, paths.app, paths.build, paths.basePath);
419
+ // Setup CORS if enabled
420
+ if (reactConfig.enableCORS) {
421
+ this.setupCORS(reactConfig.corsOptions);
422
+ }
423
+
424
+ // Setup API prefix if provided
425
+ if (reactConfig.apiPrefix) {
426
+ this.setupAPIPrefix(reactConfig.apiPrefix);
427
+ }
428
+
429
+ // Setup React app(s)
430
+ if (reactConfig.multipleApps && Array.isArray(reactConfig.apps)) {
431
+ this.setupMultipleReactApps(reactConfig.apps);
432
+ } else {
433
+ this.setupStaticFiles(paths.root, paths.app, paths.build, paths.basePath);
434
+ }
435
+
195
436
  this.setupProcessHandlers();
196
437
 
197
438
  return Promise.resolve(this.fear);
@@ -213,6 +454,9 @@ const FearServer = (function () {
213
454
  logger.warn(this.fear.logo);
214
455
  }
215
456
 
457
+ logger.info('Starting FEAR Server...');
458
+ logger.info('═══════════════════════════════════════');
459
+
216
460
  // Initialize database connection
217
461
  return this.initializeDatabase()
218
462
  .then(() => {
@@ -221,7 +465,19 @@ const FearServer = (function () {
221
465
  })
222
466
  .then((server) => {
223
467
  this.server = server;
224
- logger.info(`FEAR API Initialized :: Port ${port}`);
468
+ logger.info('═══════════════════════════════════════');
469
+ logger.info(`FEAR API Server Running on Port ${port}`);
470
+ logger.info('═══════════════════════════════════════');
471
+
472
+ // Display registered React apps
473
+ if (this.reactApps.length > 0) {
474
+ logger.info('📱 React Apps:');
475
+ this.reactApps.forEach(app => {
476
+ logger.info(`• http://localhost:${port}${app.basePath}`);
477
+ });
478
+ logger.info('═══════════════════════════════════════');
479
+ }
480
+
225
481
  return server;
226
482
  })
227
483
  .catch((error) => {
@@ -270,6 +526,20 @@ const FearServer = (function () {
270
526
  */
271
527
  getRootDir() {
272
528
  return this.rootDir;
529
+ },
530
+
531
+ /**
532
+ * Get registered React apps
533
+ */
534
+ getReactApps() {
535
+ return this.reactApps;
536
+ },
537
+
538
+ /**
539
+ * Check if environment variables are loaded
540
+ */
541
+ isEnvLoaded() {
542
+ return this.envLoaded;
273
543
  }
274
544
  };
275
545
 
@@ -0,0 +1,9 @@
1
+ const Address = require("../models/address");
2
+ const methods = require("./crud");
3
+
4
+ const crud = methods.crudController( Address );
5
+ for(prop in crud) {
6
+ if(crud.hasOwnProperty(prop)) {
7
+ module.exports[prop] = crud[prop];
8
+ }
9
+ }