@feardread/fear 1.2.1 → 2.0.1
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 +76 -99
- package/FEARServer.js +290 -20
- package/controllers/address.js +9 -0
- package/controllers/auth/index.js +498 -92
- package/controllers/auth/password.js +237 -0
- package/controllers/order.js +0 -1
- package/controllers/payment.js +5 -142
- package/libs/db/index.js +5 -0
- package/libs/emailer/info.js +22 -34
- package/libs/emailer/smtp.js +511 -65
- package/libs/passport/index.js +137 -0
- package/libs/passport.js +22 -0
- package/libs/paypal/index.js +82 -0
- package/libs/stripe/index.js +306 -0
- package/libs/validator/index.js +2 -2
- package/models/address.js +37 -0
- package/models/blog.js +947 -17
- package/models/brand.js +205 -8
- package/models/category.js +445 -7
- package/models/events.js +1 -0
- package/models/order.js +29 -154
- package/models/payment.js +18 -79
- package/models/user.js +116 -49
- package/package.json +1 -1
- package/routes/address.js +16 -0
- package/routes/auth.js +9 -3
- package/routes/mail.js +10 -165
- package/routes/order.js +7 -4
- package/routes/password.js +17 -0
- package/routes/payment.js +4 -8
- package/routes/paypal.js +12 -0
- package/routes/stripe.js +27 -0
- package/libs/passport/passport.js +0 -109
- /package/routes/{events.js → event.js} +0 -0
package/FEARServer.js
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const express = require('express');
|
|
5
|
-
require(
|
|
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: '
|
|
12
|
-
build: 'backend/
|
|
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., '/
|
|
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
|
-
|
|
40
|
-
|
|
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(
|
|
48
|
-
this.fear.getLogger().info(
|
|
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
|
|
284
|
+
console.log('Unhandled Rejection at:', promise);
|
|
75
285
|
this.fear.getLogger().error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
76
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|