@mac777/project-pinecone-models 1.0.1 → 1.0.3
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 +69 -51
- package/dist/Event.js +34 -12
- package/package.json +1 -5
- package/src/Bkash.ts +10 -0
- package/src/Event.ts +301 -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
|
@@ -4,7 +4,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
4
4
|
createdAt: NativeDate;
|
|
5
5
|
updatedAt: NativeDate;
|
|
6
6
|
status: "approved" | "rejected" | "draft" | "pending_approval" | "published" | "live" | "ended" | "cancelled";
|
|
7
|
-
visibility: "public" | "private" | "unlisted";
|
|
8
7
|
hostId: mongoose.Types.ObjectId;
|
|
9
8
|
title: string;
|
|
10
9
|
categories: string[];
|
|
@@ -12,14 +11,14 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
12
11
|
languages: string[];
|
|
13
12
|
tickets: mongoose.Types.DocumentArray<{
|
|
14
13
|
name: string;
|
|
15
|
-
status: "active" | "sold_out" | "inactive";
|
|
16
14
|
quantity: number;
|
|
17
15
|
sold: number;
|
|
18
|
-
remaining: number;
|
|
19
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
20
16
|
reserved: number;
|
|
17
|
+
isActive: boolean;
|
|
18
|
+
isVisible: boolean;
|
|
19
|
+
wristbandColor: string;
|
|
21
20
|
benefits: string[];
|
|
22
|
-
|
|
21
|
+
tier: string;
|
|
23
22
|
price?: {
|
|
24
23
|
amount: number;
|
|
25
24
|
currency: string;
|
|
@@ -32,17 +31,16 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
32
31
|
minPerOrder: number;
|
|
33
32
|
maxPerOrder: number;
|
|
34
33
|
} | null | undefined;
|
|
35
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
36
34
|
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
37
35
|
name: string;
|
|
38
|
-
status: "active" | "sold_out" | "inactive";
|
|
39
36
|
quantity: number;
|
|
40
37
|
sold: number;
|
|
41
|
-
remaining: number;
|
|
42
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
43
38
|
reserved: number;
|
|
39
|
+
isActive: boolean;
|
|
40
|
+
isVisible: boolean;
|
|
41
|
+
wristbandColor: string;
|
|
44
42
|
benefits: string[];
|
|
45
|
-
|
|
43
|
+
tier: string;
|
|
46
44
|
price?: {
|
|
47
45
|
amount: number;
|
|
48
46
|
currency: string;
|
|
@@ -55,17 +53,16 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
55
53
|
minPerOrder: number;
|
|
56
54
|
maxPerOrder: number;
|
|
57
55
|
} | null | undefined;
|
|
58
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
59
56
|
}> & {
|
|
60
57
|
name: string;
|
|
61
|
-
status: "active" | "sold_out" | "inactive";
|
|
62
58
|
quantity: number;
|
|
63
59
|
sold: number;
|
|
64
|
-
remaining: number;
|
|
65
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
66
60
|
reserved: number;
|
|
61
|
+
isActive: boolean;
|
|
62
|
+
isVisible: boolean;
|
|
63
|
+
wristbandColor: string;
|
|
67
64
|
benefits: string[];
|
|
68
|
-
|
|
65
|
+
tier: string;
|
|
69
66
|
price?: {
|
|
70
67
|
amount: number;
|
|
71
68
|
currency: string;
|
|
@@ -78,11 +75,11 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
78
75
|
minPerOrder: number;
|
|
79
76
|
maxPerOrder: number;
|
|
80
77
|
} | null | undefined;
|
|
81
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
82
78
|
}>;
|
|
83
79
|
termsAccepted: boolean;
|
|
84
80
|
legalPermissionAccepted: boolean;
|
|
85
81
|
platformTermsAccepted: boolean;
|
|
82
|
+
visibility: "public" | "private" | "unlisted";
|
|
86
83
|
history: mongoose.Types.DocumentArray<{
|
|
87
84
|
timestamp: NativeDate;
|
|
88
85
|
action?: string | null | undefined;
|
|
@@ -195,15 +192,20 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
195
192
|
publishedAt?: NativeDate | null | undefined;
|
|
196
193
|
metrics?: {
|
|
197
194
|
views: number;
|
|
195
|
+
uniqueViews: number;
|
|
196
|
+
totalOrders: number;
|
|
198
197
|
ticketsSold: number;
|
|
199
198
|
revenue: number;
|
|
200
199
|
checkIns: number;
|
|
200
|
+
uniqueViewers: string[];
|
|
201
201
|
reviewCount: number;
|
|
202
|
+
conversionRate: number;
|
|
203
|
+
lastViewedAt?: NativeDate | null | undefined;
|
|
202
204
|
averageRating?: number | null | undefined;
|
|
203
205
|
} | null | undefined;
|
|
204
206
|
pricing?: {
|
|
207
|
+
currency: string;
|
|
205
208
|
platformFee: number;
|
|
206
|
-
currency?: string | null | undefined;
|
|
207
209
|
paymentProcessingFee?: number | null | undefined;
|
|
208
210
|
} | null | undefined;
|
|
209
211
|
payout?: {
|
|
@@ -244,6 +246,8 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
244
246
|
isFlagged: boolean;
|
|
245
247
|
suspendedUntil?: NativeDate | null | undefined;
|
|
246
248
|
banReason?: string | null | undefined;
|
|
249
|
+
suspendedAt?: NativeDate | null | undefined;
|
|
250
|
+
suspensionReason?: string | null | undefined;
|
|
247
251
|
} | null | undefined;
|
|
248
252
|
seo?: {
|
|
249
253
|
keywords: string[];
|
|
@@ -255,6 +259,8 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
255
259
|
isFeatured: boolean;
|
|
256
260
|
isPremium: boolean;
|
|
257
261
|
badges: string[];
|
|
262
|
+
featuredPriority: number;
|
|
263
|
+
featuredAt?: NativeDate | null | undefined;
|
|
258
264
|
} | null | undefined;
|
|
259
265
|
submittedAt?: NativeDate | null | undefined;
|
|
260
266
|
deletedAt?: NativeDate | null | undefined;
|
|
@@ -265,7 +271,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
265
271
|
createdAt: NativeDate;
|
|
266
272
|
updatedAt: NativeDate;
|
|
267
273
|
status: "approved" | "rejected" | "draft" | "pending_approval" | "published" | "live" | "ended" | "cancelled";
|
|
268
|
-
visibility: "public" | "private" | "unlisted";
|
|
269
274
|
hostId: mongoose.Types.ObjectId;
|
|
270
275
|
title: string;
|
|
271
276
|
categories: string[];
|
|
@@ -273,14 +278,14 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
273
278
|
languages: string[];
|
|
274
279
|
tickets: mongoose.Types.DocumentArray<{
|
|
275
280
|
name: string;
|
|
276
|
-
status: "active" | "sold_out" | "inactive";
|
|
277
281
|
quantity: number;
|
|
278
282
|
sold: number;
|
|
279
|
-
remaining: number;
|
|
280
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
281
283
|
reserved: number;
|
|
284
|
+
isActive: boolean;
|
|
285
|
+
isVisible: boolean;
|
|
286
|
+
wristbandColor: string;
|
|
282
287
|
benefits: string[];
|
|
283
|
-
|
|
288
|
+
tier: string;
|
|
284
289
|
price?: {
|
|
285
290
|
amount: number;
|
|
286
291
|
currency: string;
|
|
@@ -293,17 +298,16 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
293
298
|
minPerOrder: number;
|
|
294
299
|
maxPerOrder: number;
|
|
295
300
|
} | null | undefined;
|
|
296
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
297
301
|
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
298
302
|
name: string;
|
|
299
|
-
status: "active" | "sold_out" | "inactive";
|
|
300
303
|
quantity: number;
|
|
301
304
|
sold: number;
|
|
302
|
-
remaining: number;
|
|
303
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
304
305
|
reserved: number;
|
|
306
|
+
isActive: boolean;
|
|
307
|
+
isVisible: boolean;
|
|
308
|
+
wristbandColor: string;
|
|
305
309
|
benefits: string[];
|
|
306
|
-
|
|
310
|
+
tier: string;
|
|
307
311
|
price?: {
|
|
308
312
|
amount: number;
|
|
309
313
|
currency: string;
|
|
@@ -316,17 +320,16 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
316
320
|
minPerOrder: number;
|
|
317
321
|
maxPerOrder: number;
|
|
318
322
|
} | null | undefined;
|
|
319
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
320
323
|
}> & {
|
|
321
324
|
name: string;
|
|
322
|
-
status: "active" | "sold_out" | "inactive";
|
|
323
325
|
quantity: number;
|
|
324
326
|
sold: number;
|
|
325
|
-
remaining: number;
|
|
326
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
327
327
|
reserved: number;
|
|
328
|
+
isActive: boolean;
|
|
329
|
+
isVisible: boolean;
|
|
330
|
+
wristbandColor: string;
|
|
328
331
|
benefits: string[];
|
|
329
|
-
|
|
332
|
+
tier: string;
|
|
330
333
|
price?: {
|
|
331
334
|
amount: number;
|
|
332
335
|
currency: string;
|
|
@@ -339,11 +342,11 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
339
342
|
minPerOrder: number;
|
|
340
343
|
maxPerOrder: number;
|
|
341
344
|
} | null | undefined;
|
|
342
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
343
345
|
}>;
|
|
344
346
|
termsAccepted: boolean;
|
|
345
347
|
legalPermissionAccepted: boolean;
|
|
346
348
|
platformTermsAccepted: boolean;
|
|
349
|
+
visibility: "public" | "private" | "unlisted";
|
|
347
350
|
history: mongoose.Types.DocumentArray<{
|
|
348
351
|
timestamp: NativeDate;
|
|
349
352
|
action?: string | null | undefined;
|
|
@@ -456,15 +459,20 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
456
459
|
publishedAt?: NativeDate | null | undefined;
|
|
457
460
|
metrics?: {
|
|
458
461
|
views: number;
|
|
462
|
+
uniqueViews: number;
|
|
463
|
+
totalOrders: number;
|
|
459
464
|
ticketsSold: number;
|
|
460
465
|
revenue: number;
|
|
461
466
|
checkIns: number;
|
|
467
|
+
uniqueViewers: string[];
|
|
462
468
|
reviewCount: number;
|
|
469
|
+
conversionRate: number;
|
|
470
|
+
lastViewedAt?: NativeDate | null | undefined;
|
|
463
471
|
averageRating?: number | null | undefined;
|
|
464
472
|
} | null | undefined;
|
|
465
473
|
pricing?: {
|
|
474
|
+
currency: string;
|
|
466
475
|
platformFee: number;
|
|
467
|
-
currency?: string | null | undefined;
|
|
468
476
|
paymentProcessingFee?: number | null | undefined;
|
|
469
477
|
} | null | undefined;
|
|
470
478
|
payout?: {
|
|
@@ -505,6 +513,8 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
505
513
|
isFlagged: boolean;
|
|
506
514
|
suspendedUntil?: NativeDate | null | undefined;
|
|
507
515
|
banReason?: string | null | undefined;
|
|
516
|
+
suspendedAt?: NativeDate | null | undefined;
|
|
517
|
+
suspensionReason?: string | null | undefined;
|
|
508
518
|
} | null | undefined;
|
|
509
519
|
seo?: {
|
|
510
520
|
keywords: string[];
|
|
@@ -516,6 +526,8 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
516
526
|
isFeatured: boolean;
|
|
517
527
|
isPremium: boolean;
|
|
518
528
|
badges: string[];
|
|
529
|
+
featuredPriority: number;
|
|
530
|
+
featuredAt?: NativeDate | null | undefined;
|
|
519
531
|
} | null | undefined;
|
|
520
532
|
submittedAt?: NativeDate | null | undefined;
|
|
521
533
|
deletedAt?: NativeDate | null | undefined;
|
|
@@ -526,7 +538,6 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
526
538
|
createdAt: NativeDate;
|
|
527
539
|
updatedAt: NativeDate;
|
|
528
540
|
status: "approved" | "rejected" | "draft" | "pending_approval" | "published" | "live" | "ended" | "cancelled";
|
|
529
|
-
visibility: "public" | "private" | "unlisted";
|
|
530
541
|
hostId: mongoose.Types.ObjectId;
|
|
531
542
|
title: string;
|
|
532
543
|
categories: string[];
|
|
@@ -534,14 +545,14 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
534
545
|
languages: string[];
|
|
535
546
|
tickets: mongoose.Types.DocumentArray<{
|
|
536
547
|
name: string;
|
|
537
|
-
status: "active" | "sold_out" | "inactive";
|
|
538
548
|
quantity: number;
|
|
539
549
|
sold: number;
|
|
540
|
-
remaining: number;
|
|
541
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
542
550
|
reserved: number;
|
|
551
|
+
isActive: boolean;
|
|
552
|
+
isVisible: boolean;
|
|
553
|
+
wristbandColor: string;
|
|
543
554
|
benefits: string[];
|
|
544
|
-
|
|
555
|
+
tier: string;
|
|
545
556
|
price?: {
|
|
546
557
|
amount: number;
|
|
547
558
|
currency: string;
|
|
@@ -554,17 +565,16 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
554
565
|
minPerOrder: number;
|
|
555
566
|
maxPerOrder: number;
|
|
556
567
|
} | null | undefined;
|
|
557
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
558
568
|
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
559
569
|
name: string;
|
|
560
|
-
status: "active" | "sold_out" | "inactive";
|
|
561
570
|
quantity: number;
|
|
562
571
|
sold: number;
|
|
563
|
-
remaining: number;
|
|
564
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
565
572
|
reserved: number;
|
|
573
|
+
isActive: boolean;
|
|
574
|
+
isVisible: boolean;
|
|
575
|
+
wristbandColor: string;
|
|
566
576
|
benefits: string[];
|
|
567
|
-
|
|
577
|
+
tier: string;
|
|
568
578
|
price?: {
|
|
569
579
|
amount: number;
|
|
570
580
|
currency: string;
|
|
@@ -577,17 +587,16 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
577
587
|
minPerOrder: number;
|
|
578
588
|
maxPerOrder: number;
|
|
579
589
|
} | null | undefined;
|
|
580
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
581
590
|
}> & {
|
|
582
591
|
name: string;
|
|
583
|
-
status: "active" | "sold_out" | "inactive";
|
|
584
592
|
quantity: number;
|
|
585
593
|
sold: number;
|
|
586
|
-
remaining: number;
|
|
587
|
-
visibility: "hidden" | "public" | "invite_only";
|
|
588
594
|
reserved: number;
|
|
595
|
+
isActive: boolean;
|
|
596
|
+
isVisible: boolean;
|
|
597
|
+
wristbandColor: string;
|
|
589
598
|
benefits: string[];
|
|
590
|
-
|
|
599
|
+
tier: string;
|
|
591
600
|
price?: {
|
|
592
601
|
amount: number;
|
|
593
602
|
currency: string;
|
|
@@ -600,11 +609,11 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
600
609
|
minPerOrder: number;
|
|
601
610
|
maxPerOrder: number;
|
|
602
611
|
} | null | undefined;
|
|
603
|
-
tier?: "early_bird" | "regular" | "vip" | null | undefined;
|
|
604
612
|
}>;
|
|
605
613
|
termsAccepted: boolean;
|
|
606
614
|
legalPermissionAccepted: boolean;
|
|
607
615
|
platformTermsAccepted: boolean;
|
|
616
|
+
visibility: "public" | "private" | "unlisted";
|
|
608
617
|
history: mongoose.Types.DocumentArray<{
|
|
609
618
|
timestamp: NativeDate;
|
|
610
619
|
action?: string | null | undefined;
|
|
@@ -717,15 +726,20 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
717
726
|
publishedAt?: NativeDate | null | undefined;
|
|
718
727
|
metrics?: {
|
|
719
728
|
views: number;
|
|
729
|
+
uniqueViews: number;
|
|
730
|
+
totalOrders: number;
|
|
720
731
|
ticketsSold: number;
|
|
721
732
|
revenue: number;
|
|
722
733
|
checkIns: number;
|
|
734
|
+
uniqueViewers: string[];
|
|
723
735
|
reviewCount: number;
|
|
736
|
+
conversionRate: number;
|
|
737
|
+
lastViewedAt?: NativeDate | null | undefined;
|
|
724
738
|
averageRating?: number | null | undefined;
|
|
725
739
|
} | null | undefined;
|
|
726
740
|
pricing?: {
|
|
741
|
+
currency: string;
|
|
727
742
|
platformFee: number;
|
|
728
|
-
currency?: string | null | undefined;
|
|
729
743
|
paymentProcessingFee?: number | null | undefined;
|
|
730
744
|
} | null | undefined;
|
|
731
745
|
payout?: {
|
|
@@ -766,6 +780,8 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
766
780
|
isFlagged: boolean;
|
|
767
781
|
suspendedUntil?: NativeDate | null | undefined;
|
|
768
782
|
banReason?: string | null | undefined;
|
|
783
|
+
suspendedAt?: NativeDate | null | undefined;
|
|
784
|
+
suspensionReason?: string | null | undefined;
|
|
769
785
|
} | null | undefined;
|
|
770
786
|
seo?: {
|
|
771
787
|
keywords: string[];
|
|
@@ -777,6 +793,8 @@ declare const eventSchema: mongoose.Schema<any, mongoose.Model<any, any, any, an
|
|
|
777
793
|
isFeatured: boolean;
|
|
778
794
|
isPremium: boolean;
|
|
779
795
|
badges: string[];
|
|
796
|
+
featuredPriority: number;
|
|
797
|
+
featuredAt?: NativeDate | null | undefined;
|
|
780
798
|
} | null | undefined;
|
|
781
799
|
submittedAt?: NativeDate | null | undefined;
|
|
782
800
|
deletedAt?: NativeDate | null | undefined;
|
package/dist/Event.js
CHANGED
|
@@ -77,24 +77,22 @@ const salesWindowSchema = new mongoose_1.default.Schema({
|
|
|
77
77
|
startDate: Date,
|
|
78
78
|
endDate: Date,
|
|
79
79
|
}, { _id: false });
|
|
80
|
-
const limitsSchema = new mongoose_1.default.Schema({
|
|
81
|
-
minPerOrder: { type: Number, default: 1 },
|
|
82
|
-
maxPerOrder: { type: Number, default: 10 },
|
|
83
|
-
}, { _id: false });
|
|
84
80
|
const ticketSchema = new mongoose_1.default.Schema({
|
|
85
81
|
name: { type: String, required: true },
|
|
86
|
-
description: String,
|
|
87
82
|
price: priceSchema,
|
|
88
83
|
quantity: { type: Number, required: true },
|
|
89
84
|
sold: { type: Number, default: 0 },
|
|
90
|
-
remaining: { type: Number, default: function () { return this.quantity - this.sold; } },
|
|
91
|
-
salesWindow: salesWindowSchema,
|
|
92
|
-
limits: limitsSchema,
|
|
93
|
-
visibility: { type: String, enum: ['public', 'hidden', 'invite_only'], default: 'public' },
|
|
94
85
|
reserved: { type: Number, default: 0 },
|
|
95
|
-
|
|
86
|
+
isActive: { type: Boolean, default: true },
|
|
87
|
+
isVisible: { type: Boolean, default: true },
|
|
88
|
+
wristbandColor: { type: String, default: '#000000' },
|
|
89
|
+
limits: {
|
|
90
|
+
minPerOrder: { type: Number, default: 1 },
|
|
91
|
+
maxPerOrder: { type: Number, default: 10 },
|
|
92
|
+
},
|
|
96
93
|
benefits: [String],
|
|
97
|
-
tier: { type: String,
|
|
94
|
+
tier: { type: String, default: 'regular' },
|
|
95
|
+
salesWindow: salesWindowSchema,
|
|
98
96
|
});
|
|
99
97
|
const scheduleSchema = new mongoose_1.default.Schema({
|
|
100
98
|
startDate: { type: Date, required: true },
|
|
@@ -105,16 +103,25 @@ const scheduleSchema = new mongoose_1.default.Schema({
|
|
|
105
103
|
}, { _id: false });
|
|
106
104
|
const metricsSchema = new mongoose_1.default.Schema({
|
|
107
105
|
views: { type: Number, default: 0 },
|
|
106
|
+
uniqueViews: { type: Number, default: 0 },
|
|
107
|
+
totalOrders: { type: Number, default: 0 },
|
|
108
108
|
ticketsSold: { type: Number, default: 0 },
|
|
109
109
|
revenue: { type: Number, default: 0 },
|
|
110
110
|
checkIns: { type: Number, default: 0 },
|
|
111
|
+
uniqueViewers: [String],
|
|
112
|
+
lastViewedAt: Date, // for trending calculation
|
|
113
|
+
// --- For Review ---
|
|
111
114
|
averageRating: Number,
|
|
112
115
|
reviewCount: { type: Number, default: 0 },
|
|
116
|
+
conversionRate: { type: Number, default: 0 }, // (ticketsSold / views) * 100
|
|
113
117
|
}, { _id: false });
|
|
114
118
|
const pricingSchema = new mongoose_1.default.Schema({
|
|
115
119
|
platformFee: { type: Number, default: 5 },
|
|
116
120
|
paymentProcessingFee: Number,
|
|
117
|
-
currency:
|
|
121
|
+
currency: {
|
|
122
|
+
type: String,
|
|
123
|
+
default: 'BDT'
|
|
124
|
+
},
|
|
118
125
|
}, { _id: false });
|
|
119
126
|
const payoutSchema = new mongoose_1.default.Schema({
|
|
120
127
|
status: { type: String, enum: ['pending', 'scheduled', 'completed'] },
|
|
@@ -144,6 +151,8 @@ const moderationSchema = new mongoose_1.default.Schema({
|
|
|
144
151
|
flags: [flagSchema],
|
|
145
152
|
suspendedUntil: Date,
|
|
146
153
|
banReason: String,
|
|
154
|
+
suspendedAt: Date,
|
|
155
|
+
suspensionReason: String,
|
|
147
156
|
}, { _id: false });
|
|
148
157
|
const seoSchema = new mongoose_1.default.Schema({
|
|
149
158
|
metaTitle: String,
|
|
@@ -155,6 +164,8 @@ const featuresSchema = new mongoose_1.default.Schema({
|
|
|
155
164
|
isFeatured: { type: Boolean, default: false },
|
|
156
165
|
isPremium: { type: Boolean, default: false },
|
|
157
166
|
badges: [String],
|
|
167
|
+
featuredAt: Date, // timestamp when featured
|
|
168
|
+
featuredPriority: { type: Number, default: 0 }, // for ordering featured events
|
|
158
169
|
}, { _id: false });
|
|
159
170
|
const historySchema = new mongoose_1.default.Schema({
|
|
160
171
|
action: String,
|
|
@@ -221,6 +232,17 @@ eventSchema.index({ hostId: 1 });
|
|
|
221
232
|
eventSchema.index({ 'schedule.startDate': 1 });
|
|
222
233
|
eventSchema.index({ type: 1 });
|
|
223
234
|
eventSchema.index({ categories: 1 });
|
|
235
|
+
// New compound indexes for optimized queries
|
|
236
|
+
// For trending events (recent views + high sales)
|
|
237
|
+
eventSchema.index({ 'metrics.lastViewedAt': -1, 'metrics.ticketsSold': -1 });
|
|
238
|
+
// For featured events
|
|
239
|
+
eventSchema.index({ 'features.isFeatured': 1, 'features.featuredPriority': -1 });
|
|
240
|
+
// For filtering by status + visibility + date
|
|
241
|
+
eventSchema.index({ status: 1, visibility: 1, 'schedule.startDate': 1 });
|
|
242
|
+
// For location-based queries with status
|
|
243
|
+
eventSchema.index({ 'venue.address.city': 1, status: 1 });
|
|
244
|
+
// For host's events with status filter
|
|
245
|
+
eventSchema.index({ hostId: 1, status: 1, updatedAt: -1 });
|
|
224
246
|
// Validation: sum(tickets.quantity) <= venue.capacity
|
|
225
247
|
eventSchema.pre('save', function (next) {
|
|
226
248
|
if (this.tickets && this.tickets.length > 0 && this.venue) {
|
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.3",
|
|
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,301 @@
|
|
|
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 ticketSchema = new mongoose.Schema({
|
|
86
|
+
name: { type: String, required: true },
|
|
87
|
+
price: priceSchema,
|
|
88
|
+
quantity: { type: Number, required: true },
|
|
89
|
+
sold: { type: Number, default: 0 },
|
|
90
|
+
reserved: { type: Number, default: 0 },
|
|
91
|
+
isActive: { type: Boolean, default: true },
|
|
92
|
+
isVisible: { type: Boolean, default: true },
|
|
93
|
+
wristbandColor: { type: String, default: '#000000' },
|
|
94
|
+
limits: {
|
|
95
|
+
minPerOrder: { type: Number, default: 1 },
|
|
96
|
+
maxPerOrder: { type: Number, default: 10 },
|
|
97
|
+
},
|
|
98
|
+
benefits: [String],
|
|
99
|
+
tier: { type: String, default: 'regular' },
|
|
100
|
+
salesWindow: salesWindowSchema,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const scheduleSchema = new mongoose.Schema({
|
|
104
|
+
startDate: { type: Date, required: true },
|
|
105
|
+
endDate: { type: Date, required: true },
|
|
106
|
+
timezone: String,
|
|
107
|
+
isMultiDay: Boolean,
|
|
108
|
+
doors: String,
|
|
109
|
+
}, { _id: false });
|
|
110
|
+
|
|
111
|
+
const metricsSchema = new mongoose.Schema({
|
|
112
|
+
views: { type: Number, default: 0 },
|
|
113
|
+
uniqueViews: { type: Number, default: 0 },
|
|
114
|
+
totalOrders: { type: Number, default: 0 },
|
|
115
|
+
ticketsSold: { type: Number, default: 0 },
|
|
116
|
+
revenue: { type: Number, default: 0 },
|
|
117
|
+
checkIns: { type: Number, default: 0 },
|
|
118
|
+
uniqueViewers: [String],
|
|
119
|
+
lastViewedAt: Date, // for trending calculation
|
|
120
|
+
|
|
121
|
+
// --- For Review ---
|
|
122
|
+
averageRating: Number,
|
|
123
|
+
reviewCount: { type: Number, default: 0 },
|
|
124
|
+
conversionRate: { type: Number, default: 0 }, // (ticketsSold / views) * 100
|
|
125
|
+
}, { _id: false });
|
|
126
|
+
|
|
127
|
+
const pricingSchema = new mongoose.Schema({
|
|
128
|
+
platformFee: { type: Number, default: 5 },
|
|
129
|
+
paymentProcessingFee: Number,
|
|
130
|
+
currency: {
|
|
131
|
+
type: String,
|
|
132
|
+
default: 'BDT'
|
|
133
|
+
},
|
|
134
|
+
}, { _id: false });
|
|
135
|
+
|
|
136
|
+
const payoutSchema = new mongoose.Schema({
|
|
137
|
+
status: { type: String, enum: ['pending', 'scheduled', 'completed'] },
|
|
138
|
+
amount: Number,
|
|
139
|
+
scheduledDate: Date,
|
|
140
|
+
paidAt: Date,
|
|
141
|
+
stripePayoutId: String,
|
|
142
|
+
}, { _id: false });
|
|
143
|
+
|
|
144
|
+
const cancellationSchema = new mongoose.Schema({
|
|
145
|
+
allowed: { type: Boolean, default: true },
|
|
146
|
+
refundPercentage: { type: Number, default: 100 },
|
|
147
|
+
deadlineHours: Number,
|
|
148
|
+
}, { _id: false });
|
|
149
|
+
|
|
150
|
+
const policiesSchema = new mongoose.Schema({
|
|
151
|
+
cancellation: cancellationSchema,
|
|
152
|
+
transferable: { type: Boolean, default: false },
|
|
153
|
+
}, { _id: false });
|
|
154
|
+
|
|
155
|
+
const flagSchema = new mongoose.Schema({
|
|
156
|
+
reportedBy: mongoose.Schema.Types.ObjectId,
|
|
157
|
+
reason: String,
|
|
158
|
+
description: String,
|
|
159
|
+
createdAt: { type: Date, default: Date.now },
|
|
160
|
+
status: { type: String, enum: ['pending', 'resolved', 'dismissed'], default: 'pending' },
|
|
161
|
+
}, { _id: false });
|
|
162
|
+
|
|
163
|
+
const moderationSchema = new mongoose.Schema({
|
|
164
|
+
isFlagged: { type: Boolean, default: false },
|
|
165
|
+
flags: [flagSchema],
|
|
166
|
+
suspendedUntil: Date,
|
|
167
|
+
banReason: String,
|
|
168
|
+
suspendedAt: Date,
|
|
169
|
+
suspensionReason: String,
|
|
170
|
+
}, { _id: false });
|
|
171
|
+
|
|
172
|
+
const seoSchema = new mongoose.Schema({
|
|
173
|
+
metaTitle: String,
|
|
174
|
+
metaDescription: String,
|
|
175
|
+
keywords: [String],
|
|
176
|
+
ogImage: String,
|
|
177
|
+
}, { _id: false });
|
|
178
|
+
|
|
179
|
+
const featuresSchema = new mongoose.Schema({
|
|
180
|
+
isFeatured: { type: Boolean, default: false },
|
|
181
|
+
isPremium: { type: Boolean, default: false },
|
|
182
|
+
badges: [String],
|
|
183
|
+
featuredAt: Date, // timestamp when featured
|
|
184
|
+
featuredPriority: { type: Number, default: 0 }, // for ordering featured events
|
|
185
|
+
}, { _id: false });
|
|
186
|
+
|
|
187
|
+
const historySchema = new mongoose.Schema({
|
|
188
|
+
action: String,
|
|
189
|
+
performedBy: mongoose.Schema.Types.ObjectId,
|
|
190
|
+
timestamp: { type: Date, default: Date.now },
|
|
191
|
+
changes: mongoose.Schema.Types.Mixed,
|
|
192
|
+
}, { _id: false });
|
|
193
|
+
|
|
194
|
+
const eventSchema = new mongoose.Schema({
|
|
195
|
+
hostId: { type: mongoose.Schema.Types.ObjectId, required: true },
|
|
196
|
+
slug: String,
|
|
197
|
+
|
|
198
|
+
// STEP 1: BASICS
|
|
199
|
+
title: { type: String, required: true, minlength: 10, maxlength: 100 },
|
|
200
|
+
type: { type: String, enum: ['concert', 'sports', 'conference', 'festival', 'theater', 'comedy', 'networking', 'workshop', 'other'], required: true },
|
|
201
|
+
categories: [{ type: String }],
|
|
202
|
+
tagline: { type: String, maxlength: 150 },
|
|
203
|
+
|
|
204
|
+
// STEP 2: DETAILS
|
|
205
|
+
description: { type: String, minlength: 50, maxlength: 5000 },
|
|
206
|
+
highlights: [{ type: String }],
|
|
207
|
+
languages: [String],
|
|
208
|
+
ageRestriction: { type: String, enum: ['all_ages', '18+', '21+'] },
|
|
209
|
+
dressCode: String,
|
|
210
|
+
|
|
211
|
+
media: mediaSchema,
|
|
212
|
+
|
|
213
|
+
// STEP 3: DATE, TIME & LOCATION
|
|
214
|
+
schedule: scheduleSchema,
|
|
215
|
+
venue: venueSchema,
|
|
216
|
+
|
|
217
|
+
// STEP 4: VERIFICATION
|
|
218
|
+
organizer: organizerSchema,
|
|
219
|
+
verification: verificationSchema,
|
|
220
|
+
|
|
221
|
+
// STEP 5: TICKETS
|
|
222
|
+
tickets: [ticketSchema],
|
|
223
|
+
termsAccepted: { type: Boolean, default: false },
|
|
224
|
+
legalPermissionAccepted: { type: Boolean, default: false },
|
|
225
|
+
platformTermsAccepted: { type: Boolean, default: false },
|
|
226
|
+
// STATUS & WORKFLOW
|
|
227
|
+
status: { type: String, enum: ['draft', 'pending_approval', 'approved', 'rejected', 'published', 'live', 'ended', 'cancelled'], default: 'draft' },
|
|
228
|
+
rejectionReason: String,
|
|
229
|
+
visibility: { type: String, enum: ['public', 'private', 'unlisted'], default: 'public' },
|
|
230
|
+
publishedAt: Date,
|
|
231
|
+
|
|
232
|
+
// METRICS
|
|
233
|
+
metrics: metricsSchema,
|
|
234
|
+
|
|
235
|
+
// BUSINESS
|
|
236
|
+
pricing: pricingSchema,
|
|
237
|
+
payout: payoutSchema,
|
|
238
|
+
|
|
239
|
+
// POLICIES
|
|
240
|
+
policies: policiesSchema,
|
|
241
|
+
|
|
242
|
+
// MODERATION
|
|
243
|
+
moderation: moderationSchema,
|
|
244
|
+
|
|
245
|
+
// SEO
|
|
246
|
+
seo: seoSchema,
|
|
247
|
+
|
|
248
|
+
// FEATURES
|
|
249
|
+
features: featuresSchema,
|
|
250
|
+
|
|
251
|
+
// AUDIT
|
|
252
|
+
createdAt: { type: Date, default: Date.now },
|
|
253
|
+
updatedAt: { type: Date, default: Date.now },
|
|
254
|
+
submittedAt: Date,
|
|
255
|
+
history: [historySchema],
|
|
256
|
+
deletedAt: Date,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Indexes
|
|
260
|
+
eventSchema.index({ slug: 1 });
|
|
261
|
+
eventSchema.index({ 'venue.coordinates': '2dsphere' });
|
|
262
|
+
eventSchema.index({ status: 1, visibility: 1 });
|
|
263
|
+
eventSchema.index({ hostId: 1 });
|
|
264
|
+
eventSchema.index({ 'schedule.startDate': 1 });
|
|
265
|
+
eventSchema.index({ type: 1 });
|
|
266
|
+
eventSchema.index({ categories: 1 });
|
|
267
|
+
|
|
268
|
+
// New compound indexes for optimized queries
|
|
269
|
+
// For trending events (recent views + high sales)
|
|
270
|
+
eventSchema.index({ 'metrics.lastViewedAt': -1, 'metrics.ticketsSold': -1 });
|
|
271
|
+
|
|
272
|
+
// For featured events
|
|
273
|
+
eventSchema.index({ 'features.isFeatured': 1, 'features.featuredPriority': -1 });
|
|
274
|
+
|
|
275
|
+
// For filtering by status + visibility + date
|
|
276
|
+
eventSchema.index({ status: 1, visibility: 1, 'schedule.startDate': 1 });
|
|
277
|
+
|
|
278
|
+
// For location-based queries with status
|
|
279
|
+
eventSchema.index({ 'venue.address.city': 1, status: 1 });
|
|
280
|
+
|
|
281
|
+
// For host's events with status filter
|
|
282
|
+
eventSchema.index({ hostId: 1, status: 1, updatedAt: -1 });
|
|
283
|
+
|
|
284
|
+
// Validation: sum(tickets.quantity) <= venue.capacity
|
|
285
|
+
eventSchema.pre('save', function(next) {
|
|
286
|
+
if (this.tickets && this.tickets.length > 0 && this.venue) {
|
|
287
|
+
const totalCapacity = this.tickets.reduce((sum, ticket) => sum + ticket.quantity, 0);
|
|
288
|
+
if (totalCapacity > this.venue.capacity) {
|
|
289
|
+
return next(new Error('Total ticket quantity exceeds venue capacity'));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
next();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Update updatedAt on save
|
|
296
|
+
eventSchema.pre('save', function(next) {
|
|
297
|
+
this.updatedAt = new Date();
|
|
298
|
+
next();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
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
|
+
}
|