@feardread/fear 1.0.6 → 1.0.8

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
@@ -23,8 +23,6 @@ module.exports = FEAR = (() => {
23
23
  this.logger = null;
24
24
  this.morgan = null;
25
25
  this.cloud = null;
26
- this.agentService = null;
27
- this.agentWebInterface = null;
28
26
  this.db = null;
29
27
  this.handler = null;
30
28
  this.validator = null;
@@ -38,47 +36,12 @@ module.exports = FEAR = (() => {
38
36
  this.setupMiddleware();
39
37
  this.corsConfig = this.getCorsConfig();
40
38
  this.setupRoutes();
41
- //this.setupAiAgent();
42
- //this.setupAgentWebInterface();
43
39
  };
44
40
 
45
41
  // Consolidated prototype
46
42
  FEAR.prototype = {
47
43
  constructor: FEAR,
48
44
 
49
- /**
50
- * Initialize AI Agent service
51
- */
52
- setupAiAgent() {
53
- const { getInstance } = require("./libs/agent");
54
-
55
- if (!this.agentService) {
56
- this.agentService = getInstance();
57
- }
58
- },
59
-
60
- /**
61
- * Initialize Agent Web Interface
62
- */
63
- setupAgentWebInterface() {
64
- try {
65
- const AgentWebInterface = require("./libs/agent");
66
-
67
- this.agentWebInterface = new AgentWebInterface(this);
68
-
69
- // Register agent routes
70
- this.useRouter(
71
- this.agentWebInterface.getRouter(),
72
- AGENT_ROUTE_PATH
73
- );
74
-
75
- this.logger.info(`Agent Web Interface initialized at ${AGENT_ROUTE_PATH}`);
76
- } catch (error) {
77
- this.logger.error("Failed to initialize Agent Web Interface:", error);
78
- this.logger.warn("Agent Web Interface will not be available");
79
- }
80
- },
81
-
82
45
  /**
83
46
  * Get AI Agent service instance
84
47
  */
@@ -86,13 +49,6 @@ module.exports = FEAR = (() => {
86
49
  return this.agentService;
87
50
  },
88
51
 
89
- /**
90
- * Get Agent Web Interface instance
91
- */
92
- getAgentWebInterface() {
93
- return this.agentWebInterface;
94
- },
95
-
96
52
  /**
97
53
  * Clear global FearRouter
98
54
  */
@@ -307,7 +263,7 @@ module.exports = FEAR = (() => {
307
263
 
308
264
  routers.forEach(config => {
309
265
  if (typeof config === 'function') {
310
- this.useRouter(config);
266
+ this.useRouter(config, DEFAULT_ROUTE_PATH, this.corsConfig);
311
267
  } else if (config && config.router) {
312
268
  this.useRouter(config.router, config.path, config.cors);
313
269
  } else {
@@ -1,43 +1,52 @@
1
1
  const { tryCatch } = require("../../libs/handler/error");
2
2
  const cloud = require("../../libs/cloud");
3
3
  const SearchApi = require("../../libs/search/api");
4
+ const mongoose = require("mongoose");
4
5
 
5
6
  /**
6
- * Generic CRUD operations for any Mongoose Model
7
- * All methods use then/catch pattern for consistency
7
+ * Validates MongoDB ObjectId format
8
+ * @param {string} id - ID to validate
9
+ * @returns {boolean} True if valid ObjectId
8
10
  */
11
+ const isValidObjectId = (id) => {
12
+ return mongoose.Types.ObjectId.isValid(id) && /^[0-9a-fA-F]{24}$/.test(id);
13
+ };
14
+
9
15
  /**
10
16
  * Get all documents for requested Model
11
17
  * @param {mongoose.Model} Model - Mongoose model
12
18
  * @param {Object} req - Express request object
13
19
  * @param {Object} res - Express response object
14
- * @returns {boolean} Success
15
- * @returns {string} Message
16
- * @returns {Array} Result: Array of found Documents
20
+ * @returns {Promise<void>}
17
21
  */
18
22
  exports.all = tryCatch(async (Model, req, res) => {
19
- const { sort = 'category', order = 'asc', populate = true } = req.query;
20
- let query = Model.find();
21
- // Apply sorting
22
- const sortOrder = order === 'desc' ? 'desc' : 'asc';
23
- query = query.sort({ [sort]: sortOrder });
23
+ const { sort = 'category', order = 'asc', populate = 'true' } = req.query;
24
+
25
+ const sortOrder = order === 'desc' ? -1 : 1;
26
+ let query = Model.find().sort({ [sort]: sortOrder });
27
+
24
28
  // Apply population if requested
25
- if (populate && populate !== 'false') {
29
+ if (populate !== 'false') {
26
30
  query = query.populate();
27
31
  }
28
- return query
29
- .then((result) => {
30
- if (!result || result.length === 0) {
31
- return res.status(200).json({ result: [], success: true, message: "No documents found", count: 0 });
32
- }
33
- return res.status(200).json({
34
- result, success: true, message: `Found ${result.length} documents`, count: result.length
35
- });
36
- })
37
- .catch((error) => {
38
- console.error('Error in all method:', error);
39
- return res.status(500).json({ result: null, success: false, message: "Error retrieving documents", error: error.message });
32
+
33
+ const result = await query.exec();
34
+
35
+ if (!result || result.length === 0) {
36
+ return res.status(200).json({
37
+ result: [],
38
+ success: true,
39
+ message: "No documents found",
40
+ count: 0
40
41
  });
42
+ }
43
+
44
+ return res.status(200).json({
45
+ result,
46
+ success: true,
47
+ message: `Found ${result.length} documents`,
48
+ count: result.length
49
+ });
41
50
  });
42
51
 
43
52
  /**
@@ -45,65 +54,42 @@ exports.all = tryCatch(async (Model, req, res) => {
45
54
  * @param {mongoose.Model} Model - Mongoose model
46
55
  * @param {Object} req - Express request object
47
56
  * @param {Object} res - Express response object
48
- * @param {string} req.params.id - Document ID
49
- * @returns {Document} Single Document
57
+ * @returns {Promise<void>}
50
58
  */
51
59
  exports.read = tryCatch(async (Model, req, res) => {
52
- console.log('crud read id = ', req.params);
53
- /*
54
- if (!req.params || !req.params.id) {
60
+ const { id } = req.params;
61
+
62
+ if (!id) {
55
63
  return res.status(400).json({
56
64
  result: null,
57
65
  success: false,
58
66
  message: "Document ID is required"
59
67
  });
60
68
  }
61
- */
62
- const { id } = req.query;
63
- const { populate = true } = req.query;
64
69
 
65
- /*
66
- // Validate ObjectId format
67
- if (!id.match(/^[0-9a-fA-F]{24}$/)) {
70
+ if (!isValidObjectId(id)) {
68
71
  return res.status(400).json({
69
72
  result: null,
70
73
  success: false,
71
74
  message: "Invalid document ID format"
72
75
  });
73
76
  }
74
- */
75
- let query = Model.findById(id);
76
77
 
77
- // Apply population if requested
78
- if (populate && populate !== 'false') {
79
- query = query.populate();
80
- }
78
+ const result = await Model.findById(id).exec();
81
79
 
82
- return query
83
- .then((result) => {
84
- if (!result) {
85
- return res.status(404).json({
86
- result: null,
87
- success: false,
88
- message: `Document with ID ${id} not found`
89
- });
90
- }
91
-
92
- return res.status(200).json({
93
- result,
94
- success: true,
95
- message: "Document found successfully"
96
- });
97
- })
98
- .catch((error) => {
99
- console.error('Error in read method:', error);
100
- return res.status(500).json({
101
- result: null,
102
- success: false,
103
- message: "Error retrieving document",
104
- error: error.message
105
- });
80
+ if (!result) {
81
+ return res.status(404).json({
82
+ result: null,
83
+ success: false,
84
+ message: `Document with ID ${id} not found`
106
85
  });
86
+ }
87
+
88
+ return res.status(200).json({
89
+ result,
90
+ success: true,
91
+ message: "Document found successfully"
92
+ });
107
93
  });
108
94
 
109
95
  /**
@@ -111,63 +97,49 @@ exports.read = tryCatch(async (Model, req, res) => {
111
97
  * @param {mongoose.Model} Model - Mongoose model
112
98
  * @param {Object} req - Express request object
113
99
  * @param {Object} res - Express response object
114
- * @param {Object} req.body - Document data
115
- * @returns {string} Message
100
+ * @returns {Promise<void>}
116
101
  */
117
102
  exports.create = tryCatch(async (Model, req, res) => {
118
103
  const documentData = { ...req.body };
119
104
 
120
105
  // Handle image uploads if present
121
- if (documentData.images) {
122
- documentData.images.split(',').map(item => item.trim());
123
-
124
- const imageLinks = await cloud.uploadImages(documentData.images);
125
- if ( imageLinks ) documentData.images = imageLinks;
106
+ if (documentData.images) {
107
+ // Parse comma-separated string to array if needed
108
+ const imageArray = typeof documentData.images === 'string'
109
+ ? documentData.images.split(',').map(item => item.trim())
110
+ : documentData.images;
111
+
112
+ let imageLinks = await cloud.uploadImages(imageArray);
113
+
114
+ if (imageLinks) {
115
+ documentData.images = imageLinks;
126
116
  console.log('Added Images:', imageLinks);
117
+ }
127
118
  }
128
- console.log('Creating modified document:', documentData);
129
- await new Model(documentData).save()
130
- .then((result) => {
131
- if (!result) {
132
- throw new Error("Failed to save document");
133
- }
134
- return res.status(201).json({
135
- result, success: true, message: `Document created successfully in ${Model.modelName} collection`
136
- });
137
- })
138
- .catch((error) => {
139
- console.error('Error in create method with images:', error);
140
-
141
- if (error.name === "ValidationError") {
142
- return res.status(400).json({
143
- result: null,
144
- success: false,
145
- message: "Validation failed: Required fields are missing or invalid",
146
- errors: error.errors
147
- });
148
- }
149
-
150
- return res.status(500).json({
151
- result: null,
152
- success: false,
153
- message: "Internal server error during document creation",
154
- error: error.message
155
- });
156
- });
157
119
 
120
+ console.log('Creating document:', documentData);
121
+
122
+ const document = new Model(documentData);
123
+ const result = await document.save();
124
+
125
+ return res.status(201).json({
126
+ result,
127
+ success: true,
128
+ message: `Document created successfully in ${Model.modelName} collection`
158
129
  });
130
+ });
159
131
 
160
132
  /**
161
133
  * Updates a single document
162
134
  * @param {mongoose.Model} Model - Mongoose model
163
135
  * @param {Object} req - Express request object
164
136
  * @param {Object} res - Express response object
165
- * @param {Object} req.body - Updated document data
166
- * @param {string} req.params.id - Document ID
167
- * @returns {Document} Returns updated document
137
+ * @returns {Promise<void>}
168
138
  */
169
139
  exports.update = tryCatch(async (Model, req, res) => {
170
- if (!req.params || !req.params.id) {
140
+ const { id } = req.params;
141
+
142
+ if (!id) {
171
143
  return res.status(400).json({
172
144
  result: null,
173
145
  success: false,
@@ -175,7 +147,14 @@ exports.update = tryCatch(async (Model, req, res) => {
175
147
  });
176
148
  }
177
149
 
178
- const { id } = req.params;
150
+ if (!isValidObjectId(id)) {
151
+ return res.status(400).json({
152
+ result: null,
153
+ success: false,
154
+ message: "Invalid document ID format"
155
+ });
156
+ }
157
+
179
158
  const updateData = { ...req.body };
180
159
 
181
160
  // Remove undefined values
@@ -185,114 +164,32 @@ exports.update = tryCatch(async (Model, req, res) => {
185
164
  }
186
165
  });
187
166
 
188
- // Validate ObjectId format
189
- if (!id.match(/^[0-9a-fA-F]{24}$/)) {
190
- return res.status(400).json({
191
- result: null,
192
- success: false,
193
- message: "Invalid document ID format"
194
- });
195
- }
196
-
197
167
  // Handle image uploads if present
198
- if (updateData.images && Array.isArray(updateData.images)) {
199
- return cloud.uploadImages(updateData.images)
200
- .then((uploadedImages) => {
201
- updateData.images = uploadedImages;
202
-
203
- // Update document with uploaded images
204
- return Model.findOneAndUpdate(
205
- { _id: id },
206
- updateData,
207
- { new: true, runValidators: true }
208
- ).exec();
209
- })
210
- .then((result) => {
211
- if (!result) {
212
- return res.status(404).json({
213
- result: null,
214
- success: false,
215
- message: `Document with ID ${id} not found`
216
- });
217
- }
218
-
219
- return res.status(200).json({
220
- result,
221
- success: true,
222
- message: `Document updated successfully with ID: ${id}`
223
- });
224
- })
225
- .catch((error) => {
226
- console.error('Error in update method with images:', error);
227
-
228
- if (error.name === "ValidationError") {
229
- return res.status(400).json({
230
- result: null,
231
- success: false,
232
- message: "Validation failed: Invalid field values provided",
233
- errors: error.errors
234
- });
235
- }
236
-
237
- return res.status(500).json({
238
- result: null,
239
- success: false,
240
- message: "Internal server error during document update",
241
- error: error.message
242
- });
243
- });
168
+ if (updateData.images && Array.isArray(updateData.images) && updateData.images.length > 0) {
169
+ const uploadedImages = await cloud.uploadImages(updateData.images);
170
+ updateData.images = uploadedImages;
244
171
  }
245
172
 
246
- // Update document without image uploads
247
- return Model.findOneAndUpdate(
248
- { _id: id },
173
+ // Update document
174
+ const result = await Model.findByIdAndUpdate(
175
+ id,
249
176
  updateData,
250
177
  { new: true, runValidators: true }
251
- )
252
- .exec()
253
- .then((result) => {
254
- if (!result) {
255
- return res.status(404).json({
256
- result: null,
257
- success: false,
258
- message: `Document with ID ${id} not found`
259
- });
260
- }
261
-
262
- return res.status(200).json({
263
- result,
264
- success: true,
265
- message: `Document updated successfully with ID: ${id}`
266
- });
267
- })
268
- .catch((error) => {
269
- console.error('Error in update method:', error);
270
-
271
- if (error.name === "ValidationError") {
272
- return res.status(400).json({
273
- result: null,
274
- success: false,
275
- message: "Validation failed: Invalid field values provided",
276
- errors: error.errors
277
- });
278
- }
279
-
280
- if (error.code === 11000) {
281
- return res.status(409).json({
282
- result: null,
283
- success: false,
284
- message: "Update failed: Duplicate value for unique field",
285
- error: error.message
286
- });
287
- }
288
-
289
- return res.status(500).json({
290
- result: null,
291
- success: false,
292
- message: "Internal server error during document update",
293
- error: error.message
294
- });
178
+ ).exec();
179
+
180
+ if (!result) {
181
+ return res.status(404).json({
182
+ result: null,
183
+ success: false,
184
+ message: `Document with ID ${id} not found`
295
185
  });
186
+ }
187
+
188
+ return res.status(200).json({
189
+ result,
190
+ success: true,
191
+ message: `Document updated successfully with ID: ${id}`
192
+ });
296
193
  });
297
194
 
298
195
  /**
@@ -300,11 +197,12 @@ exports.update = tryCatch(async (Model, req, res) => {
300
197
  * @param {mongoose.Model} Model - Mongoose model
301
198
  * @param {Object} req - Express request object
302
199
  * @param {Object} res - Express response object
303
- * @param {string} req.params.id - Document ID
304
- * @returns {string} Message response
200
+ * @returns {Promise<void>}
305
201
  */
306
202
  exports.delete = tryCatch(async (Model, req, res) => {
307
- if (!req.params || !req.params.id) {
203
+ const { id } = req.params;
204
+
205
+ if (!id) {
308
206
  return res.status(400).json({
309
207
  result: null,
310
208
  success: false,
@@ -312,10 +210,7 @@ exports.delete = tryCatch(async (Model, req, res) => {
312
210
  });
313
211
  }
314
212
 
315
- const { id } = req.params;
316
-
317
- // Validate ObjectId format
318
- if (!id.match(/^[0-9a-fA-F]{24}$/)) {
213
+ if (!isValidObjectId(id)) {
319
214
  return res.status(400).json({
320
215
  result: null,
321
216
  success: false,
@@ -323,38 +218,28 @@ exports.delete = tryCatch(async (Model, req, res) => {
323
218
  });
324
219
  }
325
220
 
326
- return Model.findOneAndDelete({ _id: id })
327
- .exec()
328
- .then((result) => {
329
- if (!result) {
330
- return res.status(404).json({
331
- result: null,
332
- success: false,
333
- message: `Document with ID ${id} not found`
334
- });
335
- }
336
-
337
- // TODO: Clean up associated images if they exist
338
- if (result.images && Array.isArray(result.images)) {
339
- // Could add cloud.deleteImages(result.images) here
340
- console.log('Document had images that should be cleaned up:', result.images);
341
- }
342
-
343
- return res.status(200).json({
344
- result,
345
- success: true,
346
- message: `Document deleted successfully with ID: ${id}`
347
- });
348
- })
349
- .catch((error) => {
350
- console.error('Error in delete method:', error);
351
- return res.status(500).json({
352
- result: null,
353
- success: false,
354
- message: "Error deleting document",
355
- error: error.message
356
- });
221
+ const result = await Model.findByIdAndDelete(id).exec();
222
+
223
+ if (!result) {
224
+ return res.status(404).json({
225
+ result: null,
226
+ success: false,
227
+ message: `Document with ID ${id} not found`
357
228
  });
229
+ }
230
+
231
+ // TODO: Clean up associated images if they exist
232
+ if (result.images && Array.isArray(result.images) && result.images.length > 0) {
233
+ console.log('Document had images that should be cleaned up:', result.images);
234
+ // Uncomment when cloud.deleteImages is available:
235
+ // await cloud.deleteImages(result.images);
236
+ }
237
+
238
+ return res.status(200).json({
239
+ result,
240
+ success: true,
241
+ message: `Document deleted successfully with ID: ${id}`
242
+ });
358
243
  });
359
244
 
360
245
  /**
@@ -362,71 +247,45 @@ exports.delete = tryCatch(async (Model, req, res) => {
362
247
  * @param {mongoose.Model} Model - Mongoose model
363
248
  * @param {Object} req - Express request object
364
249
  * @param {Object} res - Express response object
365
- * @param {Object} req.query - Query parameters
366
- * @returns {Object} Results with pagination
250
+ * @returns {Promise<void>}
367
251
  */
368
252
  exports.list = tryCatch(async (Model, req, res) => {
369
- const page = parseInt(req.query.page) || 1;
370
- const limit = parseInt(req.query.items) || 10;
253
+ const page = Math.max(1, parseInt(req.query.page) || 1);
254
+ const limit = Math.min(100, Math.max(1, parseInt(req.query.items) || 10));
371
255
  const skip = (page - 1) * limit;
372
- const { sort = 'createdAt', order = 'desc', populate = true } = req.query;
256
+ const { sort = 'createdAt', order = 'desc', populate = 'true' } = req.query;
373
257
 
374
- // Limit maximum items per page
375
- const maxLimit = Math.min(limit, 100);
376
258
  const sortOrder = order === 'asc' ? 1 : -1;
377
259
 
378
- // Create count and results promises
379
- const countPromise = Model.countDocuments();
380
-
381
- let resultsQuery = Model.find()
382
- .skip(skip)
383
- .limit(maxLimit)
384
- .sort({ [sort]: sortOrder });
385
-
386
- // Apply population if requested
387
- if (populate && populate !== 'false') {
388
- resultsQuery = resultsQuery.populate();
389
- }
260
+ // Execute count and find queries in parallel
261
+ const [result, count] = await Promise.all([
262
+ Model.find()
263
+ .skip(skip)
264
+ .limit(limit)
265
+ .sort({ [sort]: sortOrder })
266
+ .populate(populate !== 'false' ? undefined : null)
267
+ .exec(),
268
+ Model.countDocuments().exec()
269
+ ]);
270
+
271
+ const totalPages = Math.ceil(count / limit);
272
+ const pagination = {
273
+ currentPage: page,
274
+ totalPages,
275
+ totalItems: count,
276
+ itemsPerPage: limit,
277
+ hasNextPage: page < totalPages,
278
+ hasPrevPage: page > 1
279
+ };
390
280
 
391
- const resultsPromise = resultsQuery.exec();
392
-
393
- return Promise.all([resultsPromise, countPromise])
394
- .then(([result, count]) => {
395
- const totalPages = Math.ceil(count / maxLimit);
396
- const pagination = {
397
- currentPage: page,
398
- totalPages,
399
- totalItems: count,
400
- itemsPerPage: maxLimit,
401
- hasNextPage: page < totalPages,
402
- hasPrevPage: page > 1
403
- };
404
-
405
- if (count > 0) {
406
- return res.status(200).json({
407
- result,
408
- success: true,
409
- pagination,
410
- message: `Found ${result.length} of ${count} documents`
411
- });
412
- } else {
413
- return res.status(200).json({
414
- result: [],
415
- success: true,
416
- pagination,
417
- message: "Collection is empty"
418
- });
419
- }
420
- })
421
- .catch((error) => {
422
- console.error('Error in list method:', error);
423
- return res.status(500).json({
424
- result: null,
425
- success: false,
426
- message: "Error retrieving document list",
427
- error: error.message
428
- });
429
- });
281
+ return res.status(200).json({
282
+ result,
283
+ success: true,
284
+ pagination,
285
+ message: count > 0
286
+ ? `Found ${result.length} of ${count} documents`
287
+ : "Collection is empty"
288
+ });
430
289
  });
431
290
 
432
291
  /**
@@ -434,68 +293,75 @@ exports.list = tryCatch(async (Model, req, res) => {
434
293
  * @param {mongoose.Model} Model - Mongoose model
435
294
  * @param {Object} req - Express request object
436
295
  * @param {Object} res - Express response object
437
- * @param {Object} req.query - Query parameters
438
- * @returns {Array} List of Documents
296
+ * @returns {Promise<void>}
439
297
  */
440
298
  exports.search = tryCatch(async (Model, req, res) => {
441
- console.log('crud serach req:: ', req.query);
442
- const {
443
- keyword,
444
- category,
445
- brand,
446
- minPrice,
447
- maxPrice,
448
- rating,
449
- sortBy = 'popularity',
450
- page = 1,
451
- limit = 24
452
- } = req.query;
453
-
454
- // Build query object
455
- const queryObj = { page, limit };
456
- if (keyword) queryObj.keyword = keyword;
457
- if (category) queryObj.category = category;
458
- if (brand) queryObj.brand = { in: brand }; // Support multiple brands
459
- if (minPrice || maxPrice) {
460
- queryObj.price = {};
461
- if (minPrice) queryObj.price.gte = minPrice;
462
- if (maxPrice) queryObj.price.lte = maxPrice;
463
- }
464
- if (rating) queryObj.rating = { gte: rating };
465
-
466
- const sortOptions = {
467
- popularity: { popularity: -1 },
468
- newest: { createdAt: -1 },
469
- 'price-low': { price: 1 },
470
- 'price-high': { price: -1 },
471
- rating: { rating: -1 }
472
- };
473
-
474
- const Search = new SearchApi(Model, queryObj, {
475
- searchFields: ['_id', 'id', 'price', 'name', 'description', 'tags'],
476
- defaultSort: sortOptions[sortBy] || sortOptions.popularity
477
- });
299
+ console.log('CRUD search query:', req.query);
300
+
301
+ const {
302
+ keyword,
303
+ category,
304
+ brand,
305
+ minPrice,
306
+ maxPrice,
307
+ rating,
308
+ sortBy = 'popularity',
309
+ page = 1,
310
+ limit = 24
311
+ } = req.query;
312
+
313
+ // Build query object
314
+ const queryObj = {
315
+ page: parseInt(page),
316
+ limit: parseInt(limit)
317
+ };
478
318
 
479
- return await Search
480
- .search(['_id', 'id', 'price', 'name', 'description', 'brand', 'tags'])
481
- .filter()
482
- .sort()
483
- .selectFields('-__v,-updatedAt')
484
- .populate('reviews')
485
- .paginate(limit, 50) // Max 50 items per page
486
- .execute()
487
- .then((result) => { return res.status(200).json({ success: true, ...result }); })
488
- .catch((error) => { return res.status(500).json({ success: false, message: error.message }); })
489
- });
319
+ if (keyword) queryObj.keyword = keyword;
320
+ if (category) queryObj.category = category;
321
+ if (brand) queryObj.brand = { in: brand };
322
+
323
+ if (minPrice || maxPrice) {
324
+ queryObj.price = {};
325
+ if (minPrice) queryObj.price.gte = parseFloat(minPrice);
326
+ if (maxPrice) queryObj.price.lte = parseFloat(maxPrice);
327
+ }
328
+
329
+ if (rating) queryObj.rating = { gte: parseFloat(rating) };
330
+
331
+ const sortOptions = {
332
+ popularity: { popularity: -1 },
333
+ newest: { createdAt: -1 },
334
+ 'price-low': { price: 1 },
335
+ 'price-high': { price: -1 },
336
+ rating: { rating: -1 }
337
+ };
490
338
 
339
+ const searchApi = new SearchApi(Model, queryObj, {
340
+ searchFields: ['_id', 'id', 'price', 'name', 'description', 'tags'],
341
+ defaultSort: sortOptions[sortBy] || sortOptions.popularity
342
+ });
343
+
344
+ const result = await searchApi
345
+ .search(['_id', 'id', 'price', 'name', 'description', 'brand', 'tags'])
346
+ .filter()
347
+ .sort()
348
+ .selectFields('-__v,-updatedAt')
349
+ .populate('reviews')
350
+ .paginate(parseInt(limit), 50)
351
+ .execute();
352
+
353
+ return res.status(200).json({
354
+ success: true,
355
+ ...result
356
+ });
357
+ });
491
358
 
492
359
  /**
493
360
  * Create a controller object with all CRUD methods bound to a specific Model
494
361
  * @param {mongoose.Model} Model - Mongoose model
495
362
  * @returns {Object} Controller object with all CRUD methods
496
363
  */
497
- module.exports = ( Model ) => {
498
-
364
+ module.exports = (Model) => {
499
365
  return {
500
366
  all: (req, res) => exports.all(Model, req, res),
501
367
  read: (req, res) => exports.read(Model, req, res),
@@ -503,6 +369,6 @@ module.exports = ( Model ) => {
503
369
  update: (req, res) => exports.update(Model, req, res),
504
370
  delete: (req, res) => exports.delete(Model, req, res),
505
371
  list: (req, res) => exports.list(Model, req, res),
506
- search: (req, res) => exports.search(Model, req, res),
372
+ search: (req, res) => exports.search(Model, req, res)
507
373
  };
508
374
  };
@@ -82,7 +82,7 @@ const uploadImages = async (files, chunkSize = 3) => {
82
82
 
83
83
  return imageLinks;
84
84
  } catch (error) {
85
- throw new Error(`Image upload failed: ${error.message}`);
85
+ throw new Error(`Image upload failed: ${error}`);
86
86
  }
87
87
  };
88
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feardread/fear",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/routes/product.js CHANGED
@@ -14,8 +14,8 @@ module.exports = (fear) => {
14
14
  router.route('/search/all').get(Product.productSearch);
15
15
  router.route("/one").get(handler.async(Product.read));
16
16
  router.route("/:id")
17
- .get(handler.async(Product.read))
18
- .put(handler.async(Product.update))
17
+ .get(Product.read)
18
+ .put(Product.update)
19
19
  .delete(handler.async(Product.delete));
20
20
 
21
21
  return router;