flykup_model_production 1.0.0
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/auth.js +14 -0
- package/config.js +1 -0
- package/db_connection.js +23 -0
- package/index.js +127 -0
- package/info.txt +1 -0
- package/models/AdminEmail.model.js +36 -0
- package/models/LiveStreamInteraction.model.js +93 -0
- package/models/ProductInteraction.model.js +42 -0
- package/models/Review.model.js +121 -0
- package/models/SearchAnalytics.js +22 -0
- package/models/ShoppableInteraction.model.js +89 -0
- package/models/Wishlist.model.js +30 -0
- package/models/admin.model.js +27 -0
- package/models/appUpdate.model.js +18 -0
- package/models/assets.model.js +32 -0
- package/models/blockedRegion.models.js +27 -0
- package/models/category.model.js +29 -0
- package/models/chat.model.js +496 -0
- package/models/coHostInvitation.model.js +56 -0
- package/models/follow.model.js +37 -0
- package/models/loginlogs.model.js +25 -0
- package/models/notification.model.js +116 -0
- package/models/order.modal.js +285 -0
- package/models/productListing.model.js +132 -0
- package/models/profileInteractions.model.js +45 -0
- package/models/registerShow.model.js +28 -0
- package/models/seller.model.js +299 -0
- package/models/settings.model.js +18 -0
- package/models/shipper.model.js +128 -0
- package/models/shoppableVideo.model.js +177 -0
- package/models/shoppableVideoComment.model.js +58 -0
- package/models/shoppableVideoLike.model.js +30 -0
- package/models/shoppableVideoSave.model.js +28 -0
- package/models/shows.model.js +315 -0
- package/models/stock.model.js +128 -0
- package/models/ticket.model.js +115 -0
- package/models/user.model.js +229 -0
- package/package.json +18 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import mongoose from "mongoose";
|
|
4
|
+
const { Schema } = mongoose;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const dropshipperConnectionSchema = new Schema({
|
|
8
|
+
_id: false,
|
|
9
|
+
dropshipperId: {
|
|
10
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
11
|
+
ref: "dropshippers",
|
|
12
|
+
required: true,
|
|
13
|
+
},
|
|
14
|
+
status: {
|
|
15
|
+
type: String,
|
|
16
|
+
enum: [
|
|
17
|
+
"pending",
|
|
18
|
+
"approved",
|
|
19
|
+
"rejected",
|
|
20
|
+
"revoked_by_seller",
|
|
21
|
+
"revoked_by_dropshipper",
|
|
22
|
+
],
|
|
23
|
+
required: true,
|
|
24
|
+
default: "pending",
|
|
25
|
+
},
|
|
26
|
+
commissionRate: {
|
|
27
|
+
type: Number, // Store as percentage, e.g., 15 for 15%
|
|
28
|
+
min: 0,
|
|
29
|
+
max: 100,
|
|
30
|
+
default: null, // Or a platform default? Needs discussion
|
|
31
|
+
},
|
|
32
|
+
agreementDetails: {
|
|
33
|
+
type: String,
|
|
34
|
+
maxLength: 500,
|
|
35
|
+
default: null,
|
|
36
|
+
},
|
|
37
|
+
requestedAt: {
|
|
38
|
+
type: Date,
|
|
39
|
+
default: Date.now,
|
|
40
|
+
},
|
|
41
|
+
respondedAt: {
|
|
42
|
+
type: Date,
|
|
43
|
+
default: null,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const RedFlagsSchema = new mongoose.Schema({
|
|
48
|
+
type: { type: String, required: true },
|
|
49
|
+
description: { type: String, required: true },
|
|
50
|
+
severity: { type: String, enum: ['low', 'medium', 'high'], required: true }
|
|
51
|
+
}, { _id: false })
|
|
52
|
+
|
|
53
|
+
const ScoringSchema = new mongoose.Schema({
|
|
54
|
+
totalScore: { type: Number, default: 0, min: 0 },
|
|
55
|
+
breakdown: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
56
|
+
redFlags: [RedFlagsSchema],
|
|
57
|
+
manualChecks: [String],
|
|
58
|
+
recommendation: { type: String, enum: ['auto_approved', 'auto_rejected', 'manual_review'] },
|
|
59
|
+
rejectedReason: { type: String, default: null }
|
|
60
|
+
}, { _id: false })
|
|
61
|
+
|
|
62
|
+
const SellerSchema = new mongoose.Schema(
|
|
63
|
+
{
|
|
64
|
+
userInfo: {
|
|
65
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
66
|
+
ref: "users",
|
|
67
|
+
required: true,
|
|
68
|
+
},
|
|
69
|
+
dropshipperConnections: [
|
|
70
|
+
dropshipperConnectionSchema
|
|
71
|
+
],
|
|
72
|
+
isAdult: {
|
|
73
|
+
type: Boolean,
|
|
74
|
+
// required: function (){
|
|
75
|
+
// return this.sellerType === 'social'
|
|
76
|
+
// }
|
|
77
|
+
},
|
|
78
|
+
companyName: {
|
|
79
|
+
type: String,
|
|
80
|
+
maxLength: 50,
|
|
81
|
+
},
|
|
82
|
+
mobileNumber: {
|
|
83
|
+
type: String,
|
|
84
|
+
maxLength: 15,
|
|
85
|
+
},
|
|
86
|
+
email: {
|
|
87
|
+
type: String,
|
|
88
|
+
trim: true,
|
|
89
|
+
// required: true,
|
|
90
|
+
maxLength: 60,
|
|
91
|
+
},
|
|
92
|
+
sellerType: {
|
|
93
|
+
type: String,
|
|
94
|
+
enum: ["brand", "social"],
|
|
95
|
+
// required: true,
|
|
96
|
+
},
|
|
97
|
+
businessType: {
|
|
98
|
+
type: String,
|
|
99
|
+
enum: [
|
|
100
|
+
"Individual",
|
|
101
|
+
"Sole Proprietor",
|
|
102
|
+
"Private Limited",
|
|
103
|
+
"LLP",
|
|
104
|
+
"Partnership",
|
|
105
|
+
],
|
|
106
|
+
// required: true,
|
|
107
|
+
},
|
|
108
|
+
productCategories: [
|
|
109
|
+
{
|
|
110
|
+
type: String,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
fssaiCertificateFileName: {
|
|
114
|
+
type: String
|
|
115
|
+
},
|
|
116
|
+
productCatalog: {
|
|
117
|
+
link: { type: String },
|
|
118
|
+
file: { type: String }, // for old sellers
|
|
119
|
+
files: [{ type: String }] // for new sellers
|
|
120
|
+
},
|
|
121
|
+
wantToSell: [
|
|
122
|
+
{ type: String }
|
|
123
|
+
],
|
|
124
|
+
sellerExperienceInfo: {
|
|
125
|
+
online: [
|
|
126
|
+
{
|
|
127
|
+
platform: { type: String },
|
|
128
|
+
profile: { type: String },
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
offline: [{ type: String }],
|
|
132
|
+
experience: {
|
|
133
|
+
type: String,
|
|
134
|
+
trim: true,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
gstInfo: {
|
|
138
|
+
hasGST: {
|
|
139
|
+
type: Boolean,
|
|
140
|
+
default: null,
|
|
141
|
+
},
|
|
142
|
+
gstDeclaration: {
|
|
143
|
+
type: String,
|
|
144
|
+
default: null,
|
|
145
|
+
},
|
|
146
|
+
gstNumber: {
|
|
147
|
+
type: String,
|
|
148
|
+
trim: true,
|
|
149
|
+
maxLength: 25,
|
|
150
|
+
default: null,
|
|
151
|
+
},
|
|
152
|
+
gstDocument: {
|
|
153
|
+
type: String,
|
|
154
|
+
trim: true,
|
|
155
|
+
default: null,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
aadhaarInfo: {
|
|
159
|
+
aadhaarNumber: {
|
|
160
|
+
type: String,
|
|
161
|
+
// required: true,
|
|
162
|
+
trim: true,
|
|
163
|
+
maxLength: 12,
|
|
164
|
+
},
|
|
165
|
+
aadhaarFront: {
|
|
166
|
+
type: String,
|
|
167
|
+
default: null,
|
|
168
|
+
trim: true,
|
|
169
|
+
},
|
|
170
|
+
aadhaarBack: {
|
|
171
|
+
type: String,
|
|
172
|
+
default: null,
|
|
173
|
+
trim: true,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
panInfo: {
|
|
177
|
+
panNumber: {
|
|
178
|
+
type: String,
|
|
179
|
+
// required: true,
|
|
180
|
+
trim: true,
|
|
181
|
+
},
|
|
182
|
+
panFront: {
|
|
183
|
+
type: String,
|
|
184
|
+
default: null,
|
|
185
|
+
trim: true,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
shippingInfo: {
|
|
189
|
+
preferredShipping: {
|
|
190
|
+
type: String,
|
|
191
|
+
},
|
|
192
|
+
dispatchTime: {
|
|
193
|
+
type: String,
|
|
194
|
+
},
|
|
195
|
+
returnPolicy: {
|
|
196
|
+
type: String,
|
|
197
|
+
},
|
|
198
|
+
courierPartner: {
|
|
199
|
+
type: String,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
readiness: {
|
|
203
|
+
liveSellingFrequency: {
|
|
204
|
+
type: String,
|
|
205
|
+
},
|
|
206
|
+
cameraSetup: {
|
|
207
|
+
type: Boolean,
|
|
208
|
+
},
|
|
209
|
+
isWillingToGoLive: {
|
|
210
|
+
type: Boolean
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
promotions: {
|
|
214
|
+
promoteLiveSelling: {
|
|
215
|
+
type: Boolean,
|
|
216
|
+
default: false
|
|
217
|
+
},
|
|
218
|
+
brandPromotion: {
|
|
219
|
+
type: Boolean,
|
|
220
|
+
default: false
|
|
221
|
+
},
|
|
222
|
+
flykupCollab: {
|
|
223
|
+
type: Boolean,
|
|
224
|
+
default: false
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
address: {
|
|
228
|
+
addressLine1: {
|
|
229
|
+
type: String,
|
|
230
|
+
// required: true,
|
|
231
|
+
trim: true,
|
|
232
|
+
maxLength: 150,
|
|
233
|
+
},
|
|
234
|
+
addressLine2: {
|
|
235
|
+
type: String,
|
|
236
|
+
trim: true,
|
|
237
|
+
maxLength: 150,
|
|
238
|
+
},
|
|
239
|
+
city: {
|
|
240
|
+
type: String,
|
|
241
|
+
// required: true,
|
|
242
|
+
trim: true,
|
|
243
|
+
maxLength: 50,
|
|
244
|
+
},
|
|
245
|
+
state: {
|
|
246
|
+
type: String,
|
|
247
|
+
// required: true,
|
|
248
|
+
trim: true,
|
|
249
|
+
maxLength: 50,
|
|
250
|
+
},
|
|
251
|
+
pincode: {
|
|
252
|
+
type: String,
|
|
253
|
+
// required: true,
|
|
254
|
+
trim: true,
|
|
255
|
+
maxLength: 6,
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
// Terms Acceptance
|
|
259
|
+
generalAccepted: { type: Boolean, default: false },
|
|
260
|
+
sellerAccepted: { type: Boolean, default: false },
|
|
261
|
+
digitalAccepted: { type: Boolean, default: false },
|
|
262
|
+
approvalStatus: {
|
|
263
|
+
type: String,
|
|
264
|
+
enum: [
|
|
265
|
+
"pending",
|
|
266
|
+
"auto_approved",
|
|
267
|
+
"auto_rejected",
|
|
268
|
+
"manual_review",
|
|
269
|
+
"approved",
|
|
270
|
+
"rejected",
|
|
271
|
+
],
|
|
272
|
+
default: "pending",
|
|
273
|
+
},
|
|
274
|
+
rejectedReason: {
|
|
275
|
+
type: String,
|
|
276
|
+
default: null,
|
|
277
|
+
},
|
|
278
|
+
rejectedTimes: { type: Number, default: 0 },
|
|
279
|
+
azureMigrationStatus: {
|
|
280
|
+
type: String,
|
|
281
|
+
enum: ['pending', 'completed', 'failed'],
|
|
282
|
+
default: 'pending',
|
|
283
|
+
},
|
|
284
|
+
scoring: { type: ScoringSchema, default: {} },
|
|
285
|
+
chatSupport: {
|
|
286
|
+
isAvailable: { type: Boolean, default: true },
|
|
287
|
+
responseTime: { type: String, default: 'within 24 hours' },
|
|
288
|
+
supportHours: {
|
|
289
|
+
start: { type: String, default: '09:00' },
|
|
290
|
+
end: { type: String, default: '18:00' }
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
{ timestamps: true, strict: true }
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const Seller = mongoose.model("sellers", SellerSchema);
|
|
298
|
+
|
|
299
|
+
export default Seller;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const SettingsSchema = new mongoose.Schema({
|
|
4
|
+
appSettings: {
|
|
5
|
+
sellerAppVersion: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: '0.0.1'
|
|
8
|
+
},
|
|
9
|
+
userAppVersion: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: '0.0.1'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const Settings = mongoose.model('Settings', SettingsSchema);
|
|
17
|
+
|
|
18
|
+
export default Settings;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
const { Schema } = mongoose;
|
|
3
|
+
|
|
4
|
+
const dropshipperConnectionSchema = new Schema({
|
|
5
|
+
_id: false,
|
|
6
|
+
sellerId: {
|
|
7
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
8
|
+
ref: "sellers",
|
|
9
|
+
required: true,
|
|
10
|
+
},
|
|
11
|
+
status: {
|
|
12
|
+
type: String,
|
|
13
|
+
enum: [
|
|
14
|
+
"pending",
|
|
15
|
+
"approved",
|
|
16
|
+
"rejected",
|
|
17
|
+
"revoked_by_seller",
|
|
18
|
+
"revoked_by_dropshipper",
|
|
19
|
+
],
|
|
20
|
+
required: true,
|
|
21
|
+
default: "pending",
|
|
22
|
+
},
|
|
23
|
+
commissionRate: {
|
|
24
|
+
type: Number, // Store as percentage, e.g., 15 for 15%
|
|
25
|
+
min: 0,
|
|
26
|
+
max: 100,
|
|
27
|
+
default: null, // Or a platform default? Needs discussion
|
|
28
|
+
},
|
|
29
|
+
agreementDetails: {
|
|
30
|
+
type: String,
|
|
31
|
+
maxLength: 500,
|
|
32
|
+
default: null,
|
|
33
|
+
},
|
|
34
|
+
requestedAt: {
|
|
35
|
+
type: Date,
|
|
36
|
+
default: Date.now,
|
|
37
|
+
},
|
|
38
|
+
respondedAt: {
|
|
39
|
+
type: Date,
|
|
40
|
+
default: null,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const DropshipperSchema = new Schema(
|
|
45
|
+
{
|
|
46
|
+
userInfo: {
|
|
47
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
48
|
+
ref: "users",
|
|
49
|
+
required: true,
|
|
50
|
+
unique: true, // Ensure one user is only one dropshipper
|
|
51
|
+
index: true,
|
|
52
|
+
},
|
|
53
|
+
// Dropshipper specific info (can mirror Seller or be simpler)
|
|
54
|
+
businessName: {
|
|
55
|
+
// Optional: If they operate under a different name
|
|
56
|
+
type: String,
|
|
57
|
+
maxLength: 60,
|
|
58
|
+
trim: true,
|
|
59
|
+
},
|
|
60
|
+
mobileNumber: {
|
|
61
|
+
// May differ from user's primary mobile
|
|
62
|
+
type: String,
|
|
63
|
+
maxLength: 15,
|
|
64
|
+
trim: true,
|
|
65
|
+
},
|
|
66
|
+
email: {
|
|
67
|
+
// May differ from user's primary email
|
|
68
|
+
type: String,
|
|
69
|
+
trim: true,
|
|
70
|
+
maxLength: 60,
|
|
71
|
+
},
|
|
72
|
+
// Connections managed by the Dropshipper
|
|
73
|
+
connectedSellers: [dropshipperConnectionSchema],
|
|
74
|
+
|
|
75
|
+
// Address might be needed for correspondence or legal reasons
|
|
76
|
+
address: {
|
|
77
|
+
addressLine1: { type: String, trim: true, maxLength: 150 },
|
|
78
|
+
addressLine2: { type: String, trim: true, maxLength: 150 },
|
|
79
|
+
city: { type: String, trim: true, maxLength: 50 },
|
|
80
|
+
state: { type: String, trim: true, maxLength: 50 },
|
|
81
|
+
pincode: { type: String, trim: true, maxLength: 6 },
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Payout information (CRUCIAL for dropshippers)
|
|
85
|
+
bankDetails: {
|
|
86
|
+
accountHolderName: { type: String, trim: true },
|
|
87
|
+
accountNumber: { type: String, trim: true },
|
|
88
|
+
ifscCode: { type: String, trim: true },
|
|
89
|
+
bankName: { type: String, trim: true },
|
|
90
|
+
// Add UPI details if needed
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Admin approval status for the dropshipper account itself
|
|
94
|
+
approvalStatus: {
|
|
95
|
+
type: String,
|
|
96
|
+
enum: ["pending", "approved", "rejected", "suspended"],
|
|
97
|
+
default: "pending",
|
|
98
|
+
},
|
|
99
|
+
rejectedReason: {
|
|
100
|
+
type: String,
|
|
101
|
+
maxLength: 200,
|
|
102
|
+
default: null,
|
|
103
|
+
},
|
|
104
|
+
// Add other relevant fields like readiness for live selling etc.
|
|
105
|
+
// e.g., experience, social media links if relevant
|
|
106
|
+
},
|
|
107
|
+
{ timestamps: true, strict: true } // Use strict: true to prevent undefined fields
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Pre-hook (Example): Ensure user role is updated when dropshipper is created/approved
|
|
111
|
+
// This logic might be better placed in the controller after successful creation/approval
|
|
112
|
+
// DropshipperSchema.pre('save', async function (next) {
|
|
113
|
+
// if (this.isNew || this.isModified('approvalStatus')) {
|
|
114
|
+
// try {
|
|
115
|
+
// const User = mongoose.model('users'); // Avoid circular dependency if possible
|
|
116
|
+
// await User.findByIdAndUpdate(this.userInfo, {
|
|
117
|
+
// $set: { role: this.approvalStatus === 'approved' ? 'dropshipper' : 'user' } // Adjust logic as needed
|
|
118
|
+
// });
|
|
119
|
+
// } catch (error) {
|
|
120
|
+
// return next(error);
|
|
121
|
+
// }
|
|
122
|
+
// }
|
|
123
|
+
// next();
|
|
124
|
+
// });
|
|
125
|
+
|
|
126
|
+
const Dropshipper = mongoose.model("dropshippers", DropshipperSchema);
|
|
127
|
+
|
|
128
|
+
export default Dropshipper;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { Schema, model } from "mongoose";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
// Define the Rendition sub-schema
|
|
8
|
+
const RenditionSchema = new Schema({
|
|
9
|
+
resolution: { type: String, required: false }, // e.g. "1080p"
|
|
10
|
+
bitrate: { type: Number, required: false }, // e.g. 3000000 (bps)
|
|
11
|
+
playlistKey: { type: String, required: false }, // e.g. "video123/1080p.m3u8"
|
|
12
|
+
hlsUrl: { type: String, required: false }, // Full HLS URL for this rendition
|
|
13
|
+
size: { type: Number, required: false }, // bytes of all .ts + playlist
|
|
14
|
+
ratio: { type: Number, required: false }, // size/originalSize
|
|
15
|
+
}, { _id: false }); // No separate _id for subdocuments unless needed
|
|
16
|
+
|
|
17
|
+
const ShoppableVideoSchema = new Schema(
|
|
18
|
+
{
|
|
19
|
+
host: {
|
|
20
|
+
type: Schema.Types.ObjectId,
|
|
21
|
+
required: true,
|
|
22
|
+
refPath: 'hostModel'
|
|
23
|
+
},
|
|
24
|
+
hostModel: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: true,
|
|
27
|
+
enum: ['sellers', 'dropshippers'],
|
|
28
|
+
index: true
|
|
29
|
+
},
|
|
30
|
+
title: {
|
|
31
|
+
type: String,
|
|
32
|
+
trim: true,
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
description: {
|
|
36
|
+
type: String,
|
|
37
|
+
required: true,
|
|
38
|
+
trim: true,
|
|
39
|
+
},
|
|
40
|
+
category: { type: String, required: true, trim: true },
|
|
41
|
+
subcategory: { type: String, required: true, trim: true },
|
|
42
|
+
// thumbnailURL might be set initially or updated by the processing service
|
|
43
|
+
thumbnailURL: {
|
|
44
|
+
type: String,
|
|
45
|
+
},
|
|
46
|
+
isProductsAvailable : { type : Boolean, required : true, default : true },
|
|
47
|
+
productsListed: {
|
|
48
|
+
type: [
|
|
49
|
+
{
|
|
50
|
+
type: Schema.Types.ObjectId,
|
|
51
|
+
ref: "productlistings",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
default: [],
|
|
55
|
+
},
|
|
56
|
+
hashTags: {
|
|
57
|
+
type: [
|
|
58
|
+
{
|
|
59
|
+
type: String,
|
|
60
|
+
trim: true
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
processingStatus: {
|
|
65
|
+
type: String,
|
|
66
|
+
enum: ["queued", "processing", "published", "failed", "transcoding_complete","uploaded"], // Added new status
|
|
67
|
+
default: "uploaded",
|
|
68
|
+
index: true,
|
|
69
|
+
},
|
|
70
|
+
visibility: {
|
|
71
|
+
type: String,
|
|
72
|
+
enum: ["public", "private", "deleted"],
|
|
73
|
+
default: "public",
|
|
74
|
+
index: true,
|
|
75
|
+
},
|
|
76
|
+
thumbnailBlobName: {
|
|
77
|
+
type: String,
|
|
78
|
+
default: null,
|
|
79
|
+
},
|
|
80
|
+
originalVideoBlobName: {
|
|
81
|
+
type: String,
|
|
82
|
+
default: null,
|
|
83
|
+
},
|
|
84
|
+
originalFileSize: {
|
|
85
|
+
type: Number,
|
|
86
|
+
default: null,
|
|
87
|
+
},
|
|
88
|
+
optimizedKey: {
|
|
89
|
+
type: String,
|
|
90
|
+
default: null,
|
|
91
|
+
},
|
|
92
|
+
processingError: {
|
|
93
|
+
type: String,
|
|
94
|
+
default: null,
|
|
95
|
+
},
|
|
96
|
+
optimizedSize: {
|
|
97
|
+
type: Number,
|
|
98
|
+
default: null,
|
|
99
|
+
},
|
|
100
|
+
durationSeconds: {
|
|
101
|
+
type: Number,
|
|
102
|
+
default: null,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// === NEW FIELDS FOR VIDEO PROCESSING CALLBACK ===
|
|
106
|
+
renditions: {
|
|
107
|
+
type: [RenditionSchema], // Array of rendition objects
|
|
108
|
+
default: [],
|
|
109
|
+
},
|
|
110
|
+
videoId: { // Optimized video URL for playback from other db
|
|
111
|
+
type: String,
|
|
112
|
+
default: null,
|
|
113
|
+
trim: true,
|
|
114
|
+
},
|
|
115
|
+
optimizationStatus: { // Status of optimization process
|
|
116
|
+
type: String,
|
|
117
|
+
enum: ["success", "failed","progress"],
|
|
118
|
+
default: "progress",
|
|
119
|
+
|
|
120
|
+
},
|
|
121
|
+
reductionPercentage:{
|
|
122
|
+
type:String,
|
|
123
|
+
default: null,
|
|
124
|
+
trim: true,
|
|
125
|
+
},
|
|
126
|
+
masterPlaylistKey: { // Key for the master playlist in the public container
|
|
127
|
+
type: String,
|
|
128
|
+
default: null,
|
|
129
|
+
trim: true,
|
|
130
|
+
},
|
|
131
|
+
durationTook: {
|
|
132
|
+
type: String, // Duration took for the optimization process
|
|
133
|
+
trim: true,
|
|
134
|
+
default: null, // Duration in seconds for the optimization process
|
|
135
|
+
},
|
|
136
|
+
commentsCount: {
|
|
137
|
+
type: Number,
|
|
138
|
+
default: 0
|
|
139
|
+
},
|
|
140
|
+
shares: {
|
|
141
|
+
type: Number,
|
|
142
|
+
default: 0
|
|
143
|
+
},
|
|
144
|
+
// Analytics new fields
|
|
145
|
+
viewCount: {
|
|
146
|
+
type: Number,
|
|
147
|
+
default: 0
|
|
148
|
+
},
|
|
149
|
+
uniqueViewCount: {
|
|
150
|
+
type: Number,
|
|
151
|
+
default: 0
|
|
152
|
+
},
|
|
153
|
+
totalWatchDuration: {
|
|
154
|
+
type: Number,
|
|
155
|
+
default: 0
|
|
156
|
+
},
|
|
157
|
+
totalCompletionPercentageSum: {
|
|
158
|
+
type: Number,
|
|
159
|
+
default: 0
|
|
160
|
+
},
|
|
161
|
+
isEligibleToPlay : {
|
|
162
|
+
type: Boolean,
|
|
163
|
+
default: false, // Default to true, can be set to false if video is not eligible for playback
|
|
164
|
+
index: true // Index for quick lookups
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{ timestamps: true }
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Index for querying videos that need processing status updates
|
|
171
|
+
ShoppableVideoSchema.index({ processingStatus: 1, updatedAt: 1 });
|
|
172
|
+
// Index for host and their videos
|
|
173
|
+
ShoppableVideoSchema.index({ host: 1, hostModel: 1 });
|
|
174
|
+
|
|
175
|
+
const ShoppableVideo = model("shoppablevideos", ShoppableVideoSchema);
|
|
176
|
+
|
|
177
|
+
export default ShoppableVideo;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const ShoppableVideoCommentSchema = new mongoose.Schema({
|
|
5
|
+
videoId: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: "ShoppableVideo",
|
|
8
|
+
required: true
|
|
9
|
+
},
|
|
10
|
+
userId: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: "users", // Must match exactly with your model name
|
|
13
|
+
required: true
|
|
14
|
+
},
|
|
15
|
+
text: {
|
|
16
|
+
type: String,
|
|
17
|
+
required: true,
|
|
18
|
+
trim: true
|
|
19
|
+
},
|
|
20
|
+
likes: {
|
|
21
|
+
type: Number,
|
|
22
|
+
default: 0
|
|
23
|
+
},
|
|
24
|
+
likedBy: [{
|
|
25
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
26
|
+
ref: "users"
|
|
27
|
+
}],
|
|
28
|
+
isReply: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: false
|
|
31
|
+
},
|
|
32
|
+
parentCommentId: {
|
|
33
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
34
|
+
ref: "ShoppableVideoComment"
|
|
35
|
+
},
|
|
36
|
+
repliesCount: {
|
|
37
|
+
type: Number,
|
|
38
|
+
default: 0
|
|
39
|
+
}
|
|
40
|
+
}, {
|
|
41
|
+
timestamps: true,
|
|
42
|
+
toJSON: { virtuals: true },
|
|
43
|
+
toObject: { virtuals: true }
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Corrected virtual field
|
|
47
|
+
ShoppableVideoCommentSchema.virtual('isLiked').get(function() {
|
|
48
|
+
return this.likedBy ? this.likedBy.includes(this.userId) : false;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Indexes
|
|
52
|
+
ShoppableVideoCommentSchema.index({ videoId: 1, createdAt: -1 });
|
|
53
|
+
ShoppableVideoCommentSchema.index({ parentCommentId: 1, createdAt: -1 });
|
|
54
|
+
ShoppableVideoCommentSchema.index({ userId: 1 });
|
|
55
|
+
ShoppableVideoCommentSchema.index({ videoId: 1, isReply: 1 });
|
|
56
|
+
|
|
57
|
+
const ShoppableVideoComment = mongoose.model("ShoppableVideoComment", ShoppableVideoCommentSchema);
|
|
58
|
+
export default ShoppableVideoComment;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
const shoppableVideoLikeSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
videoId: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: "ShoppableVideo",
|
|
8
|
+
required: true,
|
|
9
|
+
},
|
|
10
|
+
userId: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: "User", // Adjust to your User model name
|
|
13
|
+
required: true,
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
timestamps: true,
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// Create compound index to ensure one like per user per video
|
|
22
|
+
shoppableVideoLikeSchema.index({ videoId: 1, userId: 1 }, { unique: true });
|
|
23
|
+
|
|
24
|
+
// Index for efficient queries
|
|
25
|
+
shoppableVideoLikeSchema.index({ videoId: 1, createdAt: -1 });
|
|
26
|
+
shoppableVideoLikeSchema.index({ userId: 1, createdAt: -1 });
|
|
27
|
+
|
|
28
|
+
const ShoppableVideoLike = mongoose.model("ShoppableVideoLike", shoppableVideoLikeSchema);
|
|
29
|
+
|
|
30
|
+
export default ShoppableVideoLike;
|