@mac777/project-pinecone-models 1.0.0 → 1.0.2
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/dist/Event.d.ts +9 -9
- package/dist/Event.js +1 -1
- package/package.json +1 -5
- package/src/Bkash.ts +10 -0
- package/src/Event.ts +274 -0
- package/src/Media.ts +36 -0
- package/src/Order.ts +201 -0
- package/src/Payment.ts +61 -0
- package/src/Ticket.ts +105 -0
- package/src/User.ts +47 -0
- package/src/index.ts +9 -0
- package/tsconfig.json +16 -0
package/dist/Event.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
19
19
|
visibility: "hidden" | "public" | "invite_only";
|
|
20
20
|
reserved: number;
|
|
21
21
|
benefits: string[];
|
|
22
|
+
tier: string;
|
|
22
23
|
description?: string | null | undefined;
|
|
23
24
|
price?: {
|
|
24
25
|
amount: number;
|
|
@@ -32,7 +33,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
32
33
|
minPerOrder: number;
|
|
33
34
|
maxPerOrder: number;
|
|
34
35
|
} | null | undefined;
|
|
35
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
36
36
|
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
37
37
|
name: string;
|
|
38
38
|
status: "active" | "sold_out" | "inactive";
|
|
@@ -42,6 +42,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
42
42
|
visibility: "hidden" | "public" | "invite_only";
|
|
43
43
|
reserved: number;
|
|
44
44
|
benefits: string[];
|
|
45
|
+
tier: string;
|
|
45
46
|
description?: string | null | undefined;
|
|
46
47
|
price?: {
|
|
47
48
|
amount: number;
|
|
@@ -55,7 +56,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
55
56
|
minPerOrder: number;
|
|
56
57
|
maxPerOrder: number;
|
|
57
58
|
} | null | undefined;
|
|
58
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
59
59
|
}> & {
|
|
60
60
|
name: string;
|
|
61
61
|
status: "active" | "sold_out" | "inactive";
|
|
@@ -65,6 +65,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
65
65
|
visibility: "hidden" | "public" | "invite_only";
|
|
66
66
|
reserved: number;
|
|
67
67
|
benefits: string[];
|
|
68
|
+
tier: string;
|
|
68
69
|
description?: string | null | undefined;
|
|
69
70
|
price?: {
|
|
70
71
|
amount: number;
|
|
@@ -78,7 +79,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
78
79
|
minPerOrder: number;
|
|
79
80
|
maxPerOrder: number;
|
|
80
81
|
} | null | undefined;
|
|
81
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
82
82
|
}>;
|
|
83
83
|
termsAccepted: boolean;
|
|
84
84
|
legalPermissionAccepted: boolean;
|
|
@@ -280,6 +280,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
280
280
|
visibility: "hidden" | "public" | "invite_only";
|
|
281
281
|
reserved: number;
|
|
282
282
|
benefits: string[];
|
|
283
|
+
tier: string;
|
|
283
284
|
description?: string | null | undefined;
|
|
284
285
|
price?: {
|
|
285
286
|
amount: number;
|
|
@@ -293,7 +294,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
293
294
|
minPerOrder: number;
|
|
294
295
|
maxPerOrder: number;
|
|
295
296
|
} | null | undefined;
|
|
296
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
297
297
|
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
298
298
|
name: string;
|
|
299
299
|
status: "active" | "sold_out" | "inactive";
|
|
@@ -303,6 +303,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
303
303
|
visibility: "hidden" | "public" | "invite_only";
|
|
304
304
|
reserved: number;
|
|
305
305
|
benefits: string[];
|
|
306
|
+
tier: string;
|
|
306
307
|
description?: string | null | undefined;
|
|
307
308
|
price?: {
|
|
308
309
|
amount: number;
|
|
@@ -316,7 +317,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
316
317
|
minPerOrder: number;
|
|
317
318
|
maxPerOrder: number;
|
|
318
319
|
} | null | undefined;
|
|
319
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
320
320
|
}> & {
|
|
321
321
|
name: string;
|
|
322
322
|
status: "active" | "sold_out" | "inactive";
|
|
@@ -326,6 +326,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
326
326
|
visibility: "hidden" | "public" | "invite_only";
|
|
327
327
|
reserved: number;
|
|
328
328
|
benefits: string[];
|
|
329
|
+
tier: string;
|
|
329
330
|
description?: string | null | undefined;
|
|
330
331
|
price?: {
|
|
331
332
|
amount: number;
|
|
@@ -339,7 +340,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
339
340
|
minPerOrder: number;
|
|
340
341
|
maxPerOrder: number;
|
|
341
342
|
} | null | undefined;
|
|
342
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
343
343
|
}>;
|
|
344
344
|
termsAccepted: boolean;
|
|
345
345
|
legalPermissionAccepted: boolean;
|
|
@@ -541,6 +541,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
541
541
|
visibility: "hidden" | "public" | "invite_only";
|
|
542
542
|
reserved: number;
|
|
543
543
|
benefits: string[];
|
|
544
|
+
tier: string;
|
|
544
545
|
description?: string | null | undefined;
|
|
545
546
|
price?: {
|
|
546
547
|
amount: number;
|
|
@@ -554,7 +555,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
554
555
|
minPerOrder: number;
|
|
555
556
|
maxPerOrder: number;
|
|
556
557
|
} | null | undefined;
|
|
557
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
558
558
|
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
559
559
|
name: string;
|
|
560
560
|
status: "active" | "sold_out" | "inactive";
|
|
@@ -564,6 +564,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
564
564
|
visibility: "hidden" | "public" | "invite_only";
|
|
565
565
|
reserved: number;
|
|
566
566
|
benefits: string[];
|
|
567
|
+
tier: string;
|
|
567
568
|
description?: string | null | undefined;
|
|
568
569
|
price?: {
|
|
569
570
|
amount: number;
|
|
@@ -577,7 +578,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
577
578
|
minPerOrder: number;
|
|
578
579
|
maxPerOrder: number;
|
|
579
580
|
} | null | undefined;
|
|
580
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
581
581
|
}> & {
|
|
582
582
|
name: string;
|
|
583
583
|
status: "active" | "sold_out" | "inactive";
|
|
@@ -587,6 +587,7 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
587
587
|
visibility: "hidden" | "public" | "invite_only";
|
|
588
588
|
reserved: number;
|
|
589
589
|
benefits: string[];
|
|
590
|
+
tier: string;
|
|
590
591
|
description?: string | null | undefined;
|
|
591
592
|
price?: {
|
|
592
593
|
amount: number;
|
|
@@ -600,7 +601,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
600
601
|
minPerOrder: number;
|
|
601
602
|
maxPerOrder: number;
|
|
602
603
|
} | null | undefined;
|
|
603
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
604
604
|
}>;
|
|
605
605
|
termsAccepted: boolean;
|
|
606
606
|
legalPermissionAccepted: boolean;
|
package/dist/Event.js
CHANGED
|
@@ -94,7 +94,7 @@ const ticketSchema = new mongoose_1.default.Schema({
|
|
|
94
94
|
reserved: { type: Number, default: 0 },
|
|
95
95
|
status: { type: String, enum: ['active', 'sold_out', 'inactive'], default: 'active' },
|
|
96
96
|
benefits: [String],
|
|
97
|
-
tier: { type: String,
|
|
97
|
+
tier: { type: String, required: true, default: 'regular' },
|
|
98
98
|
});
|
|
99
99
|
const scheduleSchema = new mongoose_1.default.Schema({
|
|
100
100
|
startDate: { type: Date, required: true },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mac777/project-pinecone-models",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -12,10 +12,6 @@
|
|
|
12
12
|
"zod": "^3.22.4",
|
|
13
13
|
"zxcvbn": "^4.4.2"
|
|
14
14
|
},
|
|
15
|
-
"files": ["dist"],
|
|
16
|
-
"publishConfig": {
|
|
17
|
-
"access": "public"
|
|
18
|
-
},
|
|
19
15
|
"devDependencies": {
|
|
20
16
|
"typescript": "^5.0.0"
|
|
21
17
|
}
|
package/src/Bkash.ts
ADDED
package/src/Event.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const mediaSchema = new mongoose.Schema({
|
|
4
|
+
coverImage: {
|
|
5
|
+
url: String,
|
|
6
|
+
thumbnailUrl: String,
|
|
7
|
+
alt: String,
|
|
8
|
+
},
|
|
9
|
+
gallery: [{
|
|
10
|
+
url: String,
|
|
11
|
+
caption: String,
|
|
12
|
+
order: Number,
|
|
13
|
+
}],
|
|
14
|
+
}, { _id: false });
|
|
15
|
+
|
|
16
|
+
const addressSchema = new mongoose.Schema({
|
|
17
|
+
street: String,
|
|
18
|
+
city: { type: String, required: true },
|
|
19
|
+
state: String,
|
|
20
|
+
postalCode: String,
|
|
21
|
+
country: { type: String, required: true },
|
|
22
|
+
}, { _id: false });
|
|
23
|
+
|
|
24
|
+
const venueSchema = new mongoose.Schema({
|
|
25
|
+
name: { type: String, required: true },
|
|
26
|
+
address: addressSchema,
|
|
27
|
+
coordinates: {
|
|
28
|
+
type: {
|
|
29
|
+
type: String,
|
|
30
|
+
enum: ['Point'],
|
|
31
|
+
default: 'Point'
|
|
32
|
+
},
|
|
33
|
+
coordinates: [Number] // [lng, lat]
|
|
34
|
+
},
|
|
35
|
+
capacity: { type: Number, required: true, min: 10, max: 100000 },
|
|
36
|
+
type: { type: String, enum: ['indoor', 'outdoor', 'hybrid'] },
|
|
37
|
+
parking: Boolean,
|
|
38
|
+
publicTransit: String,
|
|
39
|
+
}, { _id: false });
|
|
40
|
+
|
|
41
|
+
const contactPersonSchema = new mongoose.Schema({
|
|
42
|
+
name: { type: String, required: true },
|
|
43
|
+
phone: { type: String, required: true },
|
|
44
|
+
email: { type: String, required: true },
|
|
45
|
+
designation: String,
|
|
46
|
+
}, { _id: false });
|
|
47
|
+
|
|
48
|
+
const organizerSchema = new mongoose.Schema({
|
|
49
|
+
role: { type: String, enum: ['event_organizer', 'venue_owner', 'authorized_rep', 'artist'] },
|
|
50
|
+
companyName: String,
|
|
51
|
+
contactPerson: contactPersonSchema,
|
|
52
|
+
}, { _id: false });
|
|
53
|
+
|
|
54
|
+
const documentSchema = new mongoose.Schema({
|
|
55
|
+
type: { type: String, enum: ['venue_booking', 'permit', 'insurance', 'license', 'portfolio', 'other'] },
|
|
56
|
+
url: String,
|
|
57
|
+
filename: String,
|
|
58
|
+
objectKey: String,
|
|
59
|
+
uploadedAt: Date,
|
|
60
|
+
status: { type: String, enum: ['pending', 'approved', 'rejected'] },
|
|
61
|
+
rejectionReason: String,
|
|
62
|
+
}, { _id: false });
|
|
63
|
+
|
|
64
|
+
const verificationSchema = new mongoose.Schema({
|
|
65
|
+
status: { type: String, enum: ['unverified', 'pending', 'verified', 'rejected', 'needs_info'], default: 'unverified' },
|
|
66
|
+
documents: [documentSchema],
|
|
67
|
+
additionalInfo: String,
|
|
68
|
+
adminNotes: String,
|
|
69
|
+
reviewedBy: mongoose.Schema.Types.ObjectId,
|
|
70
|
+
reviewedAt: Date,
|
|
71
|
+
rejectionReason: String,
|
|
72
|
+
requestedInfo: String,
|
|
73
|
+
}, { _id: false });
|
|
74
|
+
|
|
75
|
+
const priceSchema = new mongoose.Schema({
|
|
76
|
+
amount: { type: Number, required: true },
|
|
77
|
+
currency: { type: String, default: 'USD' },
|
|
78
|
+
}, { _id: false });
|
|
79
|
+
|
|
80
|
+
const salesWindowSchema = new mongoose.Schema({
|
|
81
|
+
startDate: Date,
|
|
82
|
+
endDate: Date,
|
|
83
|
+
}, { _id: false });
|
|
84
|
+
|
|
85
|
+
const limitsSchema = new mongoose.Schema({
|
|
86
|
+
minPerOrder: { type: Number, default: 1 },
|
|
87
|
+
maxPerOrder: { type: Number, default: 10 },
|
|
88
|
+
}, { _id: false });
|
|
89
|
+
|
|
90
|
+
const ticketSchema = new mongoose.Schema({
|
|
91
|
+
name: { type: String, required: true },
|
|
92
|
+
description: String,
|
|
93
|
+
price: priceSchema,
|
|
94
|
+
quantity: { type: Number, required: true },
|
|
95
|
+
sold: { type: Number, default: 0 },
|
|
96
|
+
remaining: { type: Number, default: function(this: any) { return this.quantity - this.sold; } },
|
|
97
|
+
salesWindow: salesWindowSchema,
|
|
98
|
+
limits: limitsSchema,
|
|
99
|
+
visibility: { type: String, enum: ['public', 'hidden', 'invite_only'], default: 'public' },
|
|
100
|
+
reserved: { type: Number, default: 0 },
|
|
101
|
+
status: { type: String, enum: ['active', 'sold_out', 'inactive'], default: 'active' },
|
|
102
|
+
benefits: [String],
|
|
103
|
+
tier: { type: String, required: true, default: 'regular' },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const scheduleSchema = new mongoose.Schema({
|
|
107
|
+
startDate: { type: Date, required: true },
|
|
108
|
+
endDate: { type: Date, required: true },
|
|
109
|
+
timezone: String,
|
|
110
|
+
isMultiDay: Boolean,
|
|
111
|
+
doors: String,
|
|
112
|
+
}, { _id: false });
|
|
113
|
+
|
|
114
|
+
const metricsSchema = new mongoose.Schema({
|
|
115
|
+
views: { type: Number, default: 0 },
|
|
116
|
+
ticketsSold: { type: Number, default: 0 },
|
|
117
|
+
revenue: { type: Number, default: 0 },
|
|
118
|
+
checkIns: { type: Number, default: 0 },
|
|
119
|
+
averageRating: Number,
|
|
120
|
+
reviewCount: { type: Number, default: 0 },
|
|
121
|
+
}, { _id: false });
|
|
122
|
+
|
|
123
|
+
const pricingSchema = new mongoose.Schema({
|
|
124
|
+
platformFee: { type: Number, default: 5 },
|
|
125
|
+
paymentProcessingFee: Number,
|
|
126
|
+
currency: String,
|
|
127
|
+
}, { _id: false });
|
|
128
|
+
|
|
129
|
+
const payoutSchema = new mongoose.Schema({
|
|
130
|
+
status: { type: String, enum: ['pending', 'scheduled', 'completed'] },
|
|
131
|
+
amount: Number,
|
|
132
|
+
scheduledDate: Date,
|
|
133
|
+
paidAt: Date,
|
|
134
|
+
stripePayoutId: String,
|
|
135
|
+
}, { _id: false });
|
|
136
|
+
|
|
137
|
+
const cancellationSchema = new mongoose.Schema({
|
|
138
|
+
allowed: { type: Boolean, default: true },
|
|
139
|
+
refundPercentage: { type: Number, default: 100 },
|
|
140
|
+
deadlineHours: Number,
|
|
141
|
+
}, { _id: false });
|
|
142
|
+
|
|
143
|
+
const policiesSchema = new mongoose.Schema({
|
|
144
|
+
cancellation: cancellationSchema,
|
|
145
|
+
transferable: { type: Boolean, default: false },
|
|
146
|
+
}, { _id: false });
|
|
147
|
+
|
|
148
|
+
const flagSchema = new mongoose.Schema({
|
|
149
|
+
reportedBy: mongoose.Schema.Types.ObjectId,
|
|
150
|
+
reason: String,
|
|
151
|
+
description: String,
|
|
152
|
+
createdAt: { type: Date, default: Date.now },
|
|
153
|
+
status: { type: String, enum: ['pending', 'resolved', 'dismissed'], default: 'pending' },
|
|
154
|
+
}, { _id: false });
|
|
155
|
+
|
|
156
|
+
const moderationSchema = new mongoose.Schema({
|
|
157
|
+
isFlagged: { type: Boolean, default: false },
|
|
158
|
+
flags: [flagSchema],
|
|
159
|
+
suspendedUntil: Date,
|
|
160
|
+
banReason: String,
|
|
161
|
+
}, { _id: false });
|
|
162
|
+
|
|
163
|
+
const seoSchema = new mongoose.Schema({
|
|
164
|
+
metaTitle: String,
|
|
165
|
+
metaDescription: String,
|
|
166
|
+
keywords: [String],
|
|
167
|
+
ogImage: String,
|
|
168
|
+
}, { _id: false });
|
|
169
|
+
|
|
170
|
+
const featuresSchema = new mongoose.Schema({
|
|
171
|
+
isFeatured: { type: Boolean, default: false },
|
|
172
|
+
isPremium: { type: Boolean, default: false },
|
|
173
|
+
badges: [String],
|
|
174
|
+
}, { _id: false });
|
|
175
|
+
|
|
176
|
+
const historySchema = new mongoose.Schema({
|
|
177
|
+
action: String,
|
|
178
|
+
performedBy: mongoose.Schema.Types.ObjectId,
|
|
179
|
+
timestamp: { type: Date, default: Date.now },
|
|
180
|
+
changes: mongoose.Schema.Types.Mixed,
|
|
181
|
+
}, { _id: false });
|
|
182
|
+
|
|
183
|
+
const eventSchema = new mongoose.Schema({
|
|
184
|
+
hostId: { type: mongoose.Schema.Types.ObjectId, required: true },
|
|
185
|
+
slug: String,
|
|
186
|
+
|
|
187
|
+
// STEP 1: BASICS
|
|
188
|
+
title: { type: String, required: true, minlength: 10, maxlength: 100 },
|
|
189
|
+
type: { type: String, enum: ['concert', 'sports', 'conference', 'festival', 'theater', 'comedy', 'networking', 'workshop', 'other'], required: true },
|
|
190
|
+
categories: [{ type: String }],
|
|
191
|
+
tagline: { type: String, maxlength: 150 },
|
|
192
|
+
|
|
193
|
+
// STEP 2: DETAILS
|
|
194
|
+
description: { type: String, minlength: 50, maxlength: 5000 },
|
|
195
|
+
highlights: [{ type: String }],
|
|
196
|
+
languages: [String],
|
|
197
|
+
ageRestriction: { type: String, enum: ['all_ages', '18+', '21+'] },
|
|
198
|
+
dressCode: String,
|
|
199
|
+
|
|
200
|
+
media: mediaSchema,
|
|
201
|
+
|
|
202
|
+
// STEP 3: DATE, TIME & LOCATION
|
|
203
|
+
schedule: scheduleSchema,
|
|
204
|
+
venue: venueSchema,
|
|
205
|
+
|
|
206
|
+
// STEP 4: VERIFICATION
|
|
207
|
+
organizer: organizerSchema,
|
|
208
|
+
verification: verificationSchema,
|
|
209
|
+
|
|
210
|
+
// STEP 5: TICKETS
|
|
211
|
+
tickets: [ticketSchema],
|
|
212
|
+
termsAccepted: { type: Boolean, default: false },
|
|
213
|
+
legalPermissionAccepted: { type: Boolean, default: false },
|
|
214
|
+
platformTermsAccepted: { type: Boolean, default: false },
|
|
215
|
+
// STATUS & WORKFLOW
|
|
216
|
+
status: { type: String, enum: ['draft', 'pending_approval', 'approved', 'rejected', 'published', 'live', 'ended', 'cancelled'], default: 'draft' },
|
|
217
|
+
rejectionReason: String,
|
|
218
|
+
visibility: { type: String, enum: ['public', 'private', 'unlisted'], default: 'public' },
|
|
219
|
+
publishedAt: Date,
|
|
220
|
+
|
|
221
|
+
// METRICS
|
|
222
|
+
metrics: metricsSchema,
|
|
223
|
+
|
|
224
|
+
// BUSINESS
|
|
225
|
+
pricing: pricingSchema,
|
|
226
|
+
payout: payoutSchema,
|
|
227
|
+
|
|
228
|
+
// POLICIES
|
|
229
|
+
policies: policiesSchema,
|
|
230
|
+
|
|
231
|
+
// MODERATION
|
|
232
|
+
moderation: moderationSchema,
|
|
233
|
+
|
|
234
|
+
// SEO
|
|
235
|
+
seo: seoSchema,
|
|
236
|
+
|
|
237
|
+
// FEATURES
|
|
238
|
+
features: featuresSchema,
|
|
239
|
+
|
|
240
|
+
// AUDIT
|
|
241
|
+
createdAt: { type: Date, default: Date.now },
|
|
242
|
+
updatedAt: { type: Date, default: Date.now },
|
|
243
|
+
submittedAt: Date,
|
|
244
|
+
history: [historySchema],
|
|
245
|
+
deletedAt: Date,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Indexes
|
|
249
|
+
eventSchema.index({ slug: 1 });
|
|
250
|
+
eventSchema.index({ 'venue.coordinates': '2dsphere' });
|
|
251
|
+
eventSchema.index({ status: 1, visibility: 1 });
|
|
252
|
+
eventSchema.index({ hostId: 1 });
|
|
253
|
+
eventSchema.index({ 'schedule.startDate': 1 });
|
|
254
|
+
eventSchema.index({ type: 1 });
|
|
255
|
+
eventSchema.index({ categories: 1 });
|
|
256
|
+
|
|
257
|
+
// Validation: sum(tickets.quantity) <= venue.capacity
|
|
258
|
+
eventSchema.pre('save', function(next) {
|
|
259
|
+
if (this.tickets && this.tickets.length > 0 && this.venue) {
|
|
260
|
+
const totalCapacity = this.tickets.reduce((sum, ticket) => sum + ticket.quantity, 0);
|
|
261
|
+
if (totalCapacity > this.venue.capacity) {
|
|
262
|
+
return next(new Error('Total ticket quantity exceeds venue capacity'));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
next();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Update updatedAt on save
|
|
269
|
+
eventSchema.pre('save', function(next) {
|
|
270
|
+
this.updatedAt = new Date();
|
|
271
|
+
next();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
export default eventSchema;
|
package/src/Media.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const mediaSchema = new mongoose.Schema({
|
|
4
|
+
userId: { type: mongoose.Schema.Types.ObjectId, required: true },
|
|
5
|
+
type: { type: String, enum: ['event_cover', 'event_gallery', 'verification_doc'], required: true },
|
|
6
|
+
provider: { type: String, enum: ['imagekit', 'backblaze'], required: true },
|
|
7
|
+
status: { type: String, enum: ['temp', 'permanent', 'deleted'], required: true },
|
|
8
|
+
|
|
9
|
+
// ImageKit specific
|
|
10
|
+
fileId: String,
|
|
11
|
+
url: String,
|
|
12
|
+
thumbnailUrl: String,
|
|
13
|
+
|
|
14
|
+
// Backblaze specific
|
|
15
|
+
bucketName: String,
|
|
16
|
+
objectKey: String,
|
|
17
|
+
|
|
18
|
+
// Metadata
|
|
19
|
+
filename: { type: String, required: true },
|
|
20
|
+
mimeType: { type: String, required: true },
|
|
21
|
+
size: { type: Number, required: false },
|
|
22
|
+
|
|
23
|
+
// Lifecycle
|
|
24
|
+
uploadedAt: { type: Date, default: Date.now },
|
|
25
|
+
movedToPermanentAt: Date,
|
|
26
|
+
expiresAt: Date, // For temp files
|
|
27
|
+
deletedAt: Date,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Indexes
|
|
31
|
+
mediaSchema.index({ userId: 1 });
|
|
32
|
+
mediaSchema.index({ status: 1 });
|
|
33
|
+
mediaSchema.index({ expiresAt: 1 });
|
|
34
|
+
mediaSchema.index({ uploadedAt: 1 });
|
|
35
|
+
|
|
36
|
+
export default mediaSchema;
|
package/src/Order.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
// Order Ticket Schema
|
|
4
|
+
const orderTicketSchema = new mongoose.Schema({
|
|
5
|
+
ticketVariantId: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
required: true
|
|
8
|
+
},
|
|
9
|
+
variantName: {
|
|
10
|
+
type: String,
|
|
11
|
+
required: true
|
|
12
|
+
},
|
|
13
|
+
quantity: {
|
|
14
|
+
type: Number,
|
|
15
|
+
required: true,
|
|
16
|
+
min: 1
|
|
17
|
+
},
|
|
18
|
+
pricePerTicket: {
|
|
19
|
+
type: Number,
|
|
20
|
+
required: true,
|
|
21
|
+
min: 0
|
|
22
|
+
},
|
|
23
|
+
subtotal: {
|
|
24
|
+
type: Number,
|
|
25
|
+
required: true,
|
|
26
|
+
min: 0
|
|
27
|
+
}
|
|
28
|
+
}, { _id: false });
|
|
29
|
+
|
|
30
|
+
// Pricing Schema
|
|
31
|
+
const pricingSchema = new mongoose.Schema({
|
|
32
|
+
subtotal: {
|
|
33
|
+
type: Number,
|
|
34
|
+
required: true,
|
|
35
|
+
min: 0
|
|
36
|
+
},
|
|
37
|
+
platformFee: {
|
|
38
|
+
type: Number,
|
|
39
|
+
required: true,
|
|
40
|
+
min: 0
|
|
41
|
+
},
|
|
42
|
+
paymentFee: {
|
|
43
|
+
type: Number,
|
|
44
|
+
required: true,
|
|
45
|
+
min: 0
|
|
46
|
+
},
|
|
47
|
+
total: {
|
|
48
|
+
type: Number,
|
|
49
|
+
required: true,
|
|
50
|
+
min: 0
|
|
51
|
+
},
|
|
52
|
+
currency: {
|
|
53
|
+
type: String,
|
|
54
|
+
required: true,
|
|
55
|
+
default: 'BDT'
|
|
56
|
+
},
|
|
57
|
+
hostPayout: {
|
|
58
|
+
type: Number,
|
|
59
|
+
required: true,
|
|
60
|
+
min: 0
|
|
61
|
+
}
|
|
62
|
+
}, { _id: false });
|
|
63
|
+
|
|
64
|
+
// Refund Schema
|
|
65
|
+
const refundSchema = new mongoose.Schema({
|
|
66
|
+
reason: {
|
|
67
|
+
type: String,
|
|
68
|
+
required: true,
|
|
69
|
+
enum: ['event_cancelled', 'user_request', 'fraud']
|
|
70
|
+
},
|
|
71
|
+
amount: {
|
|
72
|
+
type: Number,
|
|
73
|
+
required: true,
|
|
74
|
+
min: 0
|
|
75
|
+
},
|
|
76
|
+
refundedAt: {
|
|
77
|
+
type: Date,
|
|
78
|
+
default: Date.now
|
|
79
|
+
},
|
|
80
|
+
stripeRefundId: String
|
|
81
|
+
}, { _id: false });
|
|
82
|
+
|
|
83
|
+
// Main Order Schema
|
|
84
|
+
const orderSchema = new mongoose.Schema({
|
|
85
|
+
orderNumber: {
|
|
86
|
+
type: String,
|
|
87
|
+
required: true,
|
|
88
|
+
unique: true,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// WHO
|
|
92
|
+
userId: {
|
|
93
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
94
|
+
required: true,
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// WHAT
|
|
98
|
+
eventId: {
|
|
99
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
100
|
+
required: true,
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
paymentId: {
|
|
104
|
+
type: String,
|
|
105
|
+
required: false,
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
tickets: [orderTicketSchema],
|
|
109
|
+
|
|
110
|
+
// MONEY
|
|
111
|
+
pricing: pricingSchema,
|
|
112
|
+
|
|
113
|
+
// STATUS
|
|
114
|
+
status: {
|
|
115
|
+
type: String,
|
|
116
|
+
required: true,
|
|
117
|
+
enum: ['pending', 'confirmed', 'cancelled', 'refunded'],
|
|
118
|
+
default: 'pending'
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
requiresManualReview: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: false
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
manualReviewReason: {
|
|
127
|
+
type: String,
|
|
128
|
+
default: null
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
paymentMethod: {
|
|
132
|
+
type: String,
|
|
133
|
+
required: true,
|
|
134
|
+
enum: ['card', 'bkash', 'bank_transfer']
|
|
135
|
+
},
|
|
136
|
+
paymentStatus: {
|
|
137
|
+
type: String,
|
|
138
|
+
required: true,
|
|
139
|
+
enum: ['pending', 'succeeded', 'failed'],
|
|
140
|
+
default: 'pending'
|
|
141
|
+
},
|
|
142
|
+
paidAt: Date,
|
|
143
|
+
|
|
144
|
+
// TICKETS ISSUED
|
|
145
|
+
ticketIds: [{
|
|
146
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
147
|
+
ref: 'Ticket'
|
|
148
|
+
}],
|
|
149
|
+
|
|
150
|
+
// LIFECYCLE
|
|
151
|
+
createdAt: {
|
|
152
|
+
type: Date,
|
|
153
|
+
default: Date.now,
|
|
154
|
+
index: true
|
|
155
|
+
},
|
|
156
|
+
expiresAt: {
|
|
157
|
+
type: Date,
|
|
158
|
+
required: true
|
|
159
|
+
},
|
|
160
|
+
confirmedAt: Date,
|
|
161
|
+
cancelledAt: Date,
|
|
162
|
+
refundedAt: Date,
|
|
163
|
+
reminderSentAt: Date,
|
|
164
|
+
|
|
165
|
+
// CONTACT
|
|
166
|
+
buyerEmail: {
|
|
167
|
+
type: String,
|
|
168
|
+
required: true
|
|
169
|
+
},
|
|
170
|
+
buyerPhone: String,
|
|
171
|
+
|
|
172
|
+
// AUDIT
|
|
173
|
+
ipAddress: String,
|
|
174
|
+
userAgent: String,
|
|
175
|
+
|
|
176
|
+
// REFUND (if applicable)
|
|
177
|
+
refund: refundSchema
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Indexes for performance
|
|
181
|
+
orderSchema.index({ userId: 1, createdAt: -1 });
|
|
182
|
+
orderSchema.index({ eventId: 1, status: 1 });
|
|
183
|
+
orderSchema.index({ status: 1, expiresAt: 1 });
|
|
184
|
+
|
|
185
|
+
// Auto-generate order number
|
|
186
|
+
orderSchema.pre('save', function(next) {
|
|
187
|
+
if (this.isNew && !this.orderNumber) {
|
|
188
|
+
const timestamp = Date.now().toString().slice(-6);
|
|
189
|
+
const random = Math.random().toString(36).substring(2, 8).toUpperCase();
|
|
190
|
+
this.orderNumber = `ORD-${timestamp}-${random}`;
|
|
191
|
+
}
|
|
192
|
+
next();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Update updatedAt on save
|
|
196
|
+
orderSchema.pre('save', function(next) {
|
|
197
|
+
// Note: We don't have updatedAt field, but could add if needed
|
|
198
|
+
next();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export default orderSchema;
|
package/src/Payment.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
// Payment schema for transaction logging
|
|
4
|
+
const paymentSchema = new mongoose.Schema({
|
|
5
|
+
// LINKS
|
|
6
|
+
orderId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true }, // Which order
|
|
7
|
+
userId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true }, // Who paid
|
|
8
|
+
|
|
9
|
+
paymentId: { type: String, required: true, unique: true, index: true }, // Stripe PI (unique)
|
|
10
|
+
|
|
11
|
+
// MONEY
|
|
12
|
+
amount: { type: Number, required: true },
|
|
13
|
+
currency: { type: String, required: true, default: 'BDT' },
|
|
14
|
+
|
|
15
|
+
// STATUS
|
|
16
|
+
status: {
|
|
17
|
+
type: String,
|
|
18
|
+
required: true,
|
|
19
|
+
enum: ['pending', 'succeeded', 'failed', 'refunded'],
|
|
20
|
+
default: 'pending'
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// METADATA
|
|
24
|
+
paymentMethod: { type: String, required: true, enum: ['bkash', 'card'] },
|
|
25
|
+
last4: String, // Card last 4 digits (for receipt)
|
|
26
|
+
brand: { type: String, enum: ['visa', 'mastercard', 'amex', 'discover'] }, // Card brand
|
|
27
|
+
|
|
28
|
+
// FAILURE (if failed)
|
|
29
|
+
failureCode: String, // "insufficient_funds", "card_declined", etc.
|
|
30
|
+
failureMessage: String,
|
|
31
|
+
|
|
32
|
+
// REFUND (if refunded)
|
|
33
|
+
refundId: String, // Stripe refund ID
|
|
34
|
+
refundAmount: Number,
|
|
35
|
+
refundedAt: Date,
|
|
36
|
+
|
|
37
|
+
// TIMESTAMPS
|
|
38
|
+
createdAt: { type: Date, default: Date.now },
|
|
39
|
+
succeededAt: Date,
|
|
40
|
+
failedAt: Date,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Indexes for performance
|
|
44
|
+
paymentSchema.index({ orderId: 1 });
|
|
45
|
+
paymentSchema.index({ userId: 1, createdAt: -1 });
|
|
46
|
+
paymentSchema.index({ status: 1, createdAt: -1 });
|
|
47
|
+
|
|
48
|
+
// Pre-save middleware to set timestamps based on status
|
|
49
|
+
paymentSchema.pre('save', function(next) {
|
|
50
|
+
if (this.isModified('status')) {
|
|
51
|
+
const now = new Date();
|
|
52
|
+
if (this.status === 'succeeded' && !this.succeededAt) {
|
|
53
|
+
this.succeededAt = now;
|
|
54
|
+
} else if (this.status === 'failed' && !this.failedAt) {
|
|
55
|
+
this.failedAt = now;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
next();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default paymentSchema;
|
package/src/Ticket.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
// Ticket schema for individual issued tickets
|
|
4
|
+
const ticketSchema = new mongoose.Schema({
|
|
5
|
+
ticketNumber: { type: String, required: true, unique: true, index: true }, // Human-readable: "TKT-EVT123-A3K9-01"
|
|
6
|
+
|
|
7
|
+
// BELONGS TO
|
|
8
|
+
orderId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true }, // Parent order
|
|
9
|
+
eventId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true }, // Which event
|
|
10
|
+
userId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true }, // Owner
|
|
11
|
+
ticketVariantId: { type: mongoose.Schema.Types.ObjectId, required: true }, // Which type (VIP, GA, etc)
|
|
12
|
+
|
|
13
|
+
// DETAILS (denormalized for speed)
|
|
14
|
+
eventTitle: { type: String, required: true },
|
|
15
|
+
eventDate: { type: Date, required: true },
|
|
16
|
+
eventVenue: { type: String, required: true },
|
|
17
|
+
ticketType: { type: String, required: true }, // "VIP Pass"
|
|
18
|
+
price: { type: Number, required: true },
|
|
19
|
+
|
|
20
|
+
// QR CODE
|
|
21
|
+
qrCode: { type: String, unique: true, required: true }, // Unique hash: SHA256(ticketId + secret)
|
|
22
|
+
qrCodeUrl: { type: String, required: true }, // ImageKit/S3 URL to QR image
|
|
23
|
+
|
|
24
|
+
// STATUS
|
|
25
|
+
status: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
enum: ['valid', 'used', 'cancelled', 'refunded', 'transferred'],
|
|
29
|
+
default: 'valid',
|
|
30
|
+
index: true
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// CHECK-IN
|
|
34
|
+
checkInStatus: {
|
|
35
|
+
type: String,
|
|
36
|
+
required: true,
|
|
37
|
+
enum: ['not_checked_in', 'checked_in'],
|
|
38
|
+
default: 'not_checked_in'
|
|
39
|
+
},
|
|
40
|
+
checkedInAt: Date,
|
|
41
|
+
checkedInBy: mongoose.Schema.Types.ObjectId, // Staff/scanner user ID
|
|
42
|
+
|
|
43
|
+
// TRANSFER (if user transfers ticket to friend)
|
|
44
|
+
transferredTo: mongoose.Schema.Types.ObjectId, // New owner user ID
|
|
45
|
+
transferredAt: Date,
|
|
46
|
+
|
|
47
|
+
// LIFECYCLE
|
|
48
|
+
issuedAt: { type: Date, default: Date.now }, // When ticket was created
|
|
49
|
+
validUntil: { type: Date, required: true }, // Event end date (after this, ticket expires)
|
|
50
|
+
|
|
51
|
+
// SECURITY
|
|
52
|
+
secretHash: { type: String, required: true }, // Internal validation hash (never exposed)
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// Pre-save middleware to set default validUntil if not provided
|
|
57
|
+
ticketSchema.pre('save', function(next) {
|
|
58
|
+
if (this.isNew && !this.validUntil && this.eventDate) {
|
|
59
|
+
// Set validUntil to end of event day if eventDate is provided
|
|
60
|
+
const eventEnd = new Date(this.eventDate);
|
|
61
|
+
eventEnd.setHours(23, 59, 59, 999); // End of the event day
|
|
62
|
+
this.validUntil = eventEnd;
|
|
63
|
+
}
|
|
64
|
+
next();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Virtual for checking if ticket is expired
|
|
68
|
+
ticketSchema.virtual('isExpired').get(function() {
|
|
69
|
+
return new Date() > this.validUntil;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Method to check if ticket can be used
|
|
73
|
+
ticketSchema.methods.canBeUsed = function() {
|
|
74
|
+
return this.status === 'valid' &&
|
|
75
|
+
this.checkInStatus === 'not_checked_in' &&
|
|
76
|
+
!this.isExpired;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Method to mark as used (check-in)
|
|
80
|
+
ticketSchema.methods.checkIn = function(staffUserId: string) {
|
|
81
|
+
if (!this.canBeUsed()) {
|
|
82
|
+
throw new Error('Ticket cannot be checked in');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.checkInStatus = 'checked_in';
|
|
86
|
+
this.checkedInAt = new Date();
|
|
87
|
+
this.checkedInBy = new mongoose.Types.ObjectId(staffUserId);
|
|
88
|
+
|
|
89
|
+
return this.save();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Method to transfer ticket
|
|
93
|
+
ticketSchema.methods.transfer = function(newOwnerId: string) {
|
|
94
|
+
if (this.status !== 'valid') {
|
|
95
|
+
throw new Error('Only valid tickets can be transferred');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.transferredTo = new mongoose.Types.ObjectId(newOwnerId);
|
|
99
|
+
this.transferredAt = new Date();
|
|
100
|
+
this.userId = new mongoose.Types.ObjectId(newOwnerId); // Update ownership
|
|
101
|
+
|
|
102
|
+
return this.save();
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export default ticketSchema;
|
package/src/User.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
const userSchema = new mongoose.Schema({
|
|
4
|
+
firstName: {
|
|
5
|
+
type: String,
|
|
6
|
+
required: true
|
|
7
|
+
},
|
|
8
|
+
lastName: {
|
|
9
|
+
type: String,
|
|
10
|
+
required: true
|
|
11
|
+
},
|
|
12
|
+
email: {
|
|
13
|
+
type: String,
|
|
14
|
+
required: true,
|
|
15
|
+
unique: true
|
|
16
|
+
},
|
|
17
|
+
password: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: false
|
|
20
|
+
},
|
|
21
|
+
organization: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: false
|
|
24
|
+
},
|
|
25
|
+
role: {
|
|
26
|
+
type: String,
|
|
27
|
+
enum: ['user', 'host', 'admin'],
|
|
28
|
+
default: 'user'
|
|
29
|
+
},
|
|
30
|
+
refreshToken: {
|
|
31
|
+
type: String,
|
|
32
|
+
required: false
|
|
33
|
+
},
|
|
34
|
+
provider: {
|
|
35
|
+
type: String,
|
|
36
|
+
enum: ['google', 'local'],
|
|
37
|
+
default: 'local'
|
|
38
|
+
},
|
|
39
|
+
googleId: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: false
|
|
42
|
+
}
|
|
43
|
+
}, {
|
|
44
|
+
timestamps: true
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export default userSchema;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Shared database models for Pinecone microservices
|
|
2
|
+
export { default as eventSchema } from './Event';
|
|
3
|
+
export { default as ticketSchema } from './Ticket';
|
|
4
|
+
export { default as mediaSchema } from './Media';
|
|
5
|
+
export { default as bkashSchema } from './Bkash';
|
|
6
|
+
export { default as paymentSchema } from './Payment';
|
|
7
|
+
export { default as orderSchema } from './Order';
|
|
8
|
+
export { default as userSchema } from './User';
|
|
9
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
16
|
+
}
|