@feardread/fear 2.0.6 → 2.0.7

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
@@ -15,6 +15,15 @@ module.exports = FEAR = (() => {
15
15
  const DEFAULT_JSON_LIMIT = '10mb';
16
16
  const DEFAULT_ROUTE_PATH = '/fear/api';
17
17
  const AGENT_ROUTE_PATH = '/fear/api/agent';
18
+ const FEAR_LOGO=`
19
+ _________________________
20
+ | ___| ____| / \ | _ \
21
+ | |_ | _| / _ \ | |_) |
22
+ | _| | |___ / ___ \| _ <
23
+ |_| |_____/_/ \_\_| \_\
24
+ ----The Quieter we become----
25
+ -- the more we are able to hear.--
26
+ `
18
27
 
19
28
  // Constructor function
20
29
  const FEAR = function (config) {
@@ -47,7 +56,6 @@ module.exports = FEAR = (() => {
47
56
  this.setupRoutes();
48
57
  };
49
58
 
50
-
51
59
  FEAR.prototype = {
52
60
  constructor: FEAR,
53
61
  getStripe() {
@@ -114,7 +122,7 @@ module.exports = FEAR = (() => {
114
122
  this.db = require("./libs/db");
115
123
  this.handler = require("./libs/handler");
116
124
  this.validator = require("./libs/validator");
117
- this.logo = this.env.FEAR_LOGO;
125
+ this.logo = FEAR_LOGO;
118
126
  this.origins = this.getAllowedOrigins();
119
127
  },
120
128
 
package/FEARServer.js CHANGED
@@ -278,10 +278,6 @@ const FearServer = (function () {
278
278
  return https.createServer(httpsOptions, this.fear.getApp());
279
279
  },
280
280
 
281
- /**
282
- * Create HTTP to HTTPS redirect server
283
- * @private
284
- */
285
281
  _createHttpRedirectServer(httpsPort) {
286
282
  const redirectApp = express();
287
283
 
@@ -438,7 +434,7 @@ const FearServer = (function () {
438
434
  };
439
435
 
440
436
  this.fear.getApp().use((req, res, next) => {
441
- res.header('Access-Control-Allow-Origin', defaultOptions.origin);
437
+ res.header('Access-Control-Allow-Origin', '*');
442
438
  res.header('Access-Control-Allow-Methods', defaultOptions.methods.join(', '));
443
439
  res.header('Access-Control-Allow-Headers', defaultOptions.allowedHeaders.join(', '));
444
440
 
@@ -34,7 +34,7 @@ const sanitizeUpdateData = (data) => {
34
34
  */
35
35
  const processImages = (images) => {
36
36
  if (!images) return Promise.resolve(null);
37
-
37
+ console.log('processing images = ', images);
38
38
  const imageArray = Array.isArray(images)
39
39
  ? images
40
40
  : images.split(',').map(item => item.trim());
@@ -132,34 +132,34 @@ exports.read = tryCatch((Model, req, res) => {
132
132
  */
133
133
  exports.create = tryCatch((Model, req, res) => {
134
134
  const documentData = { ...req.body };
135
+ const featured = documentData.featuredImage;
136
+ if (featured && documentData.images) documentData.images.push(featured);
137
+ let imagePromise;
138
+
139
+ if (documentData.images && documentData.images.length !== 0) {
140
+ imagePromise = processImages(documentData.images);
141
+ } else {
142
+ imagePromise = Promise.resolve(null);
143
+ }
135
144
 
136
- return processImages(documentData.images)
137
- .then(imageLinks => {
145
+ return imagePromise
146
+ .then((imageLinks) => {
138
147
  if (imageLinks) {
139
148
  documentData.images = imageLinks;
140
149
  }
141
-
142
- console.log('Creating document:', documentData);
143
- const document = new Model(documentData);
144
-
150
+
151
+ const document = new Model(documentData);
145
152
  return document.save();
146
- })
147
- .then(result => {
148
- return res.status(201).json({
149
- result,
150
- success: true,
151
- message: `Document created successfully in ${Model.modelName} collection`
153
+ })
154
+ .then((result) => {
155
+ return res.status(201)
156
+ .json({ result, success: true, message: `Document created successfully in ${Model.modelName} collection`
152
157
  });
153
- })
154
- .catch(error => {
158
+ })
159
+ .catch((error) => {
155
160
  console.error('Error creating document:', error);
156
- return res.status(500).json({
157
- result: null,
158
- success: false,
159
- message: "Error creating document",
160
- error: error.message
161
- });
162
- });
161
+ return res.status(500).json({ result: null, success: false, message: "Error creating document", error: error.message });
162
+ })
163
163
  });
164
164
 
165
165
  /**
@@ -26,21 +26,21 @@ const convertToBase64 = (file) => {
26
26
  * @param {string} file - Base64 encoded file or file path
27
27
  * @returns {Promise<Object>} Avatar object with public_id and url
28
28
  */
29
- const uploadAvatar = async (file) => {
30
- try {
31
- const result = await cloudinary.uploader.upload(file, {
32
- folder: "avatar",
33
- width: 150,
34
- crop: "scale",
29
+ const uploadAvatar = (file) => {
30
+ return cloudinary.uploader.upload(file, {
31
+ folder: "avatar",
32
+ width: 150,
33
+ crop: "scale",
34
+ })
35
+ .then((result) => {
36
+ return {
37
+ public_id: result.public_id,
38
+ url: result.secure_url,
39
+ };
40
+ })
41
+ .catch((error) => {
42
+ throw new Error(`Avatar upload failed: ${error.message}`);
35
43
  });
36
-
37
- return {
38
- public_id: result.public_id,
39
- url: result.secure_url,
40
- };
41
- } catch (error) {
42
- throw new Error(`Avatar upload failed: ${error.message}`);
43
- }
44
44
  };
45
45
 
46
46
  /**
@@ -49,41 +49,49 @@ const uploadAvatar = async (file) => {
49
49
  * @param {number} chunkSize - Number of files to upload concurrently
50
50
  * @returns {Promise<Object[]>} Array of image objects with public_id and url
51
51
  */
52
- const uploadImages = async (files, chunkSize = 3) => {
53
- try {
54
- // Normalize input to array
55
- const imageArray = Array.isArray(files) ? [...files] : [files];
56
-
57
- if (imageArray.length === 0) {
58
- return [];
59
- }
52
+ const uploadImages = (files, chunkSize = 3) => {
53
+ // Normalize input to array
54
+ const imageArray = Array.isArray(files) ? [...files] : [files];
55
+
56
+ if (imageArray.length === 0) {
57
+ return Promise.resolve([]);
58
+ }
60
59
 
61
- const imageLinks = [];
60
+ const imageLinks = [];
61
+
62
+ // Create a promise chain for sequential chunk processing
63
+ let promiseChain = Promise.resolve();
64
+
65
+ // Process images in chunks to avoid overwhelming the API
66
+ for (let i = 0; i < imageArray.length; i += chunkSize) {
67
+ const chunk = imageArray.slice(i, i + chunkSize);
62
68
 
63
- // Process images in chunks to avoid overwhelming the API
64
- for (let i = 0; i < imageArray.length; i += chunkSize) {
65
- const chunk = imageArray.slice(i, i + chunkSize);
66
-
69
+ promiseChain = promiseChain.then(() => {
67
70
  const uploadPromises = chunk.map((image) =>
68
71
  cloudinary.uploader.upload(image, {
69
72
  folder: "products",
70
73
  })
71
74
  );
72
75
 
73
- const results = await Promise.all(uploadPromises);
74
-
75
- const chunkResults = results.map((result) => ({
76
- product_id: result.public_id,
77
- url: result.secure_url,
78
- }));
79
-
80
- imageLinks.push(...chunkResults);
81
- }
82
-
83
- return imageLinks;
84
- } catch (error) {
85
- throw new Error(`Image upload failed: ${error}`);
76
+ return Promise.all(uploadPromises)
77
+ .then((results) => {
78
+ const chunkResults = results.map((result) => ({
79
+ public_id: result.public_id,
80
+ url: result.secure_url,
81
+ }));
82
+
83
+ imageLinks.push(...chunkResults);
84
+ });
85
+ });
86
86
  }
87
+
88
+ return promiseChain
89
+ .then(() => {
90
+ return imageLinks;
91
+ })
92
+ .catch((error) => {
93
+ throw new Error('Image upload failed: ', error);
94
+ });
87
95
  };
88
96
 
89
97
  /**
@@ -130,56 +138,55 @@ const uploadPhoto = multer({
130
138
  * @param {Object} res - Express response object
131
139
  * @param {Function} next - Express next function
132
140
  */
133
- const resizeImages = async (req, res, next) => {
134
- try {
135
- // Skip if no files uploaded or no directory specified
136
- if (!req.files || !req.directory) {
137
- return next();
138
- }
139
-
140
- // Ensure directory exists
141
- const targetDir = path.join("public/images", req.directory);
142
- if (!fs.existsSync(targetDir)) {
143
- fs.mkdirSync(targetDir, { recursive: true });
144
- }
145
-
146
- // Process all files concurrently
147
- await Promise.all(
148
- req.files.map(async (file) => {
149
- const originalPath = file.path;
150
- const targetPath = path.join(targetDir, file.filename);
151
-
152
- try {
153
- // Resize and convert image
154
- await sharp(originalPath)
155
- .resize(300, 300, {
156
- fit: 'cover',
157
- position: 'center'
158
- })
159
- .jpeg({ quality: 90 })
160
- .toFile(targetPath);
161
-
162
- // Clean up original file
163
- if (fs.existsSync(originalPath)) {
164
- fs.unlinkSync(originalPath);
165
- }
166
- } catch (imageError) {
167
- console.error(`Failed to process image ${file.filename}:`, imageError);
168
- // Clean up files on error
169
- [originalPath, targetPath].forEach(filePath => {
170
- if (fs.existsSync(filePath)) {
171
- fs.unlinkSync(filePath);
172
- }
173
- });
174
- throw imageError;
141
+ const resizeImages = (req, res, next) => {
142
+ // Skip if no files uploaded or no directory specified
143
+ if (!req.files || !req.directory) {
144
+ return next();
145
+ }
146
+
147
+ // Ensure directory exists
148
+ const targetDir = path.join("public/images", req.directory);
149
+ if (!fs.existsSync(targetDir)) {
150
+ fs.mkdirSync(targetDir, { recursive: true });
151
+ }
152
+
153
+ // Process all files concurrently
154
+ const processPromises = req.files.map((file) => {
155
+ const originalPath = file.path;
156
+ const targetPath = path.join(targetDir, file.filename);
157
+
158
+ return sharp(originalPath)
159
+ .resize(300, 300, {
160
+ fit: 'cover',
161
+ position: 'center'
162
+ })
163
+ .jpeg({ quality: 90 })
164
+ .toFile(targetPath)
165
+ .then(() => {
166
+ // Clean up original file
167
+ if (fs.existsSync(originalPath)) {
168
+ fs.unlinkSync(originalPath);
175
169
  }
176
170
  })
177
- );
171
+ .catch((imageError) => {
172
+ console.error(`Failed to process image ${file.filename}:`, imageError);
173
+ // Clean up files on error
174
+ [originalPath, targetPath].forEach(filePath => {
175
+ if (fs.existsSync(filePath)) {
176
+ fs.unlinkSync(filePath);
177
+ }
178
+ });
179
+ throw imageError;
180
+ });
181
+ });
178
182
 
179
- next();
180
- } catch (error) {
181
- next(new Error(`Image resize failed: ${error.message}`));
182
- }
183
+ Promise.all(processPromises)
184
+ .then(() => {
185
+ next();
186
+ })
187
+ .catch((error) => {
188
+ next(new Error(`Image resize failed: ${error.message}`));
189
+ });
183
190
  };
184
191
 
185
192
  /**
@@ -187,13 +194,14 @@ const resizeImages = async (req, res, next) => {
187
194
  * @param {string} publicId - Public ID of the image to delete
188
195
  * @returns {Promise<Object>} Deletion result
189
196
  */
190
- const deleteImage = async (publicId) => {
191
- try {
192
- const result = await cloudinary.uploader.destroy(publicId);
193
- return result;
194
- } catch (error) {
195
- throw new Error(`Image deletion failed: ${error.message}`);
196
- }
197
+ const deleteImage = (publicId) => {
198
+ return cloudinary.uploader.destroy(publicId)
199
+ .then((result) => {
200
+ return result;
201
+ })
202
+ .catch((error) => {
203
+ throw new Error(`Image deletion failed: ${error.message}`);
204
+ });
197
205
  };
198
206
 
199
207
  /**
@@ -67,7 +67,7 @@ try {
67
67
 
68
68
  greenlock.ready((manager) => {
69
69
  console.log('manager = ', manager);
70
- manager.defaults({
70
+ manager.serveApp({
71
71
  agreeToTerms: true,
72
72
  subscriberEmail: email
73
73
  }).then(() => {
package/models/blog.js CHANGED
@@ -3,13 +3,9 @@ const slugify = require("slugify");
3
3
 
4
4
  const blogSchema = new mongoose.Schema(
5
5
  {
6
- // Basic Information
7
- title: {
8
- type: String,
6
+ title: {type: String, trim: true, index: true,
9
7
  required: [true, "Blog title is required"],
10
- trim: true,
11
- maxlength: [200, "Title cannot exceed 200 characters"],
12
- index: true
8
+ maxlength: [300, "Title cannot exceed 200 characters"],
13
9
  },
14
10
 
15
11
  slug: {
@@ -22,13 +18,13 @@ const blogSchema = new mongoose.Schema(
22
18
  subtitle: {
23
19
  type: String,
24
20
  trim: true,
25
- maxlength: [250, "Subtitle cannot exceed 250 characters"]
21
+ maxlength: [350, "Subtitle cannot exceed 250 characters"]
26
22
  },
27
23
 
28
24
  excerpt: {
29
25
  type: String,
30
26
  trim: true,
31
- maxlength: [500, "Excerpt cannot exceed 500 characters"]
27
+ maxlength: [600, "Excerpt cannot exceed 500 characters"]
32
28
  },
33
29
 
34
30
  content: {
@@ -43,10 +39,6 @@ const blogSchema = new mongoose.Schema(
43
39
  images: [{
44
40
  public_id: { type: String },
45
41
  url: { type: String },
46
- secure_url: { type: String },
47
- alt: { type: String },
48
- caption: { type: String },
49
- order: { type: Number, default: 0 }
50
42
  }],
51
43
 
52
44
  video: {
@@ -285,11 +277,11 @@ const blogSchema = new mongoose.Schema(
285
277
  seo: {
286
278
  metaTitle: {
287
279
  type: String,
288
- maxlength: [70, "Meta title cannot exceed 70 characters"]
280
+ maxlength: [170, "Meta title cannot exceed 170 characters"]
289
281
  },
290
282
  metaDescription: {
291
283
  type: String,
292
- maxlength: [160, "Meta description cannot exceed 160 characters"]
284
+ maxlength: [260, "Meta description cannot exceed 260 characters"]
293
285
  },
294
286
  metaKeywords: [{ type: String }],
295
287
  focusKeyword: { type: String },
package/models/brand.js CHANGED
@@ -43,7 +43,6 @@ brandSchema.index({ createdAt: -1 });
43
43
  // Text index for search
44
44
  brandSchema.index({
45
45
  name: "text",
46
- title: "text",
47
46
  tags: "text"
48
47
  });
49
48
 
@@ -83,11 +82,6 @@ brandSchema.pre("save", async function(next) {
83
82
  this.active = this.isActive;
84
83
  }
85
84
 
86
- // Set title to name if not provided
87
- if (!this.title) {
88
- this.title = this.name;
89
- }
90
-
91
85
  next();
92
86
  });
93
87
 
package/models/product.js CHANGED
@@ -19,7 +19,12 @@ const productSchema = new mongoose.Schema({
19
19
  url: String
20
20
  },
21
21
  ],
22
- tags: String,
22
+ tags: [{
23
+ type: String,
24
+ trim: true,
25
+ lowercase: true,
26
+ index: true
27
+ }],
23
28
  ratings: [{
24
29
  star: Number,
25
30
  comment: String,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feardread/fear",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {