@feardread/fear 1.1.5 → 1.1.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 +76 -63
- package/FEARServer.js +5 -6
- package/controllers/address.js +9 -0
- package/controllers/auth/index.js +499 -92
- package/controllers/order.js +0 -1
- package/controllers/payment.js +5 -185
- 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/order.js +29 -154
- package/models/payment.js +18 -79
- package/models/user.js +116 -51
- package/package.json +1 -1
- package/routes/address.js +16 -0
- package/routes/auth.js +6 -0
- package/routes/mail.js +10 -165
- package/routes/order.js +7 -4
- 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/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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
module.exports = PaymentMethod;
|
|
137
|
+
module.exports = mongoose.model("PaymentMethod", paymentMethodSchema);
|
package/models/user.js
CHANGED
|
@@ -1,70 +1,135 @@
|
|
|
1
|
-
const mongoose = require(
|
|
2
|
-
const bcrypt = require(
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
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: {
|
|
15
13
|
public_id: '',
|
|
16
|
-
secure_url: ''
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 }
|
|
27
38
|
},
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
});
|
|
30
69
|
|
|
31
|
-
//
|
|
32
|
-
userSchema.pre(
|
|
33
|
-
if (!this.isModified(
|
|
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);
|
|
34
77
|
next();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
next(error);
|
|
35
80
|
}
|
|
36
|
-
const salt = await bcrypt.genSaltSync(10);
|
|
37
|
-
this.password = await bcrypt.hash(this.password, salt);
|
|
38
|
-
next();
|
|
39
81
|
});
|
|
40
82
|
|
|
41
|
-
//
|
|
42
|
-
userSchema.methods.
|
|
43
|
-
return await bcrypt.compare(
|
|
83
|
+
// Method to compare passwords
|
|
84
|
+
userSchema.methods.comparePassword = async function(candidatePassword) {
|
|
85
|
+
return await bcrypt.compare(candidatePassword, this.password);
|
|
44
86
|
};
|
|
45
87
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.passwordResetToken = crypto.createHash("sha256").update(resettoken).digest("hex");
|
|
50
|
-
this.passwordResetExpires = Date.now() + 30 * 60 * 1000;
|
|
51
|
-
|
|
52
|
-
return resettoken;
|
|
88
|
+
// Method to check if account is locked
|
|
89
|
+
userSchema.methods.isLocked = function() {
|
|
90
|
+
return !!(this.lockUntil && this.lockUntil > Date.now());
|
|
53
91
|
};
|
|
54
92
|
|
|
55
|
-
//
|
|
56
|
-
userSchema.
|
|
57
|
-
|
|
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);
|
|
58
111
|
};
|
|
59
112
|
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
});
|
|
62
119
|
};
|
|
63
120
|
|
|
64
|
-
//
|
|
65
|
-
userSchema.
|
|
66
|
-
const
|
|
67
|
-
|
|
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;
|
|
68
133
|
};
|
|
69
134
|
|
|
70
|
-
module.exports = mongoose.model(
|
|
135
|
+
module.exports = mongoose.model('User', userSchema);
|
package/package.json
CHANGED
|
@@ -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
10
|
router.post("/login", handler.async(Auth.login))
|
|
9
11
|
router.post("/logout", handler.async(Auth.logout))
|
|
10
12
|
router.post("/register", handler.async(Auth.register))
|
|
11
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));
|
|
17
|
+
|
|
12
18
|
return router;
|
|
13
19
|
}
|