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
package/auth.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { VALID_TOKENS } from "./config.js";
|
|
2
|
+
|
|
3
|
+
// console.log('VALID_TOKENS', VALID_TOKENS)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export function verifyAccessToken(token) {
|
|
7
|
+
if (!token) {
|
|
8
|
+
throw new Error('❌ Unauthorized: Missing token for shared models');
|
|
9
|
+
}
|
|
10
|
+
if (!VALID_TOKENS.includes(token)) {
|
|
11
|
+
throw new Error('❌ Unauthorized: Invalid token for shared models');
|
|
12
|
+
}
|
|
13
|
+
console.log('✅ Token validated for shared models');
|
|
14
|
+
}
|
package/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const VALID_TOKENS = ['Flykup_backend','Flykup_bga','Flykup_tapntake','Flykup_erp','Flykup_control_backend','Flykup_livestream','Flykup_shoppablevideos','Flykup_webinar','Flykup_creatordock','Flykup_socialpulse'];
|
package/db_connection.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
let isConnected = false;
|
|
4
|
+
|
|
5
|
+
export async function connectToDB(uri) {
|
|
6
|
+
if (isConnected) return;
|
|
7
|
+
|
|
8
|
+
if (!uri) {
|
|
9
|
+
throw new Error('❌ No MongoDB URI provided to connectToDB');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await mongoose.connect(uri, {
|
|
14
|
+
maxPoolSize: 10,
|
|
15
|
+
|
|
16
|
+
});
|
|
17
|
+
isConnected = true;
|
|
18
|
+
console.log(`✅ Shared-models DB connected`);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error('❌ DB connection failed:', err);
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { verifyAccessToken } from './auth.js';
|
|
2
|
+
import { connectToDB } from './db_connection.js';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import Admin from './models/admin.model.js';
|
|
6
|
+
import BlockedRegion from './models/blockedRegion.models.js';
|
|
7
|
+
import Category from './models/category.model.js';
|
|
8
|
+
import CoHostInvitation from './models/coHostInvitation.model.js';
|
|
9
|
+
import Follow from './models/follow.model.js';
|
|
10
|
+
import LoginLogs from './models/loginlogs.model.js';
|
|
11
|
+
import Notification from './models/notification.model.js';
|
|
12
|
+
import ProductListing from './models/productListing.model.js';
|
|
13
|
+
import Seller from './models/seller.model.js';
|
|
14
|
+
import Settings from './models/settings.model.js';
|
|
15
|
+
import Shipper from './models/shipper.model.js';
|
|
16
|
+
import ShoppableVideo from './models/shoppableVideo.model.js';
|
|
17
|
+
import ShoppableVideoComment from './models/shoppableVideoComment.model.js';
|
|
18
|
+
import ShoppableVideoLike from './models/shoppableVideoLike.model.js';
|
|
19
|
+
import ShoppableVideoSave from './models/shoppableVideoSave.model.js';
|
|
20
|
+
import Shows from './models/shows.model.js';
|
|
21
|
+
import Stock from './models/stock.model.js';
|
|
22
|
+
import User from './models/user.model.js';
|
|
23
|
+
import ProductInteraction from './models/ProductInteraction.model.js';
|
|
24
|
+
import Wishlist from './models/Wishlist.model.js';
|
|
25
|
+
import { ChatBlock, ChatMessage, ChatRoom, ChatRoomMember } from './models/chat.model.js';
|
|
26
|
+
import Order from './models/order.modal.js';
|
|
27
|
+
import LiveStreamInteraction from './models/LiveStreamInteraction.model.js';
|
|
28
|
+
import ProfileInteraction from './models/profileInteractions.model.js';
|
|
29
|
+
import RegisterShow from './models/registerShow.model.js';
|
|
30
|
+
import { Review, ReviewType } from './models/Review.model.js';
|
|
31
|
+
import ShoppableVideoInteraction from './models/ShoppableInteraction.model.js';
|
|
32
|
+
|
|
33
|
+
let initialized = false;
|
|
34
|
+
|
|
35
|
+
export async function initializeSharedModels({ token, dbUri }) {
|
|
36
|
+
verifyAccessToken(token);
|
|
37
|
+
|
|
38
|
+
if (!dbUri) {
|
|
39
|
+
throw new Error('❌ MONGO_URI must be provided by the backend');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await connectToDB(dbUri);
|
|
43
|
+
initialized = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ensureInitialized() {
|
|
47
|
+
if (!initialized) {
|
|
48
|
+
throw new Error('❌ Shared models not initialized. Call initializeSharedModels() first.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createModelProxy(model) {
|
|
53
|
+
return new Proxy(model, {
|
|
54
|
+
get(target, prop) {
|
|
55
|
+
ensureInitialized();
|
|
56
|
+
return target[prop];
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
export const AdminModel = createModelProxy(Admin);
|
|
63
|
+
export const BlockedRegionModel = createModelProxy(BlockedRegion);
|
|
64
|
+
export const CategoryModel = createModelProxy(Category);
|
|
65
|
+
export const CoHostInvitationModel = createModelProxy(CoHostInvitation);
|
|
66
|
+
export const FollowModel = createModelProxy(Follow);
|
|
67
|
+
export const LoginLogsModel = createModelProxy(LoginLogs);
|
|
68
|
+
export const NotificationModel = createModelProxy(Notification);
|
|
69
|
+
export const ProductListingModel = createModelProxy(ProductListing);
|
|
70
|
+
export const SellerModel = createModelProxy(Seller);
|
|
71
|
+
export const SettingsModel = createModelProxy(Settings);
|
|
72
|
+
export const ShipperModel = createModelProxy(Shipper);
|
|
73
|
+
export const ShoppableVideoModel = createModelProxy(ShoppableVideo);
|
|
74
|
+
export const ShoppableVideoCommentModel = createModelProxy(ShoppableVideoComment);
|
|
75
|
+
export const ShoppableVideoLikeModel = createModelProxy(ShoppableVideoLike);
|
|
76
|
+
export const ShoppableVideoSaveModel = createModelProxy(ShoppableVideoSave);
|
|
77
|
+
export const ShowsModel = createModelProxy(Shows);
|
|
78
|
+
export const StockModel = createModelProxy(Stock);
|
|
79
|
+
export const UserModel = createModelProxy(User);
|
|
80
|
+
export const ProductInteractionModel = createModelProxy(ProductInteraction);
|
|
81
|
+
export const WishlistModel = createModelProxy(Wishlist);
|
|
82
|
+
export const ChatBlockModel = createModelProxy(ChatBlock);
|
|
83
|
+
export const ChatMessageModel = createModelProxy(ChatMessage);
|
|
84
|
+
export const ChatRoomModel = createModelProxy(ChatRoom);
|
|
85
|
+
export const ChatRoomMemberModel = createModelProxy(ChatRoomMember);
|
|
86
|
+
export const OrderModel = createModelProxy(Order);
|
|
87
|
+
export const LiveStreamInteractionModel = createModelProxy(LiveStreamInteraction);
|
|
88
|
+
export const ProfileInteractionModel = createModelProxy(ProfileInteraction);
|
|
89
|
+
export const RegisterShowModel = createModelProxy(RegisterShow);
|
|
90
|
+
export const ReviewTypeModel = createModelProxy(ReviewType);
|
|
91
|
+
export const ReviewModel = createModelProxy(Review);
|
|
92
|
+
export const ShoppableVideoInteractionModel = createModelProxy(ShoppableVideoInteraction);
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
AdminModel as Admin,
|
|
97
|
+
BlockedRegionModel as BlockedRegion,
|
|
98
|
+
CategoryModel as Category,
|
|
99
|
+
CoHostInvitationModel as CoHostInvitation,
|
|
100
|
+
FollowModel as Follow,
|
|
101
|
+
LoginLogsModel as LoginLogs,
|
|
102
|
+
NotificationModel as Notification,
|
|
103
|
+
ProductListingModel as ProductListing,
|
|
104
|
+
SellerModel as Seller,
|
|
105
|
+
SettingsModel as Settings,
|
|
106
|
+
ShipperModel as Shipper,
|
|
107
|
+
ShoppableVideoModel as ShoppableVideo,
|
|
108
|
+
ShoppableVideoCommentModel as ShoppableVideoComment,
|
|
109
|
+
ShoppableVideoLikeModel as ShoppableVideoLike,
|
|
110
|
+
ShoppableVideoSaveModel as ShoppableVideoSave,
|
|
111
|
+
ShowsModel as Shows,
|
|
112
|
+
StockModel as Stock,
|
|
113
|
+
UserModel as User,
|
|
114
|
+
ProductInteractionModel as ProductInteraction,
|
|
115
|
+
WishlistModel as Wishlist,
|
|
116
|
+
ChatBlockModel as ChatBlock,
|
|
117
|
+
ChatRoomModel as ChatRoom,
|
|
118
|
+
ChatMessageModel as ChatMessage,
|
|
119
|
+
ChatRoomMemberModel as ChatRoomMember,
|
|
120
|
+
OrderModel as Order,
|
|
121
|
+
LiveStreamInteractionModel as LiveStreamInteraction,
|
|
122
|
+
ProfileInteractionModel as ProfileInteraction,
|
|
123
|
+
RegisterShowModel as RegisterShow,
|
|
124
|
+
ReviewModel as Review,
|
|
125
|
+
ReviewTypeModel as ReviewType,
|
|
126
|
+
ShoppableVideoInteractionModel as ShoppableVideoInteraction
|
|
127
|
+
};
|
package/info.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
publish command : npm publish --access public
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Schema, model } from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const AdminEmailSchema = new Schema({
|
|
4
|
+
email: {
|
|
5
|
+
type: String,
|
|
6
|
+
required: true,
|
|
7
|
+
unique: true,
|
|
8
|
+
lowercase: true,
|
|
9
|
+
trim: true
|
|
10
|
+
},
|
|
11
|
+
isActive: {
|
|
12
|
+
type: Boolean,
|
|
13
|
+
default: true
|
|
14
|
+
},
|
|
15
|
+
permissions: {
|
|
16
|
+
shoppableVideo: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: false
|
|
19
|
+
},
|
|
20
|
+
productAccess: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
default: false
|
|
23
|
+
},
|
|
24
|
+
totalAccess: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
default: false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}, {
|
|
30
|
+
timestamps: true
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Index for faster querying
|
|
34
|
+
AdminEmailSchema.index({ email: 1, isActive: 1 });
|
|
35
|
+
|
|
36
|
+
export default model('AdminEmail', AdminEmailSchema);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
const liveStreamInteractionSchema = new mongoose.Schema({
|
|
5
|
+
show: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: 'shows',
|
|
8
|
+
required: true
|
|
9
|
+
},
|
|
10
|
+
user: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: 'User',
|
|
13
|
+
index: true
|
|
14
|
+
},
|
|
15
|
+
sessionIdentifier: {
|
|
16
|
+
type: String,
|
|
17
|
+
index: true
|
|
18
|
+
},
|
|
19
|
+
host: {
|
|
20
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
21
|
+
refPath: 'hostModel',
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
hostModel: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: true,
|
|
27
|
+
enum: ['sellers', 'dropshippers']
|
|
28
|
+
},
|
|
29
|
+
location: {
|
|
30
|
+
city: String,
|
|
31
|
+
region: String,
|
|
32
|
+
country: String
|
|
33
|
+
},
|
|
34
|
+
platform: {
|
|
35
|
+
type: String,
|
|
36
|
+
enum: ['web', 'mobile', 'unknown'],
|
|
37
|
+
default: 'unknown'
|
|
38
|
+
},
|
|
39
|
+
device: {
|
|
40
|
+
type: String,
|
|
41
|
+
enum: ['mobile', 'desktop', 'tablet', 'other', 'unknown'],
|
|
42
|
+
default: 'unknown'
|
|
43
|
+
},
|
|
44
|
+
browser: String,
|
|
45
|
+
os: String,
|
|
46
|
+
ip: {
|
|
47
|
+
type: String,
|
|
48
|
+
required: true
|
|
49
|
+
},
|
|
50
|
+
watchDuration: {
|
|
51
|
+
type: Number,
|
|
52
|
+
default: 0
|
|
53
|
+
},
|
|
54
|
+
lastSeenAt: Date,
|
|
55
|
+
hasLiked: {
|
|
56
|
+
type: Boolean,
|
|
57
|
+
default: false
|
|
58
|
+
},
|
|
59
|
+
buyNowClicked: [{
|
|
60
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
61
|
+
ref: 'productlistings'
|
|
62
|
+
}],
|
|
63
|
+
auctionBidded: [{
|
|
64
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
65
|
+
ref: 'productlistings'
|
|
66
|
+
}],
|
|
67
|
+
giveawayEntered: [{
|
|
68
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
69
|
+
ref: 'productlistings'
|
|
70
|
+
}],
|
|
71
|
+
}, {
|
|
72
|
+
timestamps: true
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Unique indexes
|
|
76
|
+
liveStreamInteractionSchema.index({ show: 1, user: 1 }, {
|
|
77
|
+
unique: true,
|
|
78
|
+
partialFilterExpression: { user: { $exists: true } }
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
liveStreamInteractionSchema.index({ show: 1, sessionIdentifier: 1 }, {
|
|
82
|
+
unique: true,
|
|
83
|
+
partialFilterExpression: { sessionIdentifier: { $exists: true } }
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
liveStreamInteractionSchema.index({ show: 1, createdAt: -1 });
|
|
87
|
+
|
|
88
|
+
const LiveStreamInteraction = mongoose.model(
|
|
89
|
+
'LiveStreamInteraction',
|
|
90
|
+
liveStreamInteractionSchema
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
export default LiveStreamInteraction;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const productInteractionSchema = new mongoose.Schema({
|
|
4
|
+
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
|
|
5
|
+
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true },
|
|
6
|
+
seller: { type: mongoose.Schema.Types.ObjectId, ref: 'Seller', required: true },
|
|
7
|
+
location: {
|
|
8
|
+
city: String,
|
|
9
|
+
region: String,
|
|
10
|
+
country: String
|
|
11
|
+
},
|
|
12
|
+
// --- NEW FIELD ---
|
|
13
|
+
platform: { type: String, enum: ['web', 'mobile', 'unknown'], default: 'mobile' },
|
|
14
|
+
device: { type: String, enum: ['mobile', 'desktop', 'tablet', 'other'] },
|
|
15
|
+
browser: String,
|
|
16
|
+
os: String,
|
|
17
|
+
ip: { type: String, required: true },
|
|
18
|
+
isIndianRegion: { type: Boolean, default: false },
|
|
19
|
+
rating: {
|
|
20
|
+
type: Number,
|
|
21
|
+
min: 1,
|
|
22
|
+
max: 5,
|
|
23
|
+
validate: {
|
|
24
|
+
validator: Number.isInteger,
|
|
25
|
+
message: '{VALUE} is not an integer value'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
review: {
|
|
29
|
+
type: String,
|
|
30
|
+
maxlength: 500
|
|
31
|
+
}
|
|
32
|
+
}, { timestamps: true });
|
|
33
|
+
|
|
34
|
+
// Ensures a user's view is counted only once per product
|
|
35
|
+
productInteractionSchema.index({ product: 1, user: 1 }, { unique: true, partialFilterExpression: { user: { $exists: true } } });
|
|
36
|
+
|
|
37
|
+
// Additional indexes for performance
|
|
38
|
+
productInteractionSchema.index({ createdAt: -1 });
|
|
39
|
+
|
|
40
|
+
const ProductInteraction = mongoose.model('ProductInteraction', productInteractionSchema);
|
|
41
|
+
|
|
42
|
+
export default ProductInteraction;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
const { Schema } = mongoose;
|
|
3
|
+
|
|
4
|
+
// Define the ReviewType enum
|
|
5
|
+
const ReviewType = {
|
|
6
|
+
PRODUCT: "product",
|
|
7
|
+
SELLER: "seller"
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const ReviewSchema = new Schema(
|
|
11
|
+
{
|
|
12
|
+
userId: {
|
|
13
|
+
type: Schema.Types.ObjectId,
|
|
14
|
+
ref: "users",
|
|
15
|
+
required: true
|
|
16
|
+
},
|
|
17
|
+
reviewType: {
|
|
18
|
+
type: String,
|
|
19
|
+
enum: Object.values(ReviewType),
|
|
20
|
+
required: true
|
|
21
|
+
},
|
|
22
|
+
productId: {
|
|
23
|
+
type: Schema.Types.ObjectId,
|
|
24
|
+
ref: "productlistings",
|
|
25
|
+
required: function() {
|
|
26
|
+
return this.reviewType === ReviewType.PRODUCT;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
sellerId: {
|
|
30
|
+
type: Schema.Types.ObjectId,
|
|
31
|
+
ref: "sellers",
|
|
32
|
+
required: function() {
|
|
33
|
+
return this.reviewType === ReviewType.SELLER;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
rating: {
|
|
37
|
+
type: Number,
|
|
38
|
+
required: true,
|
|
39
|
+
min: 1,
|
|
40
|
+
max: 5
|
|
41
|
+
},
|
|
42
|
+
content: {
|
|
43
|
+
type: String,
|
|
44
|
+
required: true,
|
|
45
|
+
trim: true,
|
|
46
|
+
maxlength: 1000
|
|
47
|
+
},
|
|
48
|
+
helpfulVotes: {
|
|
49
|
+
type: [{
|
|
50
|
+
userId: {
|
|
51
|
+
type: Schema.Types.ObjectId,
|
|
52
|
+
ref: "users",
|
|
53
|
+
required: true
|
|
54
|
+
},
|
|
55
|
+
voteType: {
|
|
56
|
+
type: String,
|
|
57
|
+
enum: ["helpful", "not_helpful"],
|
|
58
|
+
required: true
|
|
59
|
+
},
|
|
60
|
+
createdAt: {
|
|
61
|
+
type: Date,
|
|
62
|
+
default: Date.now
|
|
63
|
+
}
|
|
64
|
+
}],
|
|
65
|
+
default: []
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
helpfulCount: {
|
|
69
|
+
type: Number,
|
|
70
|
+
default: 0,
|
|
71
|
+
min: 0
|
|
72
|
+
},
|
|
73
|
+
notHelpfulCount: {
|
|
74
|
+
type: Number,
|
|
75
|
+
default: 0,
|
|
76
|
+
min: 0
|
|
77
|
+
},
|
|
78
|
+
images: {
|
|
79
|
+
type: [String], // Array of image URLs
|
|
80
|
+
default: [],
|
|
81
|
+
validate: {
|
|
82
|
+
validator: function(array) {
|
|
83
|
+
return array.length <= 5;
|
|
84
|
+
},
|
|
85
|
+
message: 'Cannot upload more than 5 images per review'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
timestamps: true,
|
|
91
|
+
toJSON: { virtuals: true },
|
|
92
|
+
toObject: { virtuals: true }
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Indexes
|
|
97
|
+
ReviewSchema.index({ productId: 1 });
|
|
98
|
+
ReviewSchema.index({ sellerId: 1 });
|
|
99
|
+
ReviewSchema.index({ userId: 1 });
|
|
100
|
+
ReviewSchema.index({ reviewType: 1 });
|
|
101
|
+
ReviewSchema.index({ "helpfulVotes.userId": 1 });
|
|
102
|
+
|
|
103
|
+
// Virtual for total feedback count
|
|
104
|
+
ReviewSchema.virtual('totalFeedback').get(function() {
|
|
105
|
+
return this.helpfulCount + this.notHelpfulCount;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Method to check if a user has voted
|
|
109
|
+
ReviewSchema.methods.hasUserVoted = function(userId) {
|
|
110
|
+
return this.helpfulVotes.some(vote => vote.userId.equals(userId));
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Method to get user's vote type
|
|
114
|
+
ReviewSchema.methods.getUserVoteType = function(userId) {
|
|
115
|
+
const vote = this.helpfulVotes.find(vote => vote.userId.equals(userId));
|
|
116
|
+
return vote ? vote.voteType : null;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const Review = mongoose.model("reviews", ReviewSchema);
|
|
120
|
+
|
|
121
|
+
export { Review, ReviewType };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
const searchAnalyticsSchema = new mongoose.Schema({
|
|
4
|
+
searchTerm: { type: String, required: true, trim: true },
|
|
5
|
+
category: {
|
|
6
|
+
type: String,
|
|
7
|
+
enum: ["shows", "videos", "products", "users", "all"],
|
|
8
|
+
default: "all"
|
|
9
|
+
},
|
|
10
|
+
userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: false },
|
|
11
|
+
resultCount: { type: Number, default: 0 },
|
|
12
|
+
ipAddress: { type: String },
|
|
13
|
+
deviceInfo: { type: String },
|
|
14
|
+
location: {
|
|
15
|
+
city: { type: String },
|
|
16
|
+
region: { type: String },
|
|
17
|
+
country: { type: String }
|
|
18
|
+
},
|
|
19
|
+
createdAt: { type: Date, default: Date.now }
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default mongoose.model("SearchAnalytics", searchAnalyticsSchema);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
const shoppableVideoInteractionSchema = new mongoose.Schema({
|
|
5
|
+
video: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: 'ShoppableVideo', // Reference the correct model name
|
|
8
|
+
required: true
|
|
9
|
+
},
|
|
10
|
+
user: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: 'User',
|
|
13
|
+
index: true
|
|
14
|
+
},
|
|
15
|
+
// Used to identify anonymous users by hashing their IP and User-Agent
|
|
16
|
+
sessionIdentifier: {
|
|
17
|
+
type: String,
|
|
18
|
+
index: true
|
|
19
|
+
},
|
|
20
|
+
host: {
|
|
21
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
22
|
+
refPath: 'hostModel',
|
|
23
|
+
required: true
|
|
24
|
+
},
|
|
25
|
+
hostModel: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
enum: ['sellers', 'dropshippers']
|
|
29
|
+
},
|
|
30
|
+
location: {
|
|
31
|
+
city: String,
|
|
32
|
+
region: String,
|
|
33
|
+
country: String
|
|
34
|
+
},
|
|
35
|
+
platform: {
|
|
36
|
+
type: String,
|
|
37
|
+
enum: ['web', 'mobile', 'unknown'],
|
|
38
|
+
default: 'unknown'
|
|
39
|
+
},
|
|
40
|
+
device: {
|
|
41
|
+
type: String,
|
|
42
|
+
enum: ['mobile', 'desktop', 'tablet', 'other', 'unknown'],
|
|
43
|
+
default: 'unknown'
|
|
44
|
+
},
|
|
45
|
+
browser: String,
|
|
46
|
+
os: String,
|
|
47
|
+
ip: {
|
|
48
|
+
type: String,
|
|
49
|
+
required: true
|
|
50
|
+
},
|
|
51
|
+
watchDuration: { // Total seconds the user watched across sessions
|
|
52
|
+
type: Number,
|
|
53
|
+
default: 0
|
|
54
|
+
},
|
|
55
|
+
completionPercentage: { // The maximum percentage of the video watched
|
|
56
|
+
type: Number,
|
|
57
|
+
min: 0,
|
|
58
|
+
max: 100,
|
|
59
|
+
default: 0
|
|
60
|
+
},
|
|
61
|
+
productsClicked: [{ // Unique list of products clicked by this user for this video
|
|
62
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
63
|
+
ref: 'productlistings'
|
|
64
|
+
}]
|
|
65
|
+
}, {
|
|
66
|
+
timestamps: true
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Ensures a unique interaction record per logged-in user per video.
|
|
70
|
+
shoppableVideoInteractionSchema.index({ video: 1, user: 1 }, {
|
|
71
|
+
unique: true,
|
|
72
|
+
partialFilterExpression: { user: { $exists: true } }
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Ensures a unique interaction record per anonymous session per video.
|
|
76
|
+
shoppableVideoInteractionSchema.index({ video: 1, sessionIdentifier: 1 }, {
|
|
77
|
+
unique: true,
|
|
78
|
+
partialFilterExpression: { sessionIdentifier: { $exists: true } }
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Additional indexes for fast analytic queries
|
|
82
|
+
shoppableVideoInteractionSchema.index({ video: 1, createdAt: -1 });
|
|
83
|
+
|
|
84
|
+
const ShoppableVideoInteraction = mongoose.model(
|
|
85
|
+
'ShoppableVideoInteraction',
|
|
86
|
+
shoppableVideoInteractionSchema
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
export default ShoppableVideoInteraction;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// backend/models/Wishlist.js
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
const { Schema } = mongoose;
|
|
4
|
+
|
|
5
|
+
const WishlistSchema = new Schema(
|
|
6
|
+
{
|
|
7
|
+
userId: {
|
|
8
|
+
type: Schema.Types.ObjectId,
|
|
9
|
+
ref: "users",
|
|
10
|
+
required: true
|
|
11
|
+
},
|
|
12
|
+
productId: {
|
|
13
|
+
type: Schema.Types.ObjectId,
|
|
14
|
+
ref: "productlistings",
|
|
15
|
+
required: true
|
|
16
|
+
},
|
|
17
|
+
addedAt: {
|
|
18
|
+
type: Date,
|
|
19
|
+
default: Date.now
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{ timestamps: true }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Add compound index to prevent duplicate entries
|
|
26
|
+
WishlistSchema.index({ userId: 1, productId: 1 }, { unique: true });
|
|
27
|
+
|
|
28
|
+
const Wishlist = mongoose.model("wishlists", WishlistSchema);
|
|
29
|
+
|
|
30
|
+
export default Wishlist;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const AdminSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
name: { type: String, required: true },
|
|
6
|
+
role: { type: String, required: true, default: "admin" },
|
|
7
|
+
email: { type: String, required: true, unique: true },
|
|
8
|
+
mobileNumber: { type: String, required: true },
|
|
9
|
+
password: { type: String, required: true },
|
|
10
|
+
profilePicture: { type: String, default: "https://img.freepik.com/free-vector/blue-circle-with-white-user_78370-4707.jpg" },
|
|
11
|
+
|
|
12
|
+
contentAccess: {
|
|
13
|
+
users: { readOnly: Boolean, edit: Boolean },
|
|
14
|
+
pendingSellers: { readOnly: Boolean, edit: Boolean },
|
|
15
|
+
sellers: { readOnly: Boolean, edit: Boolean },
|
|
16
|
+
orders: { readOnly: Boolean, edit: Boolean },
|
|
17
|
+
category: { readOnly: Boolean, edit: Boolean },
|
|
18
|
+
// settings: { readOnly: Boolean, edit: Boolean },
|
|
19
|
+
// admins: { readOnly: Boolean, edit: Boolean },
|
|
20
|
+
},
|
|
21
|
+
maskingSwitch: { type: Boolean, default: false },
|
|
22
|
+
},
|
|
23
|
+
{ timestamps: true }
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const Admin = mongoose.model('Admin', AdminSchema);
|
|
27
|
+
export default Admin;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Schema, model } from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const updateSchema = new Schema({
|
|
4
|
+
platform: { type: String, enum: ['android', 'ios'], required: true },
|
|
5
|
+
currentVersion: { type: String, required: true },
|
|
6
|
+
minVersion: { type: String, required: true }, // Minimum supported version
|
|
7
|
+
isForceUpdate: { type: Boolean, default: false },
|
|
8
|
+
releaseDate: { type: Date, default: Date.now },
|
|
9
|
+
features: [{
|
|
10
|
+
title: String,
|
|
11
|
+
description: String
|
|
12
|
+
}],
|
|
13
|
+
releaseNotes: String,
|
|
14
|
+
downloadUrl: String,
|
|
15
|
+
createdAt: { type: Date, default: Date.now }
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default model('Update', updateSchema);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// models/Asset.js
|
|
2
|
+
|
|
3
|
+
import mongoose from 'mongoose';
|
|
4
|
+
import mongoosePaginate from 'mongoose-paginate-v2';
|
|
5
|
+
|
|
6
|
+
const assetSchema = new mongoose.Schema({
|
|
7
|
+
title: {
|
|
8
|
+
type: String,
|
|
9
|
+
required: true
|
|
10
|
+
},
|
|
11
|
+
key: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true,
|
|
14
|
+
unique: true
|
|
15
|
+
},
|
|
16
|
+
type: {
|
|
17
|
+
type: String,
|
|
18
|
+
required: true,
|
|
19
|
+
enum: ['video', 'image', 'pdf', 'other']
|
|
20
|
+
},
|
|
21
|
+
url: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: true
|
|
24
|
+
}
|
|
25
|
+
}, {
|
|
26
|
+
timestamps: true
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
assetSchema.plugin(mongoosePaginate);
|
|
30
|
+
|
|
31
|
+
// Changed to a named export 'AssetsModel'
|
|
32
|
+
export const AssetsModel = mongoose.model('Asset', assetSchema);
|