@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/models/order.js CHANGED
@@ -1,109 +1,43 @@
1
1
  const mongoose = require("mongoose");
2
+ const Address = require("./address");
2
3
 
3
4
  const orderItemSchema = new mongoose.Schema({
4
- productId: {
5
- type: mongoose.Schema.Types.ObjectId,
6
- ref: "Product",
7
- required: true,
8
- },
9
- name: {
10
- type: String,
11
- required: true,
12
- },
13
- quantity: {
14
- type: Number,
15
- required: true,
16
- min: 1,
17
- },
18
- price: {
19
- type: Number,
20
- required: true,
21
- },
22
- discount: {
23
- type: Number,
24
- default: 0,
25
- },
26
- tax: {
27
- type: Number,
28
- default: 0,
29
- },
30
- subtotal: {
31
- type: Number,
32
- required: true,
33
- },
5
+ productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product", required: true },
6
+ title: { type: String, required: true, },
7
+ quantity: { type: Number, required: true, min: 1, },
8
+ price: { type: Number, required: true, },
9
+ discount: { type: Number, default: 0, },
10
+ tax: { type: Number, default: 0, },
11
+ subtotal: { type: Number, required: true, },
34
12
  image: String,
35
13
  sku: String,
36
- attributes: {
37
- type: Map,
38
- of: String, // For size, color, etc.
39
- },
14
+ attributes: { type: Map, of: String, },
40
15
  });
41
16
 
42
17
  const orderSchema = new mongoose.Schema(
43
18
  {
44
- orderNumber: {
45
- type: String,
46
- unique: true,
47
- required: true,
48
- },
49
- userId: {
50
- type: mongoose.Schema.Types.ObjectId,
51
- ref: "User",
52
- required: true,
53
- index: true,
54
- },
55
- // Order Items
19
+ orderNumber: { type: String, unique: true, required: true, },
20
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, index: true, },
56
21
  items: [orderItemSchema],
57
-
58
- // Pricing
59
- subtotal: {
60
- type: Number,
61
- required: true,
62
- },
63
- discount: {
64
- type: Number,
65
- default: 0,
66
- },
67
- tax: {
68
- type: Number,
69
- default: 0,
70
- },
71
- shippingCost: {
72
- type: Number,
73
- default: 0,
74
- },
75
- total: {
76
- type: Number,
77
- required: true,
78
- },
79
- currency: {
80
- type: String,
81
- default: "INR",
82
- enum: ["INR", "USD", "EUR", "GBP"],
83
- },
84
-
85
- // Payment Information
86
- paymentMethod: {
87
- type: String,
88
- enum: ["razorpay", "paypal", "card", "upi", "netbanking", "cod"],
89
- required: true,
90
- },
91
- paymentStatus: {
92
- type: String,
93
- enum: ["pending", "processing", "completed", "failed", "refunded", "partially_refunded"],
94
- default: "pending",
95
- index: true,
96
- },
22
+ subtotal: { type: Number, required: true },
23
+ discount: { type: Number, default: 0, },
24
+ tax: { type: Number, default: 0, },
25
+ shippingCost: { type: Number, default: 0, },
26
+ total: { type: Number, required: true, },
27
+ currency: { type: String, default: "INR", enum: ["INR", "USD", "EUR", "GBP"], },
28
+ paymentMethod: { type: String, enum: ["razorpay", "paypal", "card", "upi", "netbanking", "cod", "stripe"], required: true, },
29
+ paymentStatus: { type: String, enum: ["pending", "processing", "completed", "failed", "refunded", "partially_refunded"], default: "pending", index: true, },
97
30
  paymentDetails: {
98
31
  // Razorpay
99
32
  razorpayOrderId: String,
100
33
  razorpayPaymentId: String,
101
34
  razorpaySignature: String,
102
-
103
35
  // PayPal
104
36
  paypalOrderId: String,
105
37
  paypalCaptureId: String,
106
-
38
+ // Stripe
39
+ stripeOrderId: String,
40
+ stripePaymentId: String,
107
41
  // Common
108
42
  transactionId: String,
109
43
  paymentMethodId: {
@@ -112,8 +46,6 @@ const orderSchema = new mongoose.Schema(
112
46
  },
113
47
  paidAt: Date,
114
48
  },
115
-
116
- // Order Status
117
49
  orderStatus: {
118
50
  type: String,
119
51
  enum: [
@@ -130,62 +62,12 @@ const orderSchema = new mongoose.Schema(
130
62
  default: "pending",
131
63
  index: true,
132
64
  },
133
-
134
- // Shipping Information
135
- shippingAddress: {
136
- name: {
137
- type: String,
138
- required: true,
139
- },
140
- phone: {
141
- type: String,
142
- required: true,
143
- },
144
- email: String,
145
- line1: {
146
- type: String,
147
- required: true,
148
- },
149
- line2: String,
150
- city: {
151
- type: String,
152
- required: true,
153
- },
154
- state: {
155
- type: String,
156
- required: true,
157
- },
158
- postalCode: {
159
- type: String,
160
- required: true,
161
- },
162
- country: {
163
- type: String,
164
- required: true,
165
- default: "IN",
166
- },
167
- landmark: String,
168
- },
169
-
170
- billingAddress: {
171
- name: String,
172
- phone: String,
173
- email: String,
174
- line1: String,
175
- line2: String,
176
- city: String,
177
- state: String,
178
- postalCode: String,
179
- country: String,
180
- },
181
-
182
- // Shipping Details
65
+ shippingAddress: Address.schema,
66
+ billingAddress: Address.schema,
183
67
  shippingProvider: String,
184
68
  trackingNumber: String,
185
69
  estimatedDeliveryDate: Date,
186
70
  actualDeliveryDate: Date,
187
-
188
- // Status History
189
71
  statusHistory: [
190
72
  {
191
73
  status: String,
@@ -200,30 +82,21 @@ const orderSchema = new mongoose.Schema(
200
82
  },
201
83
  },
202
84
  ],
203
-
204
- // Discount/Coupon
205
85
  couponCode: String,
206
86
  couponDiscount: {
207
87
  type: Number,
208
88
  default: 0,
209
89
  },
210
-
211
- // Notes
212
90
  customerNote: String,
213
91
  internalNote: String,
214
-
215
- // Cancellation/Return
216
92
  cancellationReason: String,
217
93
  cancelledAt: Date,
218
94
  cancelledBy: {
219
95
  type: mongoose.Schema.Types.ObjectId,
220
96
  ref: "User",
221
97
  },
222
-
223
98
  returnReason: String,
224
99
  returnedAt: Date,
225
-
226
- // Refund Information
227
100
  refundAmount: Number,
228
101
  refundStatus: {
229
102
  type: String,
@@ -232,8 +105,6 @@ const orderSchema = new mongoose.Schema(
232
105
  },
233
106
  refundedAt: Date,
234
107
  refundTransactionId: String,
235
-
236
- // Metadata
237
108
  metadata: {
238
109
  type: Map,
239
110
  of: mongoose.Schema.Types.Mixed,
@@ -252,6 +123,8 @@ orderSchema.index({ paymentStatus: 1 });
252
123
  orderSchema.index({ "paymentDetails.razorpayOrderId": 1 });
253
124
  orderSchema.index({ "paymentDetails.paypalOrderId": 1 });
254
125
  orderSchema.index({ trackingNumber: 1 });
126
+ orderSchema.index({ shippingAddress: 1 });
127
+ orderSchema.index({ billingAddress: 1 });
255
128
 
256
129
  // Pre-save middleware to generate order number
257
130
  orderSchema.pre("save", async function (next) {
@@ -288,7 +161,9 @@ orderSchema.statics.getUserOrders = async function (userId, options = {}) {
288
161
  .limit(limit)
289
162
  .skip(skip)
290
163
  .populate("items.productId", "name images")
291
- .populate("paymentDetails.paymentMethodId");
164
+ .populate("paymentDetails.paymentMethodId")
165
+ .populate("shippingAddress")
166
+ .populate("billingAddress");
292
167
  };
293
168
 
294
169
  // Static method to get order statistics
package/models/payment.js CHANGED
@@ -3,87 +3,28 @@ const crypto = require("crypto");
3
3
 
4
4
  const paymentMethodSchema = new mongoose.Schema(
5
5
  {
6
- userId: {
7
- type: mongoose.Schema.Types.ObjectId,
8
- ref: "User",
9
- required: true,
10
- index: true,
11
- },
12
- paymentType: {
13
- type: String,
14
- enum: ["razorpay", "paypal", "card", "upi", "netbanking"],
15
- required: true,
16
- },
17
- paymentToken: {
18
- type: String,
19
- required: true,
20
- // This stores the tokenized payment method from the payment gateway
21
- },
22
- isDefault: {
23
- type: Boolean,
24
- default: false,
25
- },
26
- isActive: {
27
- type: Boolean,
28
- default: true,
29
- },
30
- // Card/Payment details (only non-sensitive info for display)
6
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, index: true, },
7
+ paymentType: { type: String, enum: ["razorpay", "paypal", "card", "upi", "netbanking", "stripe"], required: true, },
8
+ paymentToken: { type: String, required: true, },
9
+ isDefault: { type: Boolean, default: false, },
10
+ isActive: { type: Boolean, default: true, },
31
11
  cardDetails: {
32
- last4: {
33
- type: String,
34
- maxlength: 4,
35
- },
36
- brand: {
37
- type: String, // Visa, Mastercard, Amex, etc.
38
- },
39
- expiryMonth: {
40
- type: String,
41
- maxlength: 2,
42
- },
43
- expiryYear: {
44
- type: String,
45
- maxlength: 4,
46
- },
47
- cardholderName: {
48
- type: String,
49
- },
50
- fingerprint: {
51
- type: String, // Unique identifier for the card
52
- },
12
+ last4: { type: String, maxlength: 4, },
13
+ brand: { type: String, },
14
+ expiryMonth: { type: String, maxlength: 2, },
15
+ expiryYear: { type: String, maxlength: 4, },
16
+ cardholderName: { type: String, },
17
+ fingerprint: { type: String,}
53
18
  },
54
- // PayPal details
55
- paypalDetails: {
56
- email: {
57
- type: String,
58
- },
59
- payerId: {
60
- type: String,
61
- },
19
+ paypalDetails: {
20
+ email: { type: String, },
21
+ payerId: { type: String, },
62
22
  },
63
- // UPI details
64
23
  upiDetails: {
65
- vpa: {
66
- type: String, // Virtual Payment Address (e.g., user@paytm)
67
- },
68
- },
69
- // Billing address
70
- billingAddress: {
71
- name: String,
72
- line1: String,
73
- line2: String,
74
- city: String,
75
- state: String,
76
- postalCode: String,
77
- country: {
78
- type: String,
79
- default: "IN",
80
- },
81
- },
82
- // Metadata for additional information
83
- metadata: {
84
- type: Map,
85
- of: String,
24
+ vpa: { type: String, },
86
25
  },
26
+ billingAddress: { type: mongoose.Schema.Types.ObjectId, ref: "Address", required: true, index: true, },
27
+ metadata: { type: Map, of: String, },
87
28
  },
88
29
  {
89
30
  timestamps: true,
@@ -193,6 +134,4 @@ paymentMethodSchema.virtual("isExpired").get(function () {
193
134
  paymentMethodSchema.set("toJSON", { virtuals: true });
194
135
  paymentMethodSchema.set("toObject", { virtuals: true });
195
136
 
196
- const PaymentMethod = mongoose.model("PaymentMethod", paymentMethodSchema);
197
-
198
- module.exports = PaymentMethod;
137
+ module.exports = mongoose.model("PaymentMethod", paymentMethodSchema);
package/models/user.js CHANGED
@@ -1,68 +1,135 @@
1
- const mongoose = require("mongoose");
2
- const bcrypt = require("bcrypt");
3
- const crypto = require("crypto");
1
+ const mongoose = require('mongoose');
2
+ const bcrypt = require('bcrypt');
4
3
 
5
4
  const userSchema = new mongoose.Schema({
6
- name: { type: String, required: true },
7
- lastname: { type: String, required: false },
8
- email: { type: String, required: true, unique: true },
9
- username: { type: String, required: false, unique: false },
10
- mobile: { type: String, required: false, unique: true },
11
- password: { type: String, required: true },
12
- avatar: { type: Object, required: false, default: {
5
+ email: { type: String, required: true, unique: true, lowercase: true, trim: true,
6
+ match: [/^\S+@\S+\.\S+$/, 'Please enter a valid email']
7
+ },
8
+ password: { type: String, required: true, minlength: 8, select: false },
9
+ firstName: { type: String, required: true, trim: true },
10
+ lastName: { type: String, required: true, trim: true },
11
+ displayName: { type: String, trim: true },
12
+ avatar: { type: Object, required: false, default: {
13
13
  public_id: '',
14
- secure_url: ''
15
- }},
16
- role: { type: String, default: "customer" },
17
- isBlocked: { type: Boolean, default: false },
18
- address: { type: String },
19
- shipping: { type: String },
20
- wishlist: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
21
- refreshToken: { type: String },
22
- passwordChangedAt: Date,
23
- passwordResetToken: String,
24
- passwordResetExpires: Date
14
+ secure_url: '',
15
+ }, trim: true
16
+ },
17
+ bio: { type: String, maxlength: 500 },
18
+ dateOfBirth: Date,
19
+ mobile: { type: String, trim: true, unique: false, required: false },
20
+ role: { type: String,
21
+ enum: ['user', 'admin', 'moderator'], default: 'user' },
22
+ status: { type: String,
23
+ enum: ['active', 'inactive', 'suspended', 'deleted'], default: 'active' },
24
+ emailVerified: { type: Boolean, default: false },
25
+ emailVerificationToken: String,
26
+ emailVerificationExpires: Date,
27
+ passwordResetToken: String,
28
+ passwordResetExpires: Date,
29
+ twoFactorSecret: { type: String, select: false},
30
+ twoFactorEnabled: { type: Boolean, default: false },
31
+ preferences: {
32
+ language: { type: String, default: 'en' },
33
+ timezone: { type: String, default: 'UTC' },
34
+ notifications: {
35
+ email: { type: Boolean, default: true },
36
+ push: { type: Boolean, default: true },
37
+ sms: { type: Boolean, default: false }
25
38
  },
26
- { timestamps: true }
27
- );
39
+ theme: { type: String, enum: ['light', 'dark', 'auto'], default: 'auto' }
40
+ },
41
+ socialAccounts: [{
42
+ provider: { type: String,
43
+ enum: ['google', 'facebook', 'github', 'apple']},
44
+ providerId: String,
45
+ email: String,
46
+ connectedAt: {
47
+ type: Date,
48
+ default: Date.now
49
+ }
50
+ }],
51
+ lastLoginAt: Date,
52
+ lastLoginIP: String,
53
+ loginAttempts: { type: Number, default: 0 },
54
+ lockUntil: Date
55
+ }, {
56
+ timestamps: true
57
+ });
58
+
59
+ // Indexes
60
+ userSchema.index({ email: 1 });
61
+ userSchema.index({ 'profile.firstName': 1, 'profile.lastName': 1 });
62
+ userSchema.index({ createdAt: 1 });
63
+ userSchema.index({ status: 1, role: 1 });
64
+
65
+ // Virtual for full name
66
+ userSchema.virtual('profile.fullName').get(function() {
67
+ return `${this.profile.firstName} ${this.profile.lastName}`;
68
+ });
28
69
 
29
- // Hooks
30
- userSchema.pre("save", async function (next) {
31
- if (!this.isModified("password")) {
70
+ // Hash password before saving
71
+ userSchema.pre('save', async function(next) {
72
+ if (!this.isModified('password')) return next();
73
+
74
+ try {
75
+ const salt = await bcrypt.genSalt(12);
76
+ this.password = await bcrypt.hash(this.password, salt);
32
77
  next();
78
+ } catch (error) {
79
+ next(error);
33
80
  }
34
- const salt = await bcrypt.genSaltSync(10);
35
- this.password = await bcrypt.hash(this.password, salt);
36
- next();
37
81
  });
38
82
 
39
- // Methods
40
- userSchema.methods.compare = async function (entered) {
41
- return await bcrypt.compare(entered, this.password);
83
+ // Method to compare passwords
84
+ userSchema.methods.comparePassword = async function(candidatePassword) {
85
+ return await bcrypt.compare(candidatePassword, this.password);
42
86
  };
43
87
 
44
- userSchema.methods.token = async function () {
45
- const resettoken = crypto.randomBytes(32).toString("hex");
46
-
47
- this.passwordResetToken = crypto.createHash("sha256").update(resettoken).digest("hex");
48
- this.passwordResetExpires = Date.now() + 30 * 60 * 1000;
49
-
50
- return resettoken;
88
+ // Method to check if account is locked
89
+ userSchema.methods.isLocked = function() {
90
+ return !!(this.lockUntil && this.lockUntil > Date.now());
51
91
  };
52
92
 
53
- //Statics
54
- userSchema.statics.countUsers = function () {
55
- return this.countDocuments({});
93
+ // Method to increment login attempts
94
+ userSchema.methods.incLoginAttempts = async function() {
95
+ if (this.lockUntil && this.lockUntil < Date.now()) {
96
+ return await this.updateOne({
97
+ $set: { loginAttempts: 1 },
98
+ $unset: { lockUntil: 1 }
99
+ });
100
+ }
101
+
102
+ const updates = { $inc: { loginAttempts: 1 } };
103
+ const maxAttempts = 5;
104
+ const lockTime = 2 * 60 * 60 * 1000; // 2 hours
105
+
106
+ if (this.loginAttempts + 1 >= maxAttempts && !this.isLocked()) {
107
+ updates.$set = { lockUntil: Date.now() + lockTime };
108
+ }
109
+
110
+ return await this.updateOne(updates);
56
111
  };
57
112
 
58
- userSchema.statics.findByEmail = async function (email) {
59
- return await this.findOne({ email });
113
+ // Method to reset login attempts
114
+ userSchema.methods.resetLoginAttempts = async function() {
115
+ return await this.updateOne({
116
+ $set: { loginAttempts: 0 },
117
+ $unset: { lockUntil: 1 }
118
+ });
60
119
  };
61
120
 
62
- // Query
63
- userSchema.query.paginate = function ({ page, limit }) {
64
- const skip = limit * (page - 1);
65
- return this.skip(skip).limit(limit);
121
+ // Remove sensitive data from JSON output
122
+ userSchema.methods.toJSON = function() {
123
+ const obj = this.toObject();
124
+ delete obj.password;
125
+ delete obj.twoFactorSecret;
126
+ delete obj.emailVerificationToken;
127
+ delete obj.emailVerificationExpires;
128
+ delete obj.passwordResetToken;
129
+ delete obj.passwordResetExpires;
130
+ delete obj.loginAttempts;
131
+ delete obj.lockUntil;
132
+ return obj;
66
133
  };
67
134
 
68
- module.exports = mongoose.model("User", userSchema);
135
+ module.exports = mongoose.model('User', userSchema);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feardread/fear",
3
- "version": "1.2.1",
3
+ "version": "2.0.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,16 @@
1
+ const Address = require('../controllers/address');
2
+
3
+ module.exports = ( fear ) => {
4
+ const router = fear.createRouter();
5
+ const handler = fear.getHandler();
6
+
7
+ router.get("/all", handler.async(Address.all));
8
+ router.post("/new", handler.async(Address.create));
9
+ router.post("/create", handler.async(Address.create));
10
+ router.route("/:id")
11
+ .get(handler.async(Address.read))
12
+ .put(handler.async(Address.update))
13
+ .delete(handler.async(Address.delete));
14
+
15
+ return router;
16
+ }
package/routes/auth.js CHANGED
@@ -1,13 +1,19 @@
1
1
  const Auth = require("../controllers/auth");
2
+ const passport = require("passport");
2
3
 
3
4
  module.exports = (fear) => {
4
5
  const router = fear.createRouter();
5
6
  const handler = fear.getHandler();
6
7
  const validator = fear.getValidator();
8
+ //const passport = fear.getPassport();
7
9
 
8
- router.post("/login", handler.async(Auth.login))
9
- router.post("/logout", handler.async(Auth.logout))
10
- router.post("/register", handler.async(Auth.register))
10
+ router.post("/login", handler.async(Auth.login));
11
+ router.post("/logout", handler.async(Auth.logout));
12
+ router.post("/register", handler.async(Auth.register));
13
+
14
+ router.post('/google', handler.async(Auth.googleAuth));
15
+ router.post('/google/link',handler.async(Auth.linkGoogleAccount));
16
+ router.delete('/google/unlink',handler.async(Auth.unlinkGoogleAccount));
11
17
 
12
18
  return router;
13
19
  }