@positivegrid/pg-mongoose-schema 27.8.5 → 28.0.0-beta.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/index.mjs ADDED
@@ -0,0 +1,5128 @@
1
+ import debug from "debug";
2
+ import _ from "lodash";
3
+ import mongooseLeanVirtuals from "mongoose-lean-virtuals";
4
+ import deepPopulateFactory from "mongoose-deep-populate";
5
+ import isSubset from "is-subset";
6
+ import moment from "moment";
7
+ import crypto from "node:crypto";
8
+ import { ApiErrorCode } from "@positivegrid/pg-error";
9
+ import isEmail from "is-email";
10
+ import randomString from "random-string";
11
+ //#region src/models/banks.ts
12
+ debug("model:banks");
13
+ function buildBanks(mongoose) {
14
+ const { Schema } = mongoose;
15
+ const ObjectId = Schema.Types.ObjectId;
16
+ const BankListSchema = new Schema({
17
+ name: {
18
+ type: String,
19
+ required: true
20
+ },
21
+ creator_id: {
22
+ type: ObjectId,
23
+ ref: "User",
24
+ required: true
25
+ },
26
+ description: { type: String },
27
+ preset_for: {
28
+ type: String,
29
+ required: true
30
+ },
31
+ created_on: {
32
+ type: Date,
33
+ default: Date.now
34
+ },
35
+ updated_on: {
36
+ type: Date,
37
+ default: Date.now
38
+ }
39
+ }, { collection: "bank_list" });
40
+ BankListSchema.index({
41
+ creator_id: 1,
42
+ preset_for: 1
43
+ });
44
+ const BankPresetsSchema = new Schema({
45
+ name: {
46
+ type: String,
47
+ index: true,
48
+ required: true
49
+ },
50
+ creator_id: {
51
+ type: ObjectId,
52
+ ref: "User",
53
+ required: true
54
+ },
55
+ bank_id: {
56
+ type: ObjectId,
57
+ ref: "BankList",
58
+ required: true
59
+ },
60
+ status: {
61
+ type: Number,
62
+ default: 1,
63
+ required: true
64
+ },
65
+ description: { type: String },
66
+ preset_data: {
67
+ type: Schema.Types.Mixed,
68
+ required: true
69
+ },
70
+ preset_meta: { type: Schema.Types.Mixed },
71
+ image_url: { type: String },
72
+ thumb_url: { type: String },
73
+ created_on: {
74
+ type: Date,
75
+ default: Date.now
76
+ },
77
+ updated_on: {
78
+ type: Date,
79
+ default: Date.now
80
+ }
81
+ }, {
82
+ collection: "bank_presets",
83
+ toJSON: { virtuals: true },
84
+ toObject: { virtuals: true }
85
+ });
86
+ BankPresetsSchema.index({
87
+ creator_id: 1,
88
+ bank_id: 1,
89
+ name: 1
90
+ }, { unique: true });
91
+ return {
92
+ BankPresets: mongoose.model("BankPresets", BankPresetsSchema),
93
+ BankList: mongoose.model("BankList", BankListSchema)
94
+ };
95
+ }
96
+ //#endregion
97
+ //#region src/models/device.ts
98
+ function buildDevice(mongoose) {
99
+ const { Schema } = mongoose;
100
+ const ObjectId = Schema.Types.ObjectId;
101
+ const DeviceSchema = new Schema({
102
+ user_id: {
103
+ type: ObjectId,
104
+ ref: "User",
105
+ required: true
106
+ },
107
+ device_udid: {
108
+ type: String,
109
+ required: true,
110
+ unique: true
111
+ },
112
+ display_name: { type: String },
113
+ mac_address: { type: String },
114
+ machine_id: { type: String },
115
+ platform: { type: String },
116
+ architecture: { type: String },
117
+ os_version: { type: String },
118
+ model: { type: String },
119
+ activate_random: { type: String },
120
+ other_info: { type: String },
121
+ created_on: {
122
+ type: Date,
123
+ default: Date.now
124
+ }
125
+ }, { collection: "jamup_device" });
126
+ const IapActivationSchema = new Schema({
127
+ user_id: {
128
+ type: ObjectId,
129
+ ref: "User",
130
+ required: true
131
+ },
132
+ device_id: {
133
+ type: ObjectId,
134
+ ref: "Device",
135
+ required: true
136
+ },
137
+ main_product: {
138
+ type: String,
139
+ required: true
140
+ },
141
+ status: {
142
+ type: Number,
143
+ default: 1,
144
+ required: true
145
+ },
146
+ created_on: {
147
+ type: Date,
148
+ default: Date.now
149
+ },
150
+ updated_on: {
151
+ type: Date,
152
+ default: Date.now
153
+ }
154
+ }, { collection: "iap_activation" });
155
+ const ActivationStatusSchema = new Schema({
156
+ user_id: {
157
+ type: ObjectId,
158
+ ref: "User",
159
+ required: true
160
+ },
161
+ device_id: {
162
+ type: ObjectId,
163
+ ref: "Device",
164
+ required: true
165
+ },
166
+ license_id: {
167
+ type: ObjectId,
168
+ ref: "License",
169
+ required: true
170
+ },
171
+ status: {
172
+ type: Number,
173
+ required: true
174
+ },
175
+ created_on: {
176
+ type: Date,
177
+ default: Date.now
178
+ },
179
+ updated_on: {
180
+ type: Date,
181
+ default: Date.now
182
+ }
183
+ }, { collection: "jamup_activationstatus" });
184
+ DeviceSchema.static({ getFeaturedPresetListByCondition(queryObj, cb) {
185
+ this.find(queryObj).select("-is_shown -order").then((docs) => cb(null, docs), (err) => cb(err));
186
+ } });
187
+ return {
188
+ Device: mongoose.model("Device", DeviceSchema),
189
+ ActivationStatus: mongoose.model("ActivationStatus", ActivationStatusSchema),
190
+ IapActivation: mongoose.model("IapActivation", IapActivationSchema)
191
+ };
192
+ }
193
+ //#endregion
194
+ //#region src/models/featuredList.ts
195
+ function buildFeaturedList(mongoose) {
196
+ const { Schema } = mongoose;
197
+ const ObjectId = Schema.Types.ObjectId;
198
+ const FeaturedListSchema = new Schema({
199
+ title: {
200
+ type: String,
201
+ required: true
202
+ },
203
+ description: {
204
+ type: String,
205
+ default: null
206
+ },
207
+ image_url: {
208
+ type: String,
209
+ default: null
210
+ },
211
+ thumb_url: {
212
+ type: String,
213
+ default: null
214
+ },
215
+ embed_url: {
216
+ type: String,
217
+ default: null
218
+ },
219
+ bg_url: {
220
+ type: String,
221
+ default: null
222
+ },
223
+ order: {
224
+ type: Number,
225
+ default: 0
226
+ },
227
+ type: {
228
+ type: String,
229
+ default: null
230
+ },
231
+ permission: {
232
+ type: Schema.Types.Mixed,
233
+ default: null
234
+ },
235
+ metadata: {
236
+ type: Array,
237
+ default: null
238
+ },
239
+ is_shown: {
240
+ type: Boolean,
241
+ default: false
242
+ },
243
+ list_for: {
244
+ type: String,
245
+ required: true,
246
+ unique: true
247
+ },
248
+ created_on: {
249
+ type: Date,
250
+ default: Date.now
251
+ },
252
+ updated_on: {
253
+ type: Date,
254
+ default: Date.now
255
+ }
256
+ }, {
257
+ collection: "jamup_featuredpresetlist",
258
+ toJSON: { virtuals: true },
259
+ toObject: { virtuals: true }
260
+ });
261
+ const FeaturedPresetSchema = new Schema({
262
+ featured_list_id: {
263
+ type: ObjectId,
264
+ ref: "FeaturedList",
265
+ required: true
266
+ },
267
+ preset_id: {
268
+ type: ObjectId,
269
+ ref: "Preset",
270
+ required: true
271
+ },
272
+ order: {
273
+ type: Number,
274
+ default: 0,
275
+ required: true
276
+ },
277
+ created_on: {
278
+ type: Date,
279
+ default: Date.now
280
+ }
281
+ }, { collection: "jamup_featuredpreset" });
282
+ FeaturedPresetSchema.index({
283
+ featured_list_id: 1,
284
+ preset_id: 1
285
+ }, { unique: true });
286
+ FeaturedListSchema.plugin(mongooseLeanVirtuals);
287
+ FeaturedListSchema.static({
288
+ getFeaturedPresetListByCondition(reqQuery) {
289
+ const defaultQuery = {
290
+ $or: [{ permission: { $exists: false } }, { permission: null }],
291
+ is_shown: true,
292
+ list_for: "jamup"
293
+ };
294
+ const queryObj = _.has(reqQuery, "list_for") ? _.assign({}, defaultQuery, { list_for: reqQuery.list_for }) : defaultQuery;
295
+ return this.find(queryObj).sort({ order: 1 }).select("-is_shown").lean({ virtuals: true }).exec();
296
+ },
297
+ getLimitedPresetLists(reqQuery) {
298
+ const defaultQuery = {
299
+ $and: [{ permission: { $exists: true } }, { permission: { $ne: null } }],
300
+ is_shown: true,
301
+ list_for: "jamup"
302
+ };
303
+ const queryObj = _.has(reqQuery, "list_for") ? _.assign({}, defaultQuery, { list_for: reqQuery.list_for }) : defaultQuery;
304
+ return this.find(queryObj).sort({ order: 1 }).select("-is_shown").lean({ virtuals: true }).exec();
305
+ }
306
+ });
307
+ FeaturedListSchema.virtual("id").get(function() {
308
+ return this._id.toHexString();
309
+ });
310
+ return {
311
+ FeaturedList: mongoose.model("FeaturedList", FeaturedListSchema),
312
+ FeaturedPreset: mongoose.model("FeaturedPreset", FeaturedPresetSchema)
313
+ };
314
+ }
315
+ //#endregion
316
+ //#region src/models/hardware.ts
317
+ const log$7 = debug("model:promo");
318
+ function _identifyHardwareByModel(model) {
319
+ switch (model) {
320
+ case "G1": return "head";
321
+ case "G2": return "rack";
322
+ case "G3": return "mini";
323
+ case "S": return "spark";
324
+ default: return false;
325
+ }
326
+ }
327
+ function _parseHardwareSerialNumber(hwSn) {
328
+ if (!_.isString(hwSn)) throw new Error(`Invalid hardware series number format: ${hwSn}`);
329
+ if (/^S/.test(hwSn)) {
330
+ const hwModel = hwSn.slice(0, 1);
331
+ const hwWattage = hwSn.slice(1, 4);
332
+ const hwProductType = hwSn.slice(4, 5);
333
+ const hwVersion = hwSn.slice(5, 6);
334
+ const hwYear = hwSn.slice(6, 7);
335
+ const hwMonth = hwSn.slice(7, 8);
336
+ const hwPattern = [
337
+ `hwModel:${hwModel}`,
338
+ `hwWattage:${hwWattage}`,
339
+ `hwProductType:${hwProductType}`,
340
+ `hwVersion:${hwVersion}`,
341
+ `hwYear:${hwYear}`,
342
+ `hwMonth:${hwMonth}`
343
+ ];
344
+ const hwName = _identifyHardwareByModel(hwModel);
345
+ if (hwName === false) throw new Error(`Cannot identify hardware model: ${hwModel}`);
346
+ return {
347
+ hwName,
348
+ hwModel,
349
+ hwWattage,
350
+ hwProductType,
351
+ hwVersion,
352
+ hwYear,
353
+ hwMonth,
354
+ hwPattern
355
+ };
356
+ }
357
+ const hwModel = hwSn.slice(0, 2);
358
+ const hwWattage = hwSn.slice(2, 4);
359
+ const hwVersion = hwSn.slice(4, 7);
360
+ const hwProductionPhase = hwSn.slice(7, 9);
361
+ const hwName = _identifyHardwareByModel(hwModel);
362
+ const hwPattern = [
363
+ `hwModel:${hwModel}`,
364
+ `hwWattage:${hwWattage}`,
365
+ `hwVersion:${hwVersion}`,
366
+ `hwProductionPhase:${hwProductionPhase}`
367
+ ];
368
+ if (hwName === false) throw new Error(`Cannot identify hardware model: ${hwModel}`);
369
+ return {
370
+ hwModel,
371
+ hwWattage,
372
+ hwVersion,
373
+ hwProductionPhase,
374
+ hwName,
375
+ hwPattern
376
+ };
377
+ }
378
+ function buildHardware(mongoose) {
379
+ const { Schema } = mongoose;
380
+ const ObjectId = Schema.Types.ObjectId;
381
+ const HardwareSeriesNumberSchema = new Schema({
382
+ hw_sn: {
383
+ type: String,
384
+ required: true
385
+ },
386
+ channel: {
387
+ type: String,
388
+ default: null
389
+ },
390
+ status: {
391
+ type: Number,
392
+ default: 1
393
+ },
394
+ metadata: { type: Schema.Types.Mixed },
395
+ created_on: {
396
+ type: Date,
397
+ default: Date.now
398
+ }
399
+ }, { collection: "hardware_seriesnumber" });
400
+ const HardwareActivationMetaSchema = new Schema({
401
+ pattern: {
402
+ type: [String],
403
+ required: true
404
+ },
405
+ hw_sn_pattern: {
406
+ type: Schema.Types.Mixed,
407
+ required: true
408
+ },
409
+ hw_activate_license: [{
410
+ type: ObjectId,
411
+ ref: "Product"
412
+ }],
413
+ status: {
414
+ type: Number,
415
+ required: true,
416
+ default: 1
417
+ },
418
+ created_on: {
419
+ type: Date,
420
+ default: Date.now
421
+ }
422
+ }, { collection: "hardware_activation_meta" });
423
+ const HardwareActivationRecordSchema = new Schema({
424
+ user_id: {
425
+ type: ObjectId,
426
+ ref: "User",
427
+ required: true
428
+ },
429
+ hw_sn_id: {
430
+ type: ObjectId,
431
+ ref: "HardwareSeriesNumber",
432
+ required: true
433
+ },
434
+ hw_meta_id: {
435
+ type: ObjectId,
436
+ ref: "HardwareActivationMeta",
437
+ default: null
438
+ },
439
+ hw_partner_id: {
440
+ type: ObjectId,
441
+ ref: "PartnerCoupon",
442
+ default: null
443
+ },
444
+ activate_license: [{
445
+ type: ObjectId,
446
+ ref: "License"
447
+ }],
448
+ status: {
449
+ type: Number,
450
+ required: true,
451
+ default: 1
452
+ },
453
+ activate_on: {
454
+ type: Date,
455
+ default: Date.now
456
+ }
457
+ }, { collection: "hardware_activation_record" });
458
+ const HardwareActivationConflictSchema = new Schema({
459
+ user_id: {
460
+ type: ObjectId,
461
+ ref: "User",
462
+ required: true
463
+ },
464
+ hw_sn_id: {
465
+ type: ObjectId,
466
+ ref: "HardwareSeriesNumber",
467
+ required: true
468
+ },
469
+ created_on: {
470
+ type: Date,
471
+ default: Date.now
472
+ }
473
+ }, { collection: "hardware_activation_conflict" });
474
+ const PartnerCouponSchema = new Schema({
475
+ partner: {
476
+ type: String,
477
+ required: true
478
+ },
479
+ code: {
480
+ type: String,
481
+ required: true
482
+ },
483
+ status: {
484
+ type: Number,
485
+ default: 1,
486
+ required: true
487
+ },
488
+ hw_sn_id: {
489
+ type: ObjectId,
490
+ ref: "HardwareSeriesNumber"
491
+ },
492
+ created_on: {
493
+ type: Date,
494
+ default: Date.now
495
+ }
496
+ }, { collection: "partner_coupon" });
497
+ const WarehouseInventorySchema = new Schema({
498
+ warehouse: {
499
+ type: String,
500
+ required: true
501
+ },
502
+ country: [{ type: String }],
503
+ status: {
504
+ type: Number,
505
+ default: 1,
506
+ required: true
507
+ },
508
+ items: [{
509
+ sku: {
510
+ type: String,
511
+ required: true
512
+ },
513
+ type: {
514
+ type: String,
515
+ required: true
516
+ },
517
+ total: {
518
+ type: Number,
519
+ required: true
520
+ },
521
+ available: {
522
+ type: Number,
523
+ required: true
524
+ },
525
+ transit: {
526
+ type: Number,
527
+ required: true
528
+ },
529
+ lock: {
530
+ type: Number,
531
+ required: true
532
+ }
533
+ }],
534
+ created_on: {
535
+ type: Date,
536
+ default: Date.now
537
+ }
538
+ }, { collection: "warehouse_inventory" });
539
+ WarehouseInventorySchema.index({ warehouse: 1 }, { unique: true });
540
+ WarehouseInventorySchema.index({ country: 1 });
541
+ const ItemInventoryHistorySchema = new Schema({
542
+ warehouse: {
543
+ type: String,
544
+ required: true
545
+ },
546
+ sku: {
547
+ type: String,
548
+ required: true
549
+ },
550
+ total: {
551
+ type: Number,
552
+ required: true
553
+ },
554
+ available: {
555
+ type: Number,
556
+ required: true
557
+ },
558
+ transit: {
559
+ type: Number,
560
+ required: true
561
+ },
562
+ lock: {
563
+ type: Number,
564
+ required: true
565
+ },
566
+ created_on: {
567
+ type: Date,
568
+ default: Date.now
569
+ }
570
+ }, { collection: "item_inventory_history" });
571
+ ItemInventoryHistorySchema.index({
572
+ warehouse: 1,
573
+ sku: 1
574
+ });
575
+ const CountryHardwareSkuSchema = new Schema({
576
+ country: [{ type: String }],
577
+ items: [{
578
+ name: { type: String },
579
+ hw_sku: [{ type: String }]
580
+ }],
581
+ level: [{
582
+ level: { type: Number },
583
+ min: { type: Number },
584
+ max: { type: Number },
585
+ days: { type: String }
586
+ }],
587
+ sale_threshold: { type: Number },
588
+ sale_region: { type: String },
589
+ sale_overwrite: [{
590
+ level: { type: Number },
591
+ days: { type: String }
592
+ }],
593
+ not_sold: [{ type: String }],
594
+ not_sold_threshold: { type: Number },
595
+ express_ship_days: { type: String },
596
+ status: {
597
+ type: Number,
598
+ default: 1
599
+ },
600
+ created_on: {
601
+ type: Date,
602
+ default: Date.now
603
+ }
604
+ }, { collection: "country_hardaware_sku" });
605
+ HardwareSeriesNumberSchema.static({
606
+ parseHardwareSeriesNumber(hwSn) {
607
+ return new Promise((resolve, reject) => {
608
+ try {
609
+ resolve(_parseHardwareSerialNumber(hwSn));
610
+ } catch (e) {
611
+ reject(e);
612
+ }
613
+ });
614
+ },
615
+ parseHardwareSerialNumberNoPromise(hwSn) {
616
+ try {
617
+ return _parseHardwareSerialNumber(hwSn);
618
+ } catch (e) {
619
+ return e.message;
620
+ }
621
+ }
622
+ });
623
+ HardwareActivationMetaSchema.static({ async findMostMatchingMeta(snPatternArr, opts = {}) {
624
+ try {
625
+ const allMatch = await this.find({ pattern: { $in: snPatternArr } }).sort({ created_on: -1 }).lean().exec();
626
+ if (!allMatch || allMatch.length <= 0) {
627
+ if (opts.allowEmpty === true) return null;
628
+ throw new Error("Cannot find any activation meta match the SN pattern");
629
+ }
630
+ let mostMatchingMeta = {};
631
+ let mostMatchingNumber = 0;
632
+ allMatch.forEach((metaItem) => {
633
+ if (_.isArray(metaItem.pattern) && metaItem.pattern.length > 0) {
634
+ const matchItem = _.intersection(metaItem.pattern, snPatternArr);
635
+ if (matchItem.length === metaItem.pattern.length && matchItem.length > mostMatchingNumber) {
636
+ mostMatchingMeta = metaItem;
637
+ mostMatchingNumber = matchItem.length;
638
+ }
639
+ }
640
+ });
641
+ if (_.isEmpty(mostMatchingMeta) || mostMatchingNumber === 0) {
642
+ if (opts.allowEmpty === true) return null;
643
+ throw new Error("Cannot find any activation meta match the SN pattern");
644
+ }
645
+ return mostMatchingMeta;
646
+ } catch (err) {
647
+ log$7("ERROR:findMostMatchingMeta %o", err);
648
+ throw err;
649
+ }
650
+ } });
651
+ return {
652
+ HardwareActivationRecord: mongoose.model("HardwareActivationRecord", HardwareActivationRecordSchema),
653
+ HardwareActivationMeta: mongoose.model("HardwareActivationMeta", HardwareActivationMetaSchema),
654
+ HardwareSeriesNumber: mongoose.model("HardwareSeriesNumber", HardwareSeriesNumberSchema),
655
+ HardwareActivationConflict: mongoose.model("HardwareActivationConflict", HardwareActivationConflictSchema),
656
+ PartnerCoupon: mongoose.model("PartnerCoupon", PartnerCouponSchema),
657
+ WarehouseInventory: mongoose.model("WarehouseInventory", WarehouseInventorySchema),
658
+ ItemInventoryHistory: mongoose.model("ItemInventoryHistory", ItemInventoryHistorySchema),
659
+ CountryHardwareSku: mongoose.model("CountryHardwareSku", CountryHardwareSkuSchema)
660
+ };
661
+ }
662
+ //#endregion
663
+ //#region src/models/homeConfig.ts
664
+ function buildHomeConfig(mongoose) {
665
+ const { Schema } = mongoose;
666
+ const DataSourceSchema = new Schema({
667
+ type: {
668
+ type: String,
669
+ enum: [
670
+ "tone_theme_query",
671
+ "tone_theme_ids",
672
+ "tone_theme_id"
673
+ ],
674
+ required: true
675
+ },
676
+ tone_theme_id: {
677
+ type: String,
678
+ default: null
679
+ },
680
+ query: {
681
+ type: Schema.Types.Mixed,
682
+ default: null
683
+ },
684
+ ids: [{ type: String }],
685
+ limit: {
686
+ type: Number,
687
+ default: 12
688
+ }
689
+ }, { _id: false });
690
+ const SectionSchema = new Schema({
691
+ section_type: {
692
+ type: String,
693
+ enum: [
694
+ "tone_theme_banner",
695
+ "tone_theme_list",
696
+ "tone_theme_lists"
697
+ ],
698
+ required: true
699
+ },
700
+ title: {
701
+ type: String,
702
+ default: null
703
+ },
704
+ order: {
705
+ type: Number,
706
+ required: true
707
+ },
708
+ data_source: {
709
+ type: DataSourceSchema,
710
+ required: true
711
+ }
712
+ }, { _id: false });
713
+ const HomeConfigSchema = new Schema({
714
+ config_for: {
715
+ type: String,
716
+ required: true
717
+ },
718
+ locale: {
719
+ type: String,
720
+ default: "en"
721
+ },
722
+ sections: [SectionSchema],
723
+ is_active: {
724
+ type: Boolean,
725
+ default: false
726
+ },
727
+ created_on: {
728
+ type: Date,
729
+ default: Date.now
730
+ },
731
+ updated_on: {
732
+ type: Date,
733
+ default: Date.now
734
+ }
735
+ }, { collection: "home_config" });
736
+ HomeConfigSchema.index({
737
+ config_for: 1,
738
+ locale: 1,
739
+ is_active: 1
740
+ });
741
+ return mongoose.model("HomeConfig", HomeConfigSchema);
742
+ }
743
+ //#endregion
744
+ //#region src/models/oauth.ts
745
+ function buildOauth(mongoose) {
746
+ const { Schema } = mongoose;
747
+ const ObjectId = Schema.Types.ObjectId;
748
+ const deepPopulate = deepPopulateFactory(mongoose);
749
+ const clientSchema = new Schema({
750
+ name: {
751
+ type: String,
752
+ required: true
753
+ },
754
+ clientId: {
755
+ type: String,
756
+ required: true
757
+ },
758
+ clientSecret: {
759
+ type: String,
760
+ required: true
761
+ }
762
+ }, {
763
+ collection: "auth_clients",
764
+ toJSON: { virtuals: true },
765
+ toObject: { virtuals: true }
766
+ });
767
+ const AuthorizationCodeSchema = new Schema({
768
+ code: {
769
+ type: String,
770
+ required: true
771
+ },
772
+ clientId: {
773
+ type: ObjectId,
774
+ ref: "OathClient",
775
+ required: true
776
+ },
777
+ redirectURI: {
778
+ type: String,
779
+ required: true
780
+ },
781
+ userId: {
782
+ type: ObjectId,
783
+ ref: "User",
784
+ required: true
785
+ },
786
+ scopes: [{
787
+ type: String,
788
+ required: true
789
+ }],
790
+ expiresAt: {
791
+ type: Date,
792
+ required: true
793
+ },
794
+ codeChallenge: { type: String },
795
+ codeChallengeMethod: {
796
+ type: String,
797
+ default: "S256"
798
+ },
799
+ date: {
800
+ type: Date,
801
+ default: Date.now
802
+ }
803
+ }, {
804
+ collection: "auth_authorizationCodes",
805
+ toJSON: { virtuals: true },
806
+ toObject: { virtuals: true }
807
+ });
808
+ AuthorizationCodeSchema.plugin(deepPopulate, {});
809
+ const AccessTokenSchema = new Schema({
810
+ token: {
811
+ type: String,
812
+ required: true
813
+ },
814
+ expirationDate: {
815
+ type: Date,
816
+ required: true
817
+ },
818
+ userId: {
819
+ type: ObjectId,
820
+ ref: "User",
821
+ required: true
822
+ },
823
+ clientId: {
824
+ type: ObjectId,
825
+ ref: "OathClient",
826
+ required: true
827
+ },
828
+ scopes: [{
829
+ type: String,
830
+ required: true
831
+ }],
832
+ date: {
833
+ type: Date,
834
+ default: Date.now
835
+ }
836
+ }, {
837
+ collection: "auth_accessTokens",
838
+ toJSON: { virtuals: true },
839
+ toObject: { virtuals: true }
840
+ });
841
+ const RefreshTokenSchema = new Schema({
842
+ refreshToken: {
843
+ type: String,
844
+ required: true
845
+ },
846
+ clientId: {
847
+ type: ObjectId,
848
+ ref: "OathClient",
849
+ required: true
850
+ },
851
+ expirationDate: {
852
+ type: Date,
853
+ required: true
854
+ },
855
+ userId: {
856
+ type: ObjectId,
857
+ ref: "User",
858
+ required: true
859
+ },
860
+ scopes: [{
861
+ type: String,
862
+ required: true
863
+ }],
864
+ date: {
865
+ type: Date,
866
+ default: Date.now
867
+ }
868
+ }, {
869
+ collection: "auth_refreshTokens",
870
+ toJSON: { virtuals: true },
871
+ toObject: { virtuals: true }
872
+ });
873
+ return {
874
+ OauthClient: mongoose.model("OauthClient", clientSchema),
875
+ RefreshTokens: mongoose.model("RefreshTokens", RefreshTokenSchema),
876
+ AccessToken: mongoose.model("AccessToken", AccessTokenSchema),
877
+ AuthorizationCode: mongoose.model("AuthorizationCode", AuthorizationCodeSchema)
878
+ };
879
+ }
880
+ //#endregion
881
+ //#region src/models/partner.ts
882
+ const log$6 = debug("model:partner");
883
+ const OEM_PROMOTION_TYPE = ["discount_coupon"];
884
+ function _identifyItemTypeBySku(sku) {
885
+ if (/^B(.*)/.test(sku)) return "bundle";
886
+ if (/^(L|C|U)(.*)/.test(sku)) return "product";
887
+ return false;
888
+ }
889
+ function buildPartner(mongoose) {
890
+ const { Schema } = mongoose;
891
+ const ObjectId = Schema.Types.ObjectId;
892
+ const PartnerSalesMetaSchema = new Schema({
893
+ partner_name: {
894
+ type: String,
895
+ required: true
896
+ },
897
+ sales_product: {
898
+ type: ObjectId,
899
+ ref: "Product",
900
+ default: null
901
+ },
902
+ sales_bundle: {
903
+ type: ObjectId,
904
+ ref: "Bundle",
905
+ default: null
906
+ },
907
+ b2b_redeem_id: {
908
+ type: Number,
909
+ default: null
910
+ },
911
+ pg_sales_price: {
912
+ type: Number,
913
+ default: 0,
914
+ required: true
915
+ },
916
+ purchase_price: {
917
+ type: Number,
918
+ default: 0,
919
+ required: true
920
+ },
921
+ status: {
922
+ type: Number,
923
+ default: 1,
924
+ required: true
925
+ },
926
+ created_on: {
927
+ type: Date,
928
+ default: Date.now
929
+ }
930
+ }, { collection: "pg_partner_sales_meta" });
931
+ PartnerSalesMetaSchema.index({
932
+ partner_name: 1,
933
+ status: 1
934
+ });
935
+ PartnerSalesMetaSchema.index({
936
+ sales_product: 1,
937
+ sales_bundle: 1,
938
+ status: 1
939
+ });
940
+ const PartnerRedeemSchema = new Schema({
941
+ partner_name: {
942
+ type: String,
943
+ required: true
944
+ },
945
+ sales_item_id: {
946
+ type: ObjectId,
947
+ ref: "PartnerSalesMeta",
948
+ required: true
949
+ },
950
+ purchase_price: {
951
+ type: Number,
952
+ default: 0,
953
+ required: true
954
+ },
955
+ redeem_keycode: {
956
+ type: String,
957
+ required: true
958
+ },
959
+ buyer: {
960
+ type: Schema.Types.Mixed,
961
+ default: null
962
+ },
963
+ order_number: {
964
+ type: String,
965
+ default: null
966
+ },
967
+ status: {
968
+ type: Number,
969
+ required: true,
970
+ default: 1
971
+ },
972
+ metadata: {
973
+ type: Schema.Types.Mixed,
974
+ default: null
975
+ },
976
+ created_on: {
977
+ type: Date,
978
+ default: Date.now
979
+ },
980
+ updated_on: {
981
+ type: Date,
982
+ default: Date.now
983
+ }
984
+ }, { collection: "pg_partner_redeem" });
985
+ const PartnerSalesHistorySchema = new Schema({
986
+ partner_name: {
987
+ type: String,
988
+ required: true
989
+ },
990
+ sku: {
991
+ type: String,
992
+ required: true
993
+ },
994
+ title: {
995
+ type: String,
996
+ required: true
997
+ },
998
+ previous_price: {
999
+ type: Number,
1000
+ required: true
1001
+ },
1002
+ current_price: {
1003
+ type: Number,
1004
+ required: true
1005
+ },
1006
+ updated_on: {
1007
+ type: Date,
1008
+ default: Date.now
1009
+ }
1010
+ }, { collection: "pg_partner_sales_history" });
1011
+ PartnerSalesHistorySchema.index({
1012
+ partner_name: 1,
1013
+ sku: 1
1014
+ });
1015
+ const B2bRedeemOrderSchema = new Schema({
1016
+ dealer_id: {
1017
+ type: ObjectId,
1018
+ ref: "auth_user",
1019
+ required: true
1020
+ },
1021
+ order_id: {
1022
+ type: Number,
1023
+ default: null,
1024
+ required: true
1025
+ },
1026
+ order_detail: [{
1027
+ product_id: {
1028
+ type: ObjectId,
1029
+ ref: "jamup_product",
1030
+ default: null
1031
+ },
1032
+ bundle_id: {
1033
+ type: ObjectId,
1034
+ ref: "jamup_bundle",
1035
+ default: null
1036
+ },
1037
+ quantity: {
1038
+ type: Number,
1039
+ default: 0
1040
+ }
1041
+ }],
1042
+ order_amount: {
1043
+ type: Number,
1044
+ default: 0,
1045
+ required: true
1046
+ },
1047
+ status: {
1048
+ type: Number,
1049
+ default: 0,
1050
+ required: true
1051
+ },
1052
+ created_on: {
1053
+ type: Date,
1054
+ default: Date.now
1055
+ }
1056
+ }, { collection: "pg_b2b_redeem_order" });
1057
+ B2bRedeemOrderSchema.index({
1058
+ dealer_id: 1,
1059
+ order_id: 1
1060
+ });
1061
+ const B2bRedeemSchema = new Schema({
1062
+ redeem_code: {
1063
+ type: String,
1064
+ required: true
1065
+ },
1066
+ status: {
1067
+ type: Number,
1068
+ default: 1,
1069
+ required: true
1070
+ },
1071
+ redeem_order_id: {
1072
+ type: ObjectId,
1073
+ ref: "pg_b2b_redeem_order",
1074
+ required: true
1075
+ },
1076
+ apply_user_id: {
1077
+ type: ObjectId,
1078
+ ref: "auth_user",
1079
+ default: null,
1080
+ required: true
1081
+ },
1082
+ created_on: {
1083
+ type: Date,
1084
+ default: Date.now
1085
+ }
1086
+ }, { collection: "pg_b2b_redeem" });
1087
+ const B2bDealerSchema = new Schema({
1088
+ dealer_id: {
1089
+ type: ObjectId,
1090
+ ref: "User",
1091
+ required: true
1092
+ },
1093
+ business_name: {
1094
+ type: String,
1095
+ required: true
1096
+ },
1097
+ business_address: {
1098
+ type: String,
1099
+ required: true
1100
+ },
1101
+ business_phone: {
1102
+ type: String,
1103
+ required: true
1104
+ },
1105
+ business_website: {
1106
+ type: String,
1107
+ default: null
1108
+ },
1109
+ business_tax_id: {
1110
+ type: String,
1111
+ default: null
1112
+ },
1113
+ business_vat_id: {
1114
+ type: String,
1115
+ default: null
1116
+ },
1117
+ members: [{ type: String }],
1118
+ created_on: {
1119
+ type: Date,
1120
+ default: Date.now
1121
+ }
1122
+ }, { collection: "b2b_dealer_profile" });
1123
+ const OemRedeemPromotionSchema = new Schema({
1124
+ redeem_product: {
1125
+ type: String,
1126
+ required: true
1127
+ },
1128
+ product_type: {
1129
+ type: String,
1130
+ required: true
1131
+ },
1132
+ partner_name: {
1133
+ type: String,
1134
+ required: true
1135
+ },
1136
+ promotion_type: {
1137
+ type: String,
1138
+ enum: OEM_PROMOTION_TYPE,
1139
+ required: true
1140
+ },
1141
+ promotion_data: {
1142
+ type: Schema.Types.Mixed,
1143
+ required: true
1144
+ },
1145
+ status: {
1146
+ type: Number,
1147
+ default: 1,
1148
+ required: true
1149
+ },
1150
+ created_on: {
1151
+ type: Date,
1152
+ default: Date.now
1153
+ }
1154
+ }, { collection: "pg_oem_redeem_promotion" });
1155
+ PartnerSalesMetaSchema.static({
1156
+ getPartnerSalesMeta(partnerName) {
1157
+ return this.aggregate([
1158
+ { $match: {
1159
+ partner_name: partnerName,
1160
+ status: 1
1161
+ } },
1162
+ { $lookup: {
1163
+ from: "jamup_bundle",
1164
+ foreignField: "_id",
1165
+ localField: "sales_bundle",
1166
+ as: "bundle"
1167
+ } },
1168
+ { $lookup: {
1169
+ from: "jamup_product",
1170
+ foreignField: "_id",
1171
+ localField: "sales_product",
1172
+ as: "product"
1173
+ } }
1174
+ ]).exec();
1175
+ },
1176
+ async getSalesMetaWithSku(sku, partnerName) {
1177
+ const skuType = _identifyItemTypeBySku(sku);
1178
+ if (skuType === "bundle") {
1179
+ const data = await this.aggregate([
1180
+ { $match: {
1181
+ partner_name: partnerName,
1182
+ status: 1
1183
+ } },
1184
+ { $lookup: {
1185
+ from: "jamup_bundle",
1186
+ foreignField: "_id",
1187
+ localField: "sales_bundle",
1188
+ as: "sales"
1189
+ } },
1190
+ { $unwind: "$sales" },
1191
+ { $match: { "sales.sku": sku } }
1192
+ ]).exec();
1193
+ if (Array.isArray(data) && data.length > 1) throw new Error(`Incorrect bundle sku settings: ${sku}.`);
1194
+ return data[0];
1195
+ }
1196
+ if (skuType === "product") {
1197
+ const data = await this.aggregate([
1198
+ { $match: {
1199
+ partner_name: partnerName,
1200
+ status: 1
1201
+ } },
1202
+ { $lookup: {
1203
+ from: "jamup_product",
1204
+ foreignField: "_id",
1205
+ localField: "sales_product",
1206
+ as: "sales"
1207
+ } },
1208
+ { $unwind: "$sales" },
1209
+ { $match: { "sales.sku": sku } }
1210
+ ]).exec();
1211
+ if (Array.isArray(data) && data.length > 1) throw new Error(`Incorrect product sku settings: ${sku}.`);
1212
+ return data[0];
1213
+ }
1214
+ throw new Error(`Invalid item sku: ${sku}`);
1215
+ }
1216
+ });
1217
+ OemRedeemPromotionSchema.static({ async findRedeemFullfillPromotion(redeemData) {
1218
+ try {
1219
+ if (!_.has(redeemData, "metadata.OEM_Name")) throw new Error("This is not a OEM redeem code.");
1220
+ if (!_.has(redeemData, "metadata.product_name") || !_.has(redeemData, "metadata.type")) throw new Error("This is not a valid redeem code.");
1221
+ return this.find({
1222
+ redeem_product: redeemData.metadata.product_name,
1223
+ product_type: redeemData.metadata.type,
1224
+ partner_name: redeemData.metadata.OEM_Name,
1225
+ status: 1
1226
+ }).exec();
1227
+ } catch (err) {
1228
+ log$6("ERR:findRedeemFullfillPromotion %o", err);
1229
+ throw err;
1230
+ }
1231
+ } });
1232
+ return {
1233
+ PartnerSalesMeta: mongoose.model("PartnerSalesMeta", PartnerSalesMetaSchema),
1234
+ PartnerRedeem: mongoose.model("PartnerRedeem", PartnerRedeemSchema),
1235
+ PartnerSalesHistory: mongoose.model("PartnerSalesHistory", PartnerSalesHistorySchema),
1236
+ B2bRedeemOrder: mongoose.model("B2bRedeemOrder", B2bRedeemOrderSchema),
1237
+ B2bRedeem: mongoose.model("B2bRedeem", B2bRedeemSchema),
1238
+ B2bDealer: mongoose.model("B2bDealer", B2bDealerSchema),
1239
+ OemRedeemPromotion: mongoose.model("OemRedeemPromotion", OemRedeemPromotionSchema)
1240
+ };
1241
+ }
1242
+ //#endregion
1243
+ //#region src/models/payment.ts
1244
+ const log$5 = debug("model:payment");
1245
+ const OEM_FOCUSRITE_SUPPORT_LICENSE = [
1246
+ {
1247
+ support_type: {
1248
+ product_name: "live",
1249
+ type: "lite"
1250
+ },
1251
+ display_name: "LE"
1252
+ },
1253
+ {
1254
+ support_type: {
1255
+ product_name: "bias",
1256
+ type: "lite"
1257
+ },
1258
+ display_name: "LE"
1259
+ },
1260
+ {
1261
+ support_type: {
1262
+ product_name: "amp2",
1263
+ type: "lite"
1264
+ },
1265
+ display_name: "LE"
1266
+ }
1267
+ ];
1268
+ const OEM_APOGEE_SUPPORT_LICENSE = [{
1269
+ support_type: {
1270
+ product_name: "live",
1271
+ type: "lite"
1272
+ },
1273
+ display_name: "BIAS FX JAM"
1274
+ }];
1275
+ const OEM_PROSONUS_SUPPORT_LICENSE = [{
1276
+ support_type: {
1277
+ product_name: "fx2",
1278
+ type: "lite"
1279
+ },
1280
+ display_name: "BIAS FX 2 LE"
1281
+ }];
1282
+ function _oemMetaToLicenseName(redeemMeta) {
1283
+ let ret;
1284
+ switch (redeemMeta.OEM_Name) {
1285
+ case "focusrite":
1286
+ ret = _.find(OEM_FOCUSRITE_SUPPORT_LICENSE, (o) => isSubset(redeemMeta, o.support_type));
1287
+ break;
1288
+ case "Apogee":
1289
+ ret = _.find(OEM_APOGEE_SUPPORT_LICENSE, (o) => isSubset(redeemMeta, o.support_type));
1290
+ break;
1291
+ case "ProSonus":
1292
+ ret = _.find(OEM_PROSONUS_SUPPORT_LICENSE, (o) => isSubset(redeemMeta, o.support_type));
1293
+ break;
1294
+ default:
1295
+ log$5("ERROR:_oemMetaToLicenseName %o", redeemMeta);
1296
+ return null;
1297
+ }
1298
+ return !ret ? null : {
1299
+ oem_name: redeemMeta.OEM_Name,
1300
+ license_name: ret.display_name
1301
+ };
1302
+ }
1303
+ function buildPayment(mongoose) {
1304
+ const { Schema } = mongoose;
1305
+ const ObjectId = Schema.Types.ObjectId;
1306
+ const ProductSchema = new Schema({
1307
+ sku: {
1308
+ type: String,
1309
+ required: true,
1310
+ unique: true
1311
+ },
1312
+ name: {
1313
+ type: String,
1314
+ required: true
1315
+ },
1316
+ display_name: {
1317
+ type: String,
1318
+ required: true
1319
+ },
1320
+ description: {
1321
+ type: String,
1322
+ required: true
1323
+ },
1324
+ price: {
1325
+ type: Number,
1326
+ default: 0,
1327
+ required: true
1328
+ },
1329
+ product_type: {
1330
+ type: Number,
1331
+ default: 0,
1332
+ required: true
1333
+ },
1334
+ status: {
1335
+ type: Number,
1336
+ default: 1,
1337
+ required: true
1338
+ },
1339
+ quota: {
1340
+ type: Number,
1341
+ default: 1,
1342
+ required: true
1343
+ },
1344
+ addition: {
1345
+ type: Number,
1346
+ required: true
1347
+ },
1348
+ version: {
1349
+ type: String,
1350
+ required: true
1351
+ },
1352
+ max_purchase: { type: Number },
1353
+ can_upgrade_addition: { type: Boolean },
1354
+ can_free_trial: {
1355
+ type: Boolean,
1356
+ default: false
1357
+ },
1358
+ upgrade: {
1359
+ to: { type: ObjectId },
1360
+ price: { type: Number },
1361
+ description: { type: String }
1362
+ },
1363
+ upgrade_metadata: [{
1364
+ to: { type: ObjectId },
1365
+ price: { type: Number },
1366
+ description: { type: String }
1367
+ }],
1368
+ discount: { type: Schema.Types.Mixed },
1369
+ gift_list: {
1370
+ type: [ObjectId],
1371
+ default: () => null
1372
+ },
1373
+ require_list: {
1374
+ type: Schema.Types.Mixed,
1375
+ default: null
1376
+ },
1377
+ require_one_list: [{
1378
+ type: ObjectId,
1379
+ ref: "Product"
1380
+ }],
1381
+ require_one_bundles: [{
1382
+ type: ObjectId,
1383
+ ref: "Bundle"
1384
+ }],
1385
+ free_upgrade_list: [{
1386
+ has: { type: ObjectId },
1387
+ to: { type: ObjectId }
1388
+ }],
1389
+ remap_product_sku: { type: String },
1390
+ extra_payment_fields: { type: Schema.Types.Mixed },
1391
+ created_on: {
1392
+ type: Date,
1393
+ default: Date.now
1394
+ },
1395
+ updated_on: {
1396
+ type: Date,
1397
+ default: Date.now
1398
+ }
1399
+ }, {
1400
+ collection: "jamup_product",
1401
+ toJSON: { virtuals: true },
1402
+ toObject: { virtuals: true }
1403
+ });
1404
+ const LicenseSchema = new Schema({
1405
+ user_id: {
1406
+ type: ObjectId,
1407
+ ref: "User",
1408
+ required: true
1409
+ },
1410
+ product_id: {
1411
+ type: ObjectId,
1412
+ ref: "Product",
1413
+ required: true
1414
+ },
1415
+ bc_order_id: {
1416
+ type: Number,
1417
+ default: null
1418
+ },
1419
+ bc_coupon: {
1420
+ type: [String],
1421
+ default: () => null
1422
+ },
1423
+ quota: {
1424
+ type: Number,
1425
+ default: 0
1426
+ },
1427
+ from_redeem: {
1428
+ type: String,
1429
+ default: null
1430
+ },
1431
+ redeem_id: {
1432
+ type: ObjectId,
1433
+ ref: "RedeemCode"
1434
+ },
1435
+ from_bundle: {
1436
+ type: ObjectId,
1437
+ ref: "Bundle",
1438
+ default: null
1439
+ },
1440
+ from_hardware: {
1441
+ type: ObjectId,
1442
+ ref: "hardware_activation_record",
1443
+ default: null
1444
+ },
1445
+ from_gift: {
1446
+ type: ObjectId,
1447
+ ref: "License",
1448
+ default: null
1449
+ },
1450
+ from_guest: {
1451
+ type: ObjectId,
1452
+ ref: "GuestCheckout",
1453
+ default: null
1454
+ },
1455
+ from_remap: {
1456
+ type: ObjectId,
1457
+ ref: "Product"
1458
+ },
1459
+ from_free_trial: {
1460
+ type: ObjectId,
1461
+ ref: "FreeTrial",
1462
+ default: null
1463
+ },
1464
+ upgrade_to: {
1465
+ type: ObjectId,
1466
+ ref: "License",
1467
+ default: null
1468
+ },
1469
+ price_amount: {
1470
+ type: Number,
1471
+ default: null
1472
+ },
1473
+ due_date: {
1474
+ type: Date,
1475
+ default: null
1476
+ },
1477
+ iap_device_id: {
1478
+ type: String,
1479
+ default: null
1480
+ },
1481
+ iap_source: {
1482
+ type: String,
1483
+ default: null
1484
+ },
1485
+ metadata: {
1486
+ type: String,
1487
+ default: null
1488
+ },
1489
+ custom_metadata: {
1490
+ type: Schema.Types.Mixed,
1491
+ default: null
1492
+ },
1493
+ status: {
1494
+ type: Number,
1495
+ default: 1
1496
+ },
1497
+ created_on: {
1498
+ type: Date,
1499
+ default: Date.now
1500
+ },
1501
+ updated_on: {
1502
+ type: Date,
1503
+ default: Date.now
1504
+ }
1505
+ }, { collection: "jamup_license" });
1506
+ LicenseSchema.index({
1507
+ iap_device_id: 1,
1508
+ user_id: 1,
1509
+ product_id: 1,
1510
+ status: 1
1511
+ });
1512
+ LicenseSchema.index({
1513
+ user_id: 1,
1514
+ product_id: 1,
1515
+ status: 1,
1516
+ iap_source: 1
1517
+ });
1518
+ LicenseSchema.index({
1519
+ from_redeem: 1,
1520
+ user_id: 1
1521
+ });
1522
+ LicenseSchema.index({
1523
+ user_id: 1,
1524
+ status: 1,
1525
+ due_date: 1,
1526
+ from_free_trial: 1
1527
+ });
1528
+ const trialTransform = (_doc, ret) => {
1529
+ if (ret.from_free_trial && ret.from_free_trial !== null) ret.free_trial_meta = {
1530
+ is_free_trial: true,
1531
+ start_date: ret.created_on,
1532
+ end_date: ret.due_date
1533
+ };
1534
+ else ret.free_trial_meta = null;
1535
+ return ret;
1536
+ };
1537
+ LicenseSchema.set("toJSON", {
1538
+ virtuals: true,
1539
+ transform: trialTransform
1540
+ });
1541
+ LicenseSchema.set("toObject", {
1542
+ virtuals: true,
1543
+ transform: trialTransform
1544
+ });
1545
+ const LicenseUpgradeMetaSchema = new Schema({
1546
+ bc_cart_id: {
1547
+ type: String,
1548
+ required: true
1549
+ },
1550
+ user_id: {
1551
+ type: ObjectId,
1552
+ ref: "auth_user",
1553
+ required: true
1554
+ },
1555
+ upgrade_from: {
1556
+ type: ObjectId,
1557
+ ref: "jamup_product",
1558
+ default: null,
1559
+ required: true
1560
+ },
1561
+ upgrade_to: {
1562
+ type: ObjectId,
1563
+ ref: "jamup_product",
1564
+ default: null,
1565
+ required: true
1566
+ },
1567
+ upgrade_bundle_from: [{
1568
+ type: ObjectId,
1569
+ ref: "jamup_product"
1570
+ }],
1571
+ upgrade_bundle_to: {
1572
+ type: ObjectId,
1573
+ ref: "jamup_bundle",
1574
+ default: null
1575
+ },
1576
+ status: {
1577
+ type: Number,
1578
+ default: 0
1579
+ },
1580
+ created_on: {
1581
+ type: Date,
1582
+ default: Date.now
1583
+ }
1584
+ }, { collection: "license_upgrade_meta" });
1585
+ LicenseUpgradeMetaSchema.index({
1586
+ bc_cart_id: 1,
1587
+ user_id: 1,
1588
+ upgrade_to: 1,
1589
+ upgrade_from: 1
1590
+ });
1591
+ LicenseUpgradeMetaSchema.index({
1592
+ bc_cart_id: 1,
1593
+ user_id: 1,
1594
+ upgrade_bundle_to: 1
1595
+ });
1596
+ LicenseUpgradeMetaSchema.static({
1597
+ async saveUpgradeOrder(bcCartId, userId, upgradeFromProductId, upgradeToProductId) {
1598
+ try {
1599
+ return await this.findOneAndUpdate({
1600
+ bc_cart_id: bcCartId,
1601
+ user_id: userId,
1602
+ upgrade_from: upgradeFromProductId,
1603
+ upgrade_to: upgradeToProductId
1604
+ }, { $set: {
1605
+ bc_cart_id: bcCartId,
1606
+ user_id: userId,
1607
+ upgrade_from: upgradeFromProductId,
1608
+ upgrade_to: upgradeToProductId,
1609
+ status: 0,
1610
+ created_on: /* @__PURE__ */ new Date()
1611
+ } }, {
1612
+ upsert: true,
1613
+ new: true
1614
+ }).exec();
1615
+ } catch (err) {
1616
+ log$5("ERR:saveUpgradeOrder:%o", err);
1617
+ throw err;
1618
+ }
1619
+ },
1620
+ async saveBundleUpgradeOrder(bcCartId, userId, userOwnedProductIds, upgradeToBundleId) {
1621
+ try {
1622
+ return await this.findOneAndUpdate({
1623
+ bc_cart_id: bcCartId,
1624
+ user_id: userId,
1625
+ upgrade_bundle_to: upgradeToBundleId
1626
+ }, { $set: {
1627
+ bc_cart_id: bcCartId,
1628
+ user_id: userId,
1629
+ upgrade_bundle_to: upgradeToBundleId,
1630
+ upgrade_bundle_from: userOwnedProductIds,
1631
+ status: 0,
1632
+ created_on: /* @__PURE__ */ new Date()
1633
+ } }, {
1634
+ upsert: true,
1635
+ new: true
1636
+ }).exec();
1637
+ } catch (err) {
1638
+ log$5("ERR:saveBundleUpgradeOrder:%o", err);
1639
+ throw err;
1640
+ }
1641
+ },
1642
+ async updateLicenseUpgradeStatusIfExist(bcCartId, userId, targetProductId) {
1643
+ try {
1644
+ const ret = await this.aggregate().match({
1645
+ bc_cart_id: bcCartId,
1646
+ user_id: userId,
1647
+ upgrade_to: targetProductId
1648
+ }).lookup({
1649
+ from: "auth_user",
1650
+ localField: "user_id",
1651
+ foreignField: "_id",
1652
+ as: "user"
1653
+ }).unwind("user").lookup({
1654
+ from: "jamup_product",
1655
+ localField: "upgrade_from",
1656
+ foreignField: "_id",
1657
+ as: "from"
1658
+ }).unwind("from").lookup({
1659
+ from: "jamup_product",
1660
+ localField: "upgrade_to",
1661
+ foreignField: "_id",
1662
+ as: "to"
1663
+ }).unwind("to").exec();
1664
+ if (ret.length === 1) {
1665
+ await this.findOneAndUpdate({
1666
+ bc_cart_id: bcCartId,
1667
+ user_id: userId,
1668
+ upgrade_to: targetProductId
1669
+ }, { $set: { status: 1 } }).exec();
1670
+ return ret[0];
1671
+ }
1672
+ return true;
1673
+ } catch (err) {
1674
+ log$5("ERR:updateLicenseUpgradeStatusIfExist:%o", err);
1675
+ throw err;
1676
+ }
1677
+ },
1678
+ async updateBundleUpgradeStatusIfExist(bcCartId, userId, targetBundleId) {
1679
+ try {
1680
+ if (await this.findOne({
1681
+ bc_cart_id: bcCartId,
1682
+ user_id: userId,
1683
+ upgrade_bundle_to: targetBundleId
1684
+ }).exec()) await this.findOneAndUpdate({
1685
+ bc_cart_id: bcCartId,
1686
+ user_id: userId,
1687
+ upgrade_bundle_to: targetBundleId
1688
+ }, { $set: { status: 1 } }).exec();
1689
+ return true;
1690
+ } catch (err) {
1691
+ log$5("ERR:updateBundleUpgradeStatusIfExist:%o", err);
1692
+ throw err;
1693
+ }
1694
+ }
1695
+ });
1696
+ const BundleSchema = new Schema({
1697
+ name: {
1698
+ type: String,
1699
+ required: true
1700
+ },
1701
+ display_name: {
1702
+ type: String,
1703
+ required: true
1704
+ },
1705
+ display_detail: {
1706
+ type: String,
1707
+ default: null
1708
+ },
1709
+ description: {
1710
+ type: Array,
1711
+ required: true
1712
+ },
1713
+ price: {
1714
+ type: Number,
1715
+ default: 0,
1716
+ required: true
1717
+ },
1718
+ sku: {
1719
+ type: String,
1720
+ default: null
1721
+ },
1722
+ products: [{
1723
+ type: ObjectId,
1724
+ ref: "Product"
1725
+ }],
1726
+ validate_list: [{
1727
+ type: ObjectId,
1728
+ ref: "Product"
1729
+ }],
1730
+ discount: { type: Schema.Types.Mixed },
1731
+ partial_purchase: [{
1732
+ product_id: { type: Schema.Types.Mixed },
1733
+ exclude_product_ids: [{
1734
+ type: ObjectId,
1735
+ ref: "Product"
1736
+ }],
1737
+ discount_price: { type: Number },
1738
+ discount_type: { type: String }
1739
+ }],
1740
+ require_list: {
1741
+ type: Schema.Types.Mixed,
1742
+ default: null
1743
+ },
1744
+ require_one_list: {
1745
+ type: Schema.Types.Mixed,
1746
+ default: null
1747
+ },
1748
+ extra_payment_fields: {
1749
+ type: Schema.Types.Mixed,
1750
+ default: null
1751
+ },
1752
+ status: {
1753
+ type: Number,
1754
+ default: 1,
1755
+ required: true
1756
+ },
1757
+ created_on: {
1758
+ type: Date,
1759
+ default: Date.now
1760
+ },
1761
+ updated_on: {
1762
+ type: Date,
1763
+ default: Date.now
1764
+ }
1765
+ }, {
1766
+ collection: "jamup_bundle",
1767
+ toJSON: { virtuals: true },
1768
+ toObject: { virtuals: true }
1769
+ });
1770
+ const DiscountSchema = new Schema({
1771
+ own_product: {
1772
+ type: ObjectId,
1773
+ ref: "Product",
1774
+ required: true
1775
+ },
1776
+ discount_product: {
1777
+ type: ObjectId,
1778
+ ref: "Product"
1779
+ },
1780
+ discount_bundle: {
1781
+ type: ObjectId,
1782
+ ref: "Bundle"
1783
+ },
1784
+ discount_type: {
1785
+ type: String,
1786
+ required: true
1787
+ },
1788
+ discount_amount: {
1789
+ type: Number,
1790
+ required: true
1791
+ },
1792
+ start_date: {
1793
+ type: Date,
1794
+ default: null
1795
+ },
1796
+ end_date: {
1797
+ type: Date,
1798
+ default: null
1799
+ },
1800
+ status: {
1801
+ type: Boolean,
1802
+ default: true
1803
+ },
1804
+ created_on: {
1805
+ type: Date,
1806
+ default: Date.now
1807
+ }
1808
+ }, {
1809
+ collection: "jamup_discount",
1810
+ toJSON: { virtuals: true },
1811
+ toObject: { virtuals: true }
1812
+ });
1813
+ const EduDomainSchema = new Schema({
1814
+ domain: {
1815
+ type: String,
1816
+ required: true
1817
+ },
1818
+ name: {
1819
+ type: String,
1820
+ required: true
1821
+ },
1822
+ status: {
1823
+ type: Number,
1824
+ default: 1
1825
+ }
1826
+ }, { collection: "edu_domain" });
1827
+ EduDomainSchema.index({ domain: 1 }, { unique: true });
1828
+ const EduUserSchema = new Schema({
1829
+ edu_domain_id: {
1830
+ type: ObjectId,
1831
+ ref: "edu_domain",
1832
+ required: false
1833
+ },
1834
+ user_id: {
1835
+ type: ObjectId,
1836
+ ref: "auth_user",
1837
+ required: true
1838
+ },
1839
+ edu_email: {
1840
+ type: String,
1841
+ required: true
1842
+ },
1843
+ verify_token: {
1844
+ type: String,
1845
+ default: null
1846
+ },
1847
+ university_field: {
1848
+ type: String,
1849
+ default: null
1850
+ },
1851
+ coupon: {
1852
+ type: String,
1853
+ default: null
1854
+ },
1855
+ status: {
1856
+ type: Number,
1857
+ default: 0,
1858
+ required: true
1859
+ },
1860
+ created_on: {
1861
+ type: Date,
1862
+ default: Date.now,
1863
+ required: true
1864
+ }
1865
+ }, { collection: "edu_user" });
1866
+ const GuestCheckoutSchema = new Schema({
1867
+ email: {
1868
+ type: String,
1869
+ required: true
1870
+ },
1871
+ status: {
1872
+ type: Number,
1873
+ default: 1,
1874
+ required: true
1875
+ },
1876
+ bc_order_id: {
1877
+ type: Number,
1878
+ default: null
1879
+ },
1880
+ user_id: {
1881
+ type: ObjectId,
1882
+ ref: "User",
1883
+ default: null
1884
+ },
1885
+ purchased_items: [{
1886
+ sku: {
1887
+ type: String,
1888
+ required: true
1889
+ },
1890
+ number: {
1891
+ type: Number,
1892
+ default: 1,
1893
+ required: true
1894
+ }
1895
+ }],
1896
+ store_code: { type: String },
1897
+ created_on: {
1898
+ type: Date,
1899
+ default: Date.now,
1900
+ required: true
1901
+ },
1902
+ updated_on: {
1903
+ type: Date,
1904
+ default: Date.now,
1905
+ required: true
1906
+ }
1907
+ }, { collection: "pg_guest_checkout" });
1908
+ GuestCheckoutSchema.index({
1909
+ email: 1,
1910
+ bc_order_id: 1
1911
+ }, { unique: true });
1912
+ const AdditionalPurchaseSchema = new Schema({
1913
+ main_product_sku: [{
1914
+ type: String,
1915
+ required: true
1916
+ }],
1917
+ rule: {
1918
+ type: String,
1919
+ default: "has"
1920
+ },
1921
+ category: {
1922
+ type: String,
1923
+ default: null
1924
+ },
1925
+ multiplier: {
1926
+ type: Number,
1927
+ default: 1
1928
+ },
1929
+ additional_purchase: {
1930
+ sku: {
1931
+ type: String,
1932
+ required: true
1933
+ },
1934
+ disount_type: {
1935
+ type: String,
1936
+ default: "amount"
1937
+ },
1938
+ disount_price: {
1939
+ type: Number,
1940
+ required: true
1941
+ }
1942
+ },
1943
+ include_country: [{ type: String }],
1944
+ exclude_country: [{ type: String }],
1945
+ status: {
1946
+ type: Number,
1947
+ default: 1
1948
+ },
1949
+ created_on: {
1950
+ type: Date,
1951
+ default: Date.now
1952
+ }
1953
+ }, { collection: "additional_purchase_meta" });
1954
+ AdditionalPurchaseSchema.index({ main_product_sku: 1 });
1955
+ AdditionalPurchaseSchema.index({ "additional_purchase.sku": 1 });
1956
+ AdditionalPurchaseSchema.index({ include_country: 1 });
1957
+ AdditionalPurchaseSchema.index({ exclude_country: 1 });
1958
+ AdditionalPurchaseSchema.static({
1959
+ async canPurchaseAdditional(ownedSku, additionalSku) {
1960
+ if (!_.isArray(ownedSku) || !_.isArray(additionalSku)) throw new Error("Invalid parameter format!");
1961
+ const additionalPurchaseData = await this.find({
1962
+ "additional_purchase.sku": { $in: additionalSku },
1963
+ status: 1
1964
+ }).exec();
1965
+ if (additionalPurchaseData.length <= 0) return true;
1966
+ let canPurchase = false;
1967
+ _.forEach(additionalPurchaseData, (addition) => {
1968
+ if (addition.rule === "has") {
1969
+ if (_.difference(ownedSku, addition.main_product_sku).length < ownedSku.length) canPurchase = true;
1970
+ }
1971
+ });
1972
+ return canPurchase;
1973
+ },
1974
+ async verifyAdditionalPurchase(skus) {
1975
+ const hasAdditionalPurchase = await this.find({
1976
+ "additional_purchase.sku": { $in: skus },
1977
+ status: 1
1978
+ }).lean().exec();
1979
+ if (hasAdditionalPurchase.length <= 0) return true;
1980
+ let hasFulfill = true;
1981
+ _.forEach(hasAdditionalPurchase, (addition) => {
1982
+ if (addition.rule === "has") {
1983
+ const mainSkus = addition.main_product_sku;
1984
+ if (_.difference(mainSkus, skus).length >= mainSkus.length) hasFulfill = false;
1985
+ }
1986
+ });
1987
+ return hasFulfill;
1988
+ }
1989
+ });
1990
+ const CnSalesMetaSchema = new Schema({
1991
+ product_id: {
1992
+ type: ObjectId,
1993
+ ref: "Product",
1994
+ required: true
1995
+ },
1996
+ status: {
1997
+ type: Number,
1998
+ default: 1,
1999
+ required: true
2000
+ },
2001
+ price: {
2002
+ type: Number,
2003
+ required: true
2004
+ },
2005
+ discount: {
2006
+ type: Number,
2007
+ default: null
2008
+ },
2009
+ gift: {
2010
+ type: [ObjectId],
2011
+ default: () => null
2012
+ },
2013
+ date: {
2014
+ type: Date,
2015
+ default: Date.now
2016
+ }
2017
+ }, { collection: "cn_sales_meta" });
2018
+ CnSalesMetaSchema.static({ async getAllSaleItems(query = {}) {
2019
+ try {
2020
+ const queryCondition = _.isObject(query) && _.isEmpty(query) ? { status: 1 } : query;
2021
+ return await this.aggregate().match(queryCondition).lookup({
2022
+ from: "jamup_product",
2023
+ localField: "product_id",
2024
+ foreignField: "_id",
2025
+ as: "product"
2026
+ }).unwind("$product").project({
2027
+ project_id: 1,
2028
+ price: 1,
2029
+ discount: 1,
2030
+ cn_price: { $cond: {
2031
+ if: { $ne: ["$discount", null] },
2032
+ then: { $multiply: ["$discount", 7] },
2033
+ else: { $multiply: ["$price", 7] }
2034
+ } },
2035
+ date: 1,
2036
+ product: {
2037
+ _id: 1,
2038
+ name: 1,
2039
+ display_name: 1,
2040
+ sku: 1
2041
+ }
2042
+ }).exec();
2043
+ } catch (err) {
2044
+ log$5("ERR:getAllSaleItems:%o", err);
2045
+ throw err;
2046
+ }
2047
+ } });
2048
+ const CnCouponSchema = new Schema({
2049
+ code: {
2050
+ type: String,
2051
+ unique: true,
2052
+ required: true
2053
+ },
2054
+ user_id: {
2055
+ type: ObjectId,
2056
+ ref: "auth_user",
2057
+ required: false
2058
+ },
2059
+ stripe_order_id: {
2060
+ type: String,
2061
+ required: false
2062
+ },
2063
+ multiple_use: {
2064
+ type: Boolean,
2065
+ required: false
2066
+ },
2067
+ value: {
2068
+ type: Number,
2069
+ required: function() {
2070
+ return !this.ratio;
2071
+ }
2072
+ },
2073
+ ratio: {
2074
+ type: Number,
2075
+ required: function() {
2076
+ return !this.value;
2077
+ }
2078
+ },
2079
+ type: {
2080
+ type: Number,
2081
+ default: 1,
2082
+ required: true
2083
+ },
2084
+ status: {
2085
+ type: Number,
2086
+ default: 1,
2087
+ required: true
2088
+ },
2089
+ created_on: {
2090
+ type: Date,
2091
+ default: Date.now
2092
+ },
2093
+ expired_on: {
2094
+ type: Date,
2095
+ required: false
2096
+ }
2097
+ }, { collection: "cn_coupon" });
2098
+ CnCouponSchema.static({
2099
+ async createCoupon({ value, user }) {
2100
+ const that = this;
2101
+ const generateCnCouponCode = async () => {
2102
+ const code = `PGCN${Math.random().toString(36).substring(2, 12).toUpperCase()}`;
2103
+ if (await that.findOne({ code }).exec()) return generateCnCouponCode();
2104
+ return code;
2105
+ };
2106
+ try {
2107
+ if (!value) throw new Error("No value provided");
2108
+ const code = await generateCnCouponCode();
2109
+ return await that.create({
2110
+ code,
2111
+ user_id: user ? user._id : null,
2112
+ value
2113
+ });
2114
+ } catch (err) {
2115
+ log$5("ERR:CnCoupon:createCoupon:%o", err);
2116
+ throw err;
2117
+ }
2118
+ },
2119
+ async findCoupon(code) {
2120
+ return this.findOne({
2121
+ code,
2122
+ stripe_order_id: { $exists: false },
2123
+ $or: [
2124
+ { expired_on: { $gte: /* @__PURE__ */ new Date() } },
2125
+ { expired_on: null },
2126
+ { expired_on: { $exists: false } }
2127
+ ]
2128
+ }).exec();
2129
+ }
2130
+ });
2131
+ CnCouponSchema.method({
2132
+ calculateFinalPrice(price) {
2133
+ return this.value ? price - this.value : parseFloat(String(price * this.ratio)).toPrecision(4);
2134
+ },
2135
+ calculateDeducted(price) {
2136
+ return this.value ? this.value : price - parseFloat(String(price * this.ratio));
2137
+ },
2138
+ async consumeCoupon({ orderId }) {
2139
+ if (!this.multiple_use) {
2140
+ this.stripe_order_id = orderId;
2141
+ return this.save();
2142
+ }
2143
+ }
2144
+ });
2145
+ const BcCartUpdateStatusSchema = new Schema({
2146
+ bc_cart_id: {
2147
+ type: String,
2148
+ required: true
2149
+ },
2150
+ cart_item_id: {
2151
+ type: String,
2152
+ required: true
2153
+ },
2154
+ status: {
2155
+ type: Number,
2156
+ default: 1
2157
+ },
2158
+ created_on: {
2159
+ type: Date,
2160
+ default: Date.now
2161
+ }
2162
+ }, { collection: "bc_cart_update_status" });
2163
+ BcCartUpdateStatusSchema.static({
2164
+ async initCartUpdate(cartId, itemId) {
2165
+ return this.findOneAndUpdate({
2166
+ bc_cart_id: cartId,
2167
+ cart_item_id: itemId
2168
+ }, { $set: {
2169
+ bc_cart_id: cartId,
2170
+ cart_item_id: itemId,
2171
+ status: 1,
2172
+ created_on: /* @__PURE__ */ new Date()
2173
+ } }, {
2174
+ upsert: true,
2175
+ new: true
2176
+ }).exec();
2177
+ },
2178
+ async finishCartUpdate(cartId, itemId) {
2179
+ return this.findOneAndUpdate({
2180
+ bc_cart_id: cartId,
2181
+ cart_item_id: itemId
2182
+ }, { $set: { status: 2 } }, { new: true }).exec();
2183
+ },
2184
+ async handleCartUpdateError(cartId, itemId) {
2185
+ return this.findOneAndUpdate({
2186
+ bc_cart_id: cartId,
2187
+ cart_item_id: itemId
2188
+ }, { $set: { status: 3 } }, { new: true }).exec();
2189
+ }
2190
+ });
2191
+ const PgUserOrderSchema = new Schema({
2192
+ orderId: {
2193
+ type: Number,
2194
+ required: true
2195
+ },
2196
+ customerId: { type: Number },
2197
+ statusId: { type: Number },
2198
+ country: { type: String },
2199
+ totalPrice: { type: Number },
2200
+ purchaseItems: [{
2201
+ sku: { type: String },
2202
+ name: { type: String },
2203
+ quantity: { type: Number },
2204
+ price: { type: Number }
2205
+ }],
2206
+ orderData: { type: Schema.Types.Mixed },
2207
+ createdAt: {
2208
+ type: Date,
2209
+ default: Date.now
2210
+ },
2211
+ updatedAt: {
2212
+ type: Date,
2213
+ default: Date.now
2214
+ }
2215
+ }, { collection: "pg_user_orders" });
2216
+ PgUserOrderSchema.index({ orderId: 1 });
2217
+ PgUserOrderSchema.index({
2218
+ statusId: 1,
2219
+ "purchaseItems.sku": 1
2220
+ });
2221
+ ProductSchema.static({
2222
+ getProductsByCondition(condition, cb) {
2223
+ this.find(condition ?? {}).sort({ created_on: -1 }).then((docs) => cb(null, docs), (err) => cb(err));
2224
+ },
2225
+ createProduct(data, cb) {
2226
+ this.create(data).then((p) => cb(null, p), (err) => cb(err));
2227
+ }
2228
+ });
2229
+ LicenseSchema.static({
2230
+ hasLicenseOrNot(userId, productId, cb) {
2231
+ this.findOne({
2232
+ user_id: userId,
2233
+ product_id: productId,
2234
+ status: 1
2235
+ }).then((license) => {
2236
+ if (license) return cb(/* @__PURE__ */ new Error("User already owned this license"));
2237
+ return cb(null);
2238
+ }, (err) => cb(err));
2239
+ },
2240
+ async hasLicensePromise(userID, productID) {
2241
+ return await this.findOne({
2242
+ user_id: userID,
2243
+ product_id: productID,
2244
+ status: 1,
2245
+ $or: [{ due_date: null }, { due_date: { $gt: /* @__PURE__ */ new Date() } }]
2246
+ }).exec();
2247
+ },
2248
+ getLicenseByRedeem(redeem, cb) {
2249
+ this.find({ from_redeem: redeem }).sort({ created_on: -1 }).then((docs) => cb(null, docs), (err) => cb(err));
2250
+ },
2251
+ async getLicenseByRedeemPromise(redeem, userID) {
2252
+ return this.find({
2253
+ from_redeem: redeem,
2254
+ user_id: new mongoose.Types.ObjectId(userID)
2255
+ }).sort({ created_on: -1 }).populate("product_id").exec();
2256
+ },
2257
+ getUserLicense(query, cb) {
2258
+ if (!_.isObject(query)) return cb(/* @__PURE__ */ new Error("Given query params should be a object"));
2259
+ const queryObj = _.assign({}, query, {
2260
+ status: { $in: [1, 4] },
2261
+ $or: [{ due_date: null }, { due_date: { $gt: /* @__PURE__ */ new Date() } }]
2262
+ });
2263
+ this.find(queryObj).sort({ created_on: -1 }).populate("product_id", "id name price product_type status version addition extra_payment_fields").then((licenses) => {
2264
+ if (!Array.isArray(licenses)) return cb(/* @__PURE__ */ new Error("Invalid data format"));
2265
+ const result = [];
2266
+ licenses.forEach((item) => {
2267
+ if (item.product_id) result.push(item);
2268
+ });
2269
+ return cb(null, result);
2270
+ }, (err) => cb(err));
2271
+ },
2272
+ async getUserLicensePromise(query) {
2273
+ if (!_.isObject(query)) throw new Error("Given query params should be a object");
2274
+ const queryObj = _.assign({}, query, {
2275
+ status: { $in: [1, 4] },
2276
+ $or: [{ due_date: null }, { due_date: { $gt: /* @__PURE__ */ new Date() } }]
2277
+ });
2278
+ const licenseData = await this.find(queryObj).sort({ created_on: -1 }).populate("product_id", "id name price product_type status version addition extra_payment_fields").exec();
2279
+ if (!_.isArray(licenseData)) throw new Error("Invalid license data");
2280
+ const result = [];
2281
+ licenseData.forEach((item) => {
2282
+ if (item.product_id) result.push(item);
2283
+ });
2284
+ return result;
2285
+ },
2286
+ getUserPurchase(query, productType, cb) {
2287
+ if (!_.isObject(query)) return cb(/* @__PURE__ */ new Error("Given query params should be a object"));
2288
+ const populateProductList = productType === "pack" ? "id name product_type display_name extra_payment_fields created_on" : "id name price product_type display_name status version addition";
2289
+ const queryObj = _.assign({}, query, {
2290
+ status: { $in: [1, 4] },
2291
+ $or: [{ due_date: null }, { due_date: { $gt: /* @__PURE__ */ new Date() } }]
2292
+ });
2293
+ this.find(queryObj).sort({ created_on: -1 }).populate("product_id", populateProductList).then((docs) => cb(null, docs), (err) => cb(err));
2294
+ },
2295
+ async getUserPurchasePromise(query, productType, opts) {
2296
+ if (!_.isObject(query)) throw new Error("Given query params should be a object");
2297
+ const populateProductList = productType === "pack" ? "id name sku product_type display_name extra_payment_fields created_on" : "id name sku product_type display_name addition created_on";
2298
+ const freeTrialQuery = productType === "free_trial" ? { $and: [{ from_free_trial: { $ne: null } }, { from_free_trial: { $exists: true } }] } : { $or: [{ from_free_trial: null }, { from_free_trial: { $exists: false } }] };
2299
+ const queryObj = _.assign({}, {
2300
+ status: { $in: [1, 4] },
2301
+ $or: [{ due_date: null }, { due_date: { $gt: /* @__PURE__ */ new Date() } }]
2302
+ }, freeTrialQuery, query);
2303
+ if (_.has(opts, "isLean") && opts.isLean === true) return this.find(queryObj).sort({ created_on: -1 }).populate("product_id", populateProductList).lean({ virtuals: true }).exec();
2304
+ return this.find(queryObj).sort({ created_on: -1 }).populate("product_id", populateProductList).exec();
2305
+ },
2306
+ async getUserIapLicenses(userId, iapIds) {
2307
+ try {
2308
+ return this.aggregate([
2309
+ { $match: {
2310
+ user_id: userId,
2311
+ product_id: { $in: iapIds },
2312
+ status: 1,
2313
+ $or: [{ due_date: null }, { due_date: { $gt: /* @__PURE__ */ new Date() } }]
2314
+ } },
2315
+ { $lookup: {
2316
+ from: "jamup_product",
2317
+ localField: "product_id",
2318
+ foreignField: "_id",
2319
+ as: "product"
2320
+ } },
2321
+ { $unwind: "$product" },
2322
+ { $project: {
2323
+ main_product: "$product.extra_payment_fields.main_product",
2324
+ iap_id: "$product.extra_payment_fields.iap_id",
2325
+ category: "$product.extra_payment_fields.category",
2326
+ display_name: "$product.display_name",
2327
+ user_id: "$user_id",
2328
+ source: "$iap_source",
2329
+ created_on: "$created_on"
2330
+ } }
2331
+ ]).exec();
2332
+ } catch (err) {
2333
+ log$5("ERR:getUserIapLicenses %o", err);
2334
+ throw err;
2335
+ }
2336
+ },
2337
+ async createTrialLicense(data) {
2338
+ try {
2339
+ if (!_.has(data, "due_date") || !moment(data.due_date).isValid()) throw new Error("Invalid trail license format!");
2340
+ return await this.create(_.assign({}, data, {
2341
+ status: 4,
2342
+ due_date: moment(data.due_date).toDate()
2343
+ }));
2344
+ } catch (err) {
2345
+ log$5("ERR:createTrialLicense:%o", err);
2346
+ throw err;
2347
+ }
2348
+ },
2349
+ createLicense(data, cb) {
2350
+ const that = this;
2351
+ that.create(data).then((license) => {
2352
+ that.findById(license.id).populate("product_id").then((doc) => cb(null, doc), (err) => cb(err));
2353
+ }, (err) => cb(err));
2354
+ },
2355
+ async createLicensePromise(data) {
2356
+ try {
2357
+ const newLicense = await this.create(data);
2358
+ return await this.findById(newLicense.id).populate("product_id").exec();
2359
+ } catch (err) {
2360
+ log$5("ERR:createLicensePromise:%o", err);
2361
+ throw err;
2362
+ }
2363
+ },
2364
+ async removeLicense(licenseID) {
2365
+ return await this.findOneAndUpdate({ _id: licenseID }, { $set: { status: 2 } }, { new: true }).exec();
2366
+ }
2367
+ });
2368
+ ProductSchema.virtual("id").get(function() {
2369
+ return this._id.toHexString();
2370
+ });
2371
+ LicenseSchema.virtual("id").get(function() {
2372
+ return this._id.toHexString();
2373
+ });
2374
+ LicenseSchema.virtual("product").get(function() {
2375
+ return this.product_id;
2376
+ });
2377
+ LicenseSchema.virtual("oem").get(function() {
2378
+ return this.custom_metadata && this.custom_metadata.OEM_Name ? _oemMetaToLicenseName(this.custom_metadata) : null;
2379
+ });
2380
+ BundleSchema.virtual("id").get(function() {
2381
+ return this._id.toHexString();
2382
+ });
2383
+ DiscountSchema.virtual("id").get(function() {
2384
+ return this._id.toHexString();
2385
+ });
2386
+ LicenseSchema.plugin(mongooseLeanVirtuals);
2387
+ return {
2388
+ Product: mongoose.model("Product", ProductSchema),
2389
+ License: mongoose.model("License", LicenseSchema),
2390
+ LicenseUpgradeMeta: mongoose.model("LicenseUpgradeMeta", LicenseUpgradeMetaSchema),
2391
+ Bundle: mongoose.model("Bundle", BundleSchema),
2392
+ Discount: mongoose.model("Discount", DiscountSchema),
2393
+ EduDomain: mongoose.model("EduDomain", EduDomainSchema),
2394
+ EduUser: mongoose.model("EduUser", EduUserSchema),
2395
+ GuestCheckout: mongoose.model("GuestCheckout", GuestCheckoutSchema),
2396
+ CnCoupon: mongoose.model("CnCoupon", CnCouponSchema),
2397
+ CnSalesMeta: mongoose.model("CnSalesMeta", CnSalesMetaSchema),
2398
+ AdditionalPurchase: mongoose.model("AdditionalPurchase", AdditionalPurchaseSchema),
2399
+ BcCartUpdateStatus: mongoose.model("BcCartUpdateStatus", BcCartUpdateStatusSchema),
2400
+ UserOrder: mongoose.model("UserOrder", PgUserOrderSchema)
2401
+ };
2402
+ }
2403
+ //#endregion
2404
+ //#region src/models/pgConfig.ts
2405
+ function buildPgConfig(mongoose) {
2406
+ const { Schema } = mongoose;
2407
+ const ObjectId = Schema.Types.ObjectId;
2408
+ const PgConfigSchema = new Schema({
2409
+ config_key: {
2410
+ type: String,
2411
+ index: { unique: true },
2412
+ required: true
2413
+ },
2414
+ config_value: {
2415
+ type: Schema.Types.Mixed,
2416
+ required: true
2417
+ },
2418
+ created_on: {
2419
+ type: Date,
2420
+ default: Date.now
2421
+ }
2422
+ }, { collection: "jamup_positivegridconfig" });
2423
+ const PgLoggingSchema = new Schema({
2424
+ type: {
2425
+ type: String,
2426
+ required: true
2427
+ },
2428
+ log_id: {
2429
+ type: String,
2430
+ required: true
2431
+ },
2432
+ log_content: {
2433
+ type: Schema.Types.Mixed,
2434
+ required: true
2435
+ },
2436
+ user_id: {
2437
+ type: ObjectId,
2438
+ ref: "User"
2439
+ },
2440
+ created_on: {
2441
+ type: Date,
2442
+ default: Date.now
2443
+ }
2444
+ }, { collection: "jamup_logging" });
2445
+ PgLoggingSchema.index({
2446
+ type: 1,
2447
+ log_id: 1
2448
+ });
2449
+ const KeyValueSchema = new Schema({
2450
+ key: {
2451
+ type: String,
2452
+ required: true
2453
+ },
2454
+ value: {
2455
+ type: Schema.Types.Mixed,
2456
+ required: true
2457
+ },
2458
+ created_on: {
2459
+ type: Date,
2460
+ default: Date.now
2461
+ },
2462
+ expire_at: {
2463
+ type: Date,
2464
+ default: null
2465
+ }
2466
+ }, { collection: "pg_key_value" });
2467
+ KeyValueSchema.index({ key: 1 });
2468
+ KeyValueSchema.index({ expire_at: 1 }, { expireAfterSeconds: 0 });
2469
+ const CrashDataSchema = new Schema({
2470
+ product: {
2471
+ type: String,
2472
+ required: true
2473
+ },
2474
+ crash_data: {
2475
+ type: Schema.Types.Mixed,
2476
+ default: null
2477
+ },
2478
+ email: {
2479
+ type: String,
2480
+ default: null
2481
+ },
2482
+ crash_file: {
2483
+ type: String,
2484
+ required: true
2485
+ },
2486
+ created_on: {
2487
+ type: Date,
2488
+ default: Date.now
2489
+ }
2490
+ }, { collection: "pg_crash_data" });
2491
+ PgConfigSchema.static({
2492
+ getConfigByKey(config_key, cb) {
2493
+ this.findOne({ config_key }).then((doc) => cb(null, doc), (err) => cb(err));
2494
+ },
2495
+ createOrUpdateConfig(key, value, doMerge, cb) {
2496
+ const that = this;
2497
+ let realCb = cb;
2498
+ let realMerge;
2499
+ if (_.isFunction(doMerge)) realCb = doMerge;
2500
+ else realMerge = doMerge;
2501
+ that.findOne({ config_key: key }).then((config) => {
2502
+ if (config) {
2503
+ config.config_value = _.isBoolean(realMerge) && realMerge === true ? _.assign({}, config.config_value, value) : value;
2504
+ config.created_on = /* @__PURE__ */ new Date();
2505
+ config.save().then((saved) => realCb(null, saved), (err) => realCb(err));
2506
+ } else that.create({
2507
+ config_key: key,
2508
+ config_value: value
2509
+ }).then((created) => realCb(null, created), (err) => realCb(err));
2510
+ }, (err) => realCb(err));
2511
+ },
2512
+ deleteConfig(key, cb) {
2513
+ this.findOneAndDelete({ config_key: key }).then((doc) => {
2514
+ if (!doc) return cb(/* @__PURE__ */ new Error(`Cannot find config key ${key}`));
2515
+ return cb(null, doc);
2516
+ }, (err) => cb(err));
2517
+ }
2518
+ });
2519
+ return {
2520
+ PgConfig: mongoose.model("PgConfig", PgConfigSchema),
2521
+ PgLogging: mongoose.model("PgLogging", PgLoggingSchema),
2522
+ KeyValue: mongoose.model("KeyValue", KeyValueSchema),
2523
+ CrashData: mongoose.model("CrashData", CrashDataSchema)
2524
+ };
2525
+ }
2526
+ //#endregion
2527
+ //#region src/models/preset.ts
2528
+ const log$4 = debug("model:preset");
2529
+ function _queryCompatibilityHandler(query) {
2530
+ let formatQuery = {};
2531
+ if (_.has(query, "preset_for") && query.preset_for === "amp2") formatQuery = _.assign({}, query, { preset_for: { $in: ["amp2", "bias"] } });
2532
+ else if (!_.has(query, "preset_for") || query.preset_for === "") formatQuery = _.assign({}, query, { preset_for: "jamup" });
2533
+ if (_.isEmpty(formatQuery)) formatQuery = _.clone(query);
2534
+ return formatQuery;
2535
+ }
2536
+ function _handlePresetVersion(queryObj, req) {
2537
+ const supportVersionComparison = [
2538
+ "eq",
2539
+ "gt",
2540
+ "gte",
2541
+ "lt",
2542
+ "lte",
2543
+ "ne"
2544
+ ];
2545
+ if (!_.has(req.query, "preset_for") || _.has(req.query, "preset_for") && req.query.preset_for !== "live") return _.clone(queryObj);
2546
+ else if (_.has(req.query, "preset_version") && _.has(req.query, "version_comparison")) {
2547
+ if (_.indexOf(supportVersionComparison, req.query.version_comparison) !== -1) {
2548
+ const tmpVersionQuery = _.clone(queryObj);
2549
+ const versionQuery = _buildPresetVersionQuery(req.query.preset_version, req.query.version_comparison);
2550
+ if (versionQuery) return _.merge(tmpVersionQuery, versionQuery);
2551
+ }
2552
+ }
2553
+ return _.clone(queryObj);
2554
+ }
2555
+ function _buildPresetVersionQuery(version, comparison) {
2556
+ const tmpAttrs = String(version).split(".");
2557
+ if (tmpAttrs.length !== 2) return false;
2558
+ const major = parseInt(tmpAttrs[0], 10);
2559
+ const minor = parseInt(tmpAttrs[1], 10);
2560
+ switch (comparison) {
2561
+ case "eq": return { $and: [{ "version.major": major }, { "version.minor": minor }] };
2562
+ case "gt":
2563
+ case "gte":
2564
+ case "lt":
2565
+ case "lte": {
2566
+ const compareOper = `$${comparison}`;
2567
+ const minorQuery = {};
2568
+ minorQuery[compareOper] = minor;
2569
+ return { $and: [{ "version.major": major }, { "version.minor": minorQuery }] };
2570
+ }
2571
+ default: return false;
2572
+ }
2573
+ }
2574
+ function _buildSortQuery(orderParam) {
2575
+ switch (orderParam) {
2576
+ case "popular": return { num_likes: -1 };
2577
+ case "alphabet": return { name: 1 };
2578
+ case "hot": return { num_downloads: -1 };
2579
+ default: return { created_on: -1 };
2580
+ }
2581
+ }
2582
+ function _buildLicenseTierQuery(licenseTierParam) {
2583
+ const tmpLicenseTier = String(licenseTierParam).split(",");
2584
+ const tmpData = /* @__PURE__ */ new Set();
2585
+ const expansionData = /* @__PURE__ */ new Set();
2586
+ const allExpansion = new Set(["celestion", "classic"]);
2587
+ _.forEach(tmpLicenseTier, (lic) => {
2588
+ switch (lic) {
2589
+ case "celestion":
2590
+ case "classic":
2591
+ expansionData.add(lic);
2592
+ allExpansion.delete(lic);
2593
+ break;
2594
+ case "elite":
2595
+ tmpData.add("elite");
2596
+ tmpData.add("pro");
2597
+ tmpData.add("std");
2598
+ tmpData.add(null);
2599
+ break;
2600
+ case "pro":
2601
+ tmpData.add("pro");
2602
+ tmpData.add("std");
2603
+ tmpData.add(null);
2604
+ break;
2605
+ case "std":
2606
+ tmpData.add("std");
2607
+ tmpData.add(null);
2608
+ break;
2609
+ case "lite":
2610
+ case "demo":
2611
+ tmpData.add(null);
2612
+ break;
2613
+ default: break;
2614
+ }
2615
+ });
2616
+ return expansionData.size > 0 ? { $and: [
2617
+ { license_tier: { $in: Array.from(tmpData) } },
2618
+ { license_tier: { $in: _.concat(Array.from(tmpData), Array.from(expansionData)) } },
2619
+ { license_tier: { $nin: Array.from(allExpansion) } }
2620
+ ] } : { $and: [{ license_tier: { $in: Array.from(tmpData) } }, { license_tier: { $nin: Array.from(allExpansion) } }] };
2621
+ }
2622
+ function _parsePageParams(data) {
2623
+ let pageParams = {
2624
+ page_size: 12,
2625
+ page: 1
2626
+ };
2627
+ if (data && _.has(data, "page_size")) {
2628
+ const pageSize = Array.isArray(data.page_size) ? parseInt(data.page_size.pop(), 10) : parseInt(data.page_size, 10);
2629
+ if (pageSize >= 1) pageParams = _.merge(pageParams, { page_size: pageSize });
2630
+ }
2631
+ if (data && _.has(data, "page")) {
2632
+ const page = Array.isArray(data.page) ? parseInt(data.page.pop(), 10) : parseInt(data.page, 10);
2633
+ if (page > 1) pageParams = _.merge(pageParams, { page });
2634
+ }
2635
+ return pageParams;
2636
+ }
2637
+ function _filterKeyword(keyword) {
2638
+ return String(keyword).replace(/[/\\@%&]+/g, "");
2639
+ }
2640
+ function buildPreset(mongoose) {
2641
+ const { Schema } = mongoose;
2642
+ const ObjectId = Schema.Types.ObjectId;
2643
+ const deepPopulate = deepPopulateFactory(mongoose);
2644
+ const PresetSchema = new Schema({
2645
+ name: {
2646
+ type: String,
2647
+ index: true,
2648
+ required: true
2649
+ },
2650
+ creator_id: {
2651
+ type: ObjectId,
2652
+ ref: "User",
2653
+ required: true
2654
+ },
2655
+ preset_for: {
2656
+ type: String,
2657
+ required: true
2658
+ },
2659
+ status: {
2660
+ type: Number,
2661
+ default: 0,
2662
+ required: true
2663
+ },
2664
+ description: { type: String },
2665
+ tags: {
2666
+ type: [{ type: String }],
2667
+ default: () => null
2668
+ },
2669
+ is_featured: {
2670
+ type: Boolean,
2671
+ default: false
2672
+ },
2673
+ category: {
2674
+ type: String,
2675
+ index: true
2676
+ },
2677
+ active_effects: { type: String },
2678
+ preset_data: { type: String },
2679
+ preset_features: {
2680
+ type: Schema.Types.Mixed,
2681
+ default: null
2682
+ },
2683
+ version: {
2684
+ major: { type: Number },
2685
+ minor: { type: Number }
2686
+ },
2687
+ preset_meta: { type: Schema.Types.Mixed },
2688
+ preset_scene: { type: Array },
2689
+ license_tier: {
2690
+ type: [{ type: String }],
2691
+ index: true,
2692
+ default: () => null
2693
+ },
2694
+ num_comments: {
2695
+ type: Number,
2696
+ default: 0
2697
+ },
2698
+ num_downloads: {
2699
+ type: Number,
2700
+ default: 0
2701
+ },
2702
+ num_likes: {
2703
+ type: Number,
2704
+ default: 0
2705
+ },
2706
+ rating_sum: {
2707
+ type: Number,
2708
+ default: 0
2709
+ },
2710
+ rating_avg: {
2711
+ type: Number,
2712
+ default: 0
2713
+ },
2714
+ rating_count: {
2715
+ type: Number,
2716
+ default: 0
2717
+ },
2718
+ image_url: { type: String },
2719
+ thumb_url: { type: String },
2720
+ signal_chain_type: {
2721
+ type: String,
2722
+ enum: ["in1", "in2"]
2723
+ },
2724
+ created_on: {
2725
+ type: Date,
2726
+ default: Date.now
2727
+ },
2728
+ updated_on: {
2729
+ type: Date,
2730
+ default: Date.now
2731
+ }
2732
+ }, {
2733
+ collection: "jamup_preset",
2734
+ toJSON: { virtuals: true },
2735
+ toObject: { virtuals: true }
2736
+ });
2737
+ PresetSchema.index({
2738
+ name: 1,
2739
+ description: 1,
2740
+ category: 1
2741
+ });
2742
+ PresetSchema.index({
2743
+ preset_for: 1,
2744
+ status: 1
2745
+ });
2746
+ PresetSchema.index({
2747
+ preset_for: 1,
2748
+ signal_chain_type: 1,
2749
+ status: 1
2750
+ });
2751
+ const PresetSongMappingSchema = new Schema({
2752
+ preset_id: {
2753
+ type: ObjectId,
2754
+ ref: "Preset",
2755
+ required: true
2756
+ },
2757
+ song_id: {
2758
+ type: String,
2759
+ required: true
2760
+ },
2761
+ created_on: {
2762
+ type: Date,
2763
+ default: Date.now
2764
+ }
2765
+ }, { collection: "preset_song_mapping" });
2766
+ PresetSongMappingSchema.index({ song_id: 1 });
2767
+ PresetSongMappingSchema.index({
2768
+ preset_id: 1,
2769
+ song_id: 1
2770
+ }, { unique: true });
2771
+ const PresetTagsMetaSchema = new Schema({
2772
+ name: {
2773
+ type: String,
2774
+ required: true
2775
+ },
2776
+ count: {
2777
+ type: Number,
2778
+ default: 0,
2779
+ required: true
2780
+ },
2781
+ preset_for: {
2782
+ type: String,
2783
+ required: true
2784
+ },
2785
+ created_on: {
2786
+ type: Date,
2787
+ default: Date.now
2788
+ }
2789
+ }, { collection: "preset_tags_meta" });
2790
+ PresetTagsMetaSchema.index({
2791
+ name: 1,
2792
+ preset_for: 1
2793
+ }, { unique: true });
2794
+ PresetTagsMetaSchema.index({ count: -1 });
2795
+ const PresetUserComment = new Schema({
2796
+ user_id: {
2797
+ type: ObjectId,
2798
+ ref: "User",
2799
+ required: true
2800
+ },
2801
+ preset_id: {
2802
+ type: ObjectId,
2803
+ ref: "Preset",
2804
+ required: true
2805
+ },
2806
+ comment_text: {
2807
+ type: String,
2808
+ required: true
2809
+ },
2810
+ preset_for: { type: String },
2811
+ status: {
2812
+ type: Number,
2813
+ default: 1,
2814
+ required: true
2815
+ },
2816
+ date_comment: {
2817
+ type: Date,
2818
+ default: Date.now
2819
+ }
2820
+ }, { collection: "jamup_userpresetcomment" });
2821
+ const PresetUserLike = new Schema({
2822
+ user_id: {
2823
+ type: ObjectId,
2824
+ ref: "User",
2825
+ required: true
2826
+ },
2827
+ preset_id: {
2828
+ type: ObjectId,
2829
+ ref: "Preset",
2830
+ required: true
2831
+ },
2832
+ preset_for: { type: String },
2833
+ date_like: {
2834
+ type: Date,
2835
+ default: Date.now
2836
+ }
2837
+ }, { collection: "jamup_userpresetlike" });
2838
+ const FakePresetLikeSchema = new Schema({
2839
+ preset_id: {
2840
+ type: ObjectId,
2841
+ ref: "Preset",
2842
+ required: true
2843
+ },
2844
+ likes: {
2845
+ type: Number,
2846
+ default: 0
2847
+ }
2848
+ }, { collection: "fake_userpresetlike" });
2849
+ const PresetUserDownload = new Schema({
2850
+ user_id: {
2851
+ type: ObjectId,
2852
+ ref: "User",
2853
+ required: true
2854
+ },
2855
+ preset_id: {
2856
+ type: ObjectId,
2857
+ ref: "Preset",
2858
+ required: true
2859
+ },
2860
+ preset_for: {
2861
+ type: String,
2862
+ reqired: true
2863
+ },
2864
+ date_download: {
2865
+ type: Date,
2866
+ default: Date.now
2867
+ },
2868
+ updated_on: {
2869
+ type: Date,
2870
+ default: Date.now
2871
+ }
2872
+ }, { collection: "jamup_userpresetdownload" });
2873
+ const PopularPresetList = new Schema({
2874
+ preset_for: {
2875
+ type: String,
2876
+ required: true
2877
+ },
2878
+ popular_range: {
2879
+ type: String,
2880
+ required: true
2881
+ },
2882
+ popular_preset_list: [{
2883
+ type: ObjectId,
2884
+ ref: "Preset",
2885
+ required: true
2886
+ }],
2887
+ created_on: {
2888
+ type: Date,
2889
+ default: Date.now,
2890
+ required: true
2891
+ }
2892
+ }, { collection: "jamup_popularpresetlist" });
2893
+ PresetSchema.plugin(deepPopulate, {});
2894
+ PresetUserLike.plugin(deepPopulate, {});
2895
+ PresetUserComment.plugin(deepPopulate, {});
2896
+ PresetSchema.static({
2897
+ getPresetByUserId(userId, cb) {
2898
+ this.find({ creator_id: userId }).sort({ created_on: -1 }).then((docs) => cb(null, docs), (err) => cb(err));
2899
+ },
2900
+ async getPresetByQuery(req, opts = {}) {
2901
+ if (!_.has(req.query, "keyword") || !_.isString(req.query.keyword)) throw new Error("missing the searching term");
2902
+ const pageParams = _parsePageParams(req.query);
2903
+ let searchCondition = {
2904
+ preset_for: req.query.preset_for ? req.query.preset_for : "jamup",
2905
+ status: 0
2906
+ };
2907
+ if (_.has(req.query, "license_tier")) searchCondition = _.merge(_.clone(searchCondition), _buildLicenseTierQuery(req.query.license_tier));
2908
+ if (_.has(req.query, "order") && req.query.order === "hot") searchCondition = _.assign({}, searchCondition, { created_on: { $gte: moment().subtract(1, "month").toDate() } });
2909
+ searchCondition = _handlePresetVersion(searchCondition, req);
2910
+ const aggregatePipeline = [
2911
+ { $search: {
2912
+ index: "default",
2913
+ compound: { should: [{ text: {
2914
+ path: "name",
2915
+ query: req.query.keyword,
2916
+ fuzzy: {
2917
+ maxEdits: 1,
2918
+ prefixLength: 1,
2919
+ maxExpansions: 30
2920
+ },
2921
+ score: { boost: { value: 3 } }
2922
+ } }, { text: {
2923
+ path: ["description", "category"],
2924
+ query: req.query.keyword,
2925
+ fuzzy: {
2926
+ maxEdits: 2,
2927
+ prefixLength: 0,
2928
+ maxExpansions: 50
2929
+ }
2930
+ } }] }
2931
+ } },
2932
+ { $match: searchCondition },
2933
+ { $skip: (pageParams.page - 1) * pageParams.page_size },
2934
+ { $limit: pageParams.page_size }
2935
+ ];
2936
+ if (!_.has(opts, "full")) aggregatePipeline.push({ $project: { preset_data: 0 } });
2937
+ return this.aggregate(aggregatePipeline);
2938
+ },
2939
+ async getPresetsBySearch(req) {
2940
+ const pageParams = _parsePageParams(req.query);
2941
+ const sortCondition = _buildSortQuery(req.query.order);
2942
+ let searchCondition = {
2943
+ preset_for: req.query.preset_for ? req.query.preset_for : "jamup",
2944
+ status: 0
2945
+ };
2946
+ let selectOps = {};
2947
+ if (!_.has(req.query, "detail") || req.query.detail !== "yes") selectOps = { preset_data: 0 };
2948
+ if (_.has(req.query, "pids")) {
2949
+ const pids = req.query.pids.split(",");
2950
+ searchCondition._id = { $in: pids };
2951
+ }
2952
+ if (_.has(req.query, "category")) searchCondition.category = { $in: String(req.query.category).split(",") };
2953
+ else if (_.has(req.query, "except_cateogry")) searchCondition.category = { $nin: String(req.query.except_cateogry).split(",") };
2954
+ if (_.has(req.query, "pedal_type")) searchCondition["preset_meta.pedal_type"] = req.query.pedal_type;
2955
+ if (_.has(req.query, "preset_features")) {
2956
+ const params = req.query.preset_features.split(",");
2957
+ const featureQuery = {};
2958
+ _.forEach(params, (param) => {
2959
+ const t = param.split(":");
2960
+ if (t.length === 2) featureQuery[t[0]] = parseInt(t[1], 10);
2961
+ });
2962
+ searchCondition.preset_features = featureQuery;
2963
+ }
2964
+ const createQuery = {};
2965
+ if (_.has(req.query, "create_after")) {
2966
+ const d = moment(req.query.create_after);
2967
+ if (d.isValid()) createQuery.$gte = d.toDate();
2968
+ }
2969
+ if (_.has(req.query, "create_before")) {
2970
+ const d = moment(req.query.create_before);
2971
+ if (d.isValid()) createQuery.$lte = d.toDate();
2972
+ }
2973
+ if (!_.isEmpty(createQuery)) searchCondition.created_on = createQuery;
2974
+ const updateQuery = {};
2975
+ if (_.has(req.query, "update_after")) {
2976
+ const d = moment(req.query.update_after);
2977
+ if (d.isValid()) updateQuery.$gte = d.toDate();
2978
+ }
2979
+ if (_.has(req.query, "update_before")) {
2980
+ const d = moment(req.query.update_before);
2981
+ if (d.isValid()) updateQuery.$lte = d.toDate();
2982
+ }
2983
+ if (!_.isEmpty(updateQuery)) searchCondition.updated_on = updateQuery;
2984
+ if (_.has(req.query, "license_tier")) searchCondition = _.merge(_.clone(searchCondition), _buildLicenseTierQuery(req.query.license_tier));
2985
+ if (_.has(req.query, "order") && req.query.order === "hot") searchCondition = _.assign({}, searchCondition, { created_on: { $gte: moment().subtract(1, "month").toDate() } });
2986
+ if (searchCondition.preset_for === "spark") {
2987
+ if (_.has(req.query, "signal_chain_type")) searchCondition.signal_chain_type = req.query.signal_chain_type;
2988
+ if (_.has(req.query, "no_dspid")) searchCondition["preset_meta.dspId"] = { $nin: String(req.query.no_dspid).split(",") };
2989
+ }
2990
+ searchCondition = _handlePresetVersion(searchCondition, req);
2991
+ if (_.has(req, "query.keyword") && !_.isEmpty(req.query.keyword)) {
2992
+ if (_.has(process.env, "IS_CHINA")) {
2993
+ searchCondition.$or = [{ name: new RegExp(`^${_filterKeyword(req.query.keyword)}`, "i") }, { description: new RegExp(`${_filterKeyword(req.query.keyword)}`, "i") }];
2994
+ return this.aggregate([
2995
+ { $match: _queryCompatibilityHandler(searchCondition) },
2996
+ { $sort: sortCondition },
2997
+ { $project: selectOps },
2998
+ { $skip: (pageParams.page - 1) * pageParams.page_size },
2999
+ { $limit: pageParams.page_size },
3000
+ { $addFields: { id: "$_id" } }
3001
+ ]);
3002
+ }
3003
+ return this.aggregate([
3004
+ { $search: { text: {
3005
+ path: ["name", "description"],
3006
+ query: req.query.keyword
3007
+ } } },
3008
+ { $match: _queryCompatibilityHandler(searchCondition) },
3009
+ { $sort: sortCondition },
3010
+ { $project: selectOps },
3011
+ { $skip: (pageParams.page - 1) * pageParams.page_size },
3012
+ { $limit: pageParams.page_size },
3013
+ { $addFields: { id: "$_id" } }
3014
+ ]);
3015
+ }
3016
+ return this.aggregate([
3017
+ { $match: _queryCompatibilityHandler(searchCondition) },
3018
+ { $sort: sortCondition },
3019
+ { $project: selectOps },
3020
+ { $skip: (pageParams.page - 1) * pageParams.page_size },
3021
+ { $limit: pageParams.page_size },
3022
+ { $addFields: { id: "$_id" } }
3023
+ ]);
3024
+ },
3025
+ getPresetsList(query, sort, req, cb) {
3026
+ const pageParams = _parsePageParams(req.query);
3027
+ let q = query;
3028
+ if (_.isObject(q)) q.status = 0;
3029
+ if (_.has(q, "preset_for") && q.preset_for !== "jamup") {
3030
+ q.image_url = { $ne: null };
3031
+ q.thumb_url = { $ne: null };
3032
+ }
3033
+ if (_.has(req.query, "license_tier")) q = _.merge(_.clone(q), _buildLicenseTierQuery(req.query.license_tier));
3034
+ let finalSort = sort;
3035
+ if (_.has(req.query, "order")) finalSort = _buildSortQuery(req.query.order);
3036
+ q = _handlePresetVersion(q, req);
3037
+ this.find(_queryCompatibilityHandler(q)).sort(finalSort).select("-preset_data").skip((pageParams.page - 1) * pageParams.page_size).limit(pageParams.page_size).then((docs) => cb(null, docs), (err) => cb(err));
3038
+ },
3039
+ getPresetsListPromise(query, sort, req) {
3040
+ return new Promise((resolve, reject) => {
3041
+ this.getPresetsList(query, sort, req, (err, likes) => err ? reject(err) : resolve(likes));
3042
+ });
3043
+ },
3044
+ getMyPresets(query, sort, req, cb) {
3045
+ const pageParams = _parsePageParams(req.query);
3046
+ let q = query;
3047
+ if (_.isObject(q)) q.status = { $in: [0, 2] };
3048
+ if (_.has(q, "preset_for") && q.preset_for !== "jamup") {
3049
+ q.image_url = { $ne: null };
3050
+ q.thumb_url = { $ne: null };
3051
+ }
3052
+ q = _handlePresetVersion(q, req);
3053
+ this.find(_queryCompatibilityHandler(q)).sort(sort).select("-preset_data").skip((pageParams.page - 1) * pageParams.page_size).limit(pageParams.page_size).then((docs) => cb(null, docs), (err) => cb(err));
3054
+ },
3055
+ getOnePresetByCondition(query, opts, cb) {
3056
+ const defaultSort = _.has(opts, "sort") && _.isObject(opts.sort) ? opts.sort : { created_on: -1 };
3057
+ const defaultSelect = _.has(opts, "select") && _.isString(opts.select) ? opts.select : "";
3058
+ this.findOne(_queryCompatibilityHandler(query)).sort(defaultSort).select(defaultSelect).then((doc) => cb(null, doc), (err) => cb(err));
3059
+ },
3060
+ getSinglePreset(id, cb) {
3061
+ this.findOne({
3062
+ _id: id,
3063
+ status: 0
3064
+ }).then((d) => cb(null, d), (err) => cb(err));
3065
+ },
3066
+ getValidSinglePreset(id, cb) {
3067
+ this.findOne({
3068
+ _id: id,
3069
+ status: { $in: [
3070
+ 0,
3071
+ 2,
3072
+ 3
3073
+ ] }
3074
+ }).then((d) => cb(null, d), (err) => cb(err));
3075
+ }
3076
+ });
3077
+ PresetUserComment.static({ async getPresetComments(presetId, req) {
3078
+ try {
3079
+ const pageParams = _parsePageParams(req.query);
3080
+ return await this.aggregate([
3081
+ { $match: {
3082
+ preset_id: new mongoose.Types.ObjectId(presetId),
3083
+ status: 1
3084
+ } },
3085
+ { $sort: { date_comment: -1 } },
3086
+ { $skip: (pageParams.page - 1) * pageParams.page_size },
3087
+ { $limit: pageParams.page_size },
3088
+ { $lookup: {
3089
+ from: "auth_user",
3090
+ localField: "user_id",
3091
+ foreignField: "_id",
3092
+ as: "tmpUser"
3093
+ } },
3094
+ { $lookup: {
3095
+ from: "jamup_userprofile",
3096
+ localField: "user_id",
3097
+ foreignField: "user_id",
3098
+ as: "tmpProfile"
3099
+ } },
3100
+ { $unwind: "$tmpUser" },
3101
+ { $unwind: "$tmpProfile" },
3102
+ { $project: {
3103
+ preset_id: 1,
3104
+ comment_text: 1,
3105
+ date_comment: 1,
3106
+ user: {
3107
+ _id: "$tmpUser._id",
3108
+ email: "$tmpUser.email",
3109
+ date_joined: "$tmpUser.date_joined",
3110
+ full_name: "$tmpProfile.full_name",
3111
+ first_name: "$tmpProfile.first_name",
3112
+ last_name: "$tmpProfile.last_name",
3113
+ profile_image_url: "$tmpProfile.profile_image_url",
3114
+ profile_thumb_url: "$tmpProfile.profile_thumb_url"
3115
+ }
3116
+ } }
3117
+ ]).exec();
3118
+ } catch (err) {
3119
+ log$4("ERR:getPresetComment:%o", err);
3120
+ throw err;
3121
+ }
3122
+ } });
3123
+ PresetUserLike.static({
3124
+ getCountByPresetCondition(query, condition, cb) {
3125
+ this.find(query).populate("preset_id").then((result) => {
3126
+ return cb(null, _.filter(result, (item) => {
3127
+ const tmpObj = item.toObject ? item.toObject() : item;
3128
+ return _.has(tmpObj.preset_id, condition);
3129
+ }).length);
3130
+ }, (err) => cb(err));
3131
+ },
3132
+ getPresetLikeList(query, sort, req, cb) {
3133
+ const pageParams = _parsePageParams(req.query);
3134
+ const defaultPresetCondition = { "preset.status": { $eq: 0 } };
3135
+ let filterQuery = {};
3136
+ if (_.has(query, "no_dspid")) {
3137
+ defaultPresetCondition["preset.preset_meta.dspId"] = { $nin: String(query.no_dspid).split(",") };
3138
+ filterQuery = _.omit(query, ["no_dspid"]);
3139
+ } else filterQuery = _.clone(query);
3140
+ const aggregateQuery = [
3141
+ { $match: _.assign({}, _queryCompatibilityHandler(filterQuery), { user_id: new mongoose.Types.ObjectId(filterQuery.user_id) }) },
3142
+ { $lookup: {
3143
+ from: "jamup_preset",
3144
+ localField: "preset_id",
3145
+ foreignField: "_id",
3146
+ as: "preset"
3147
+ } },
3148
+ { $unwind: "$preset" },
3149
+ { $match: defaultPresetCondition }
3150
+ ];
3151
+ const presetQuery = _handlePresetVersion({}, req);
3152
+ if (!_.isEmpty(presetQuery) && _.has(presetQuery, "$and") && _.isArray(presetQuery.$and)) _.forEach(presetQuery.$and, (item) => {
3153
+ const newQuery = { $match: {} };
3154
+ const objKey = _.keys(item);
3155
+ const objVal = _.values(item);
3156
+ if (objKey.length === 1 && objVal.length === 1) {
3157
+ newQuery.$match[`preset.${objKey[0]}`] = objVal[0];
3158
+ aggregateQuery.push(newQuery);
3159
+ }
3160
+ });
3161
+ this.aggregate(aggregateQuery).sort({ date_like: -1 }).skip((pageParams.page - 1) * pageParams.page_size).limit(pageParams.page_size).then((docs) => cb(null, docs), (err) => cb(err));
3162
+ },
3163
+ getPresetLikeListPromise(query, sort, req) {
3164
+ return new Promise((resolve, reject) => {
3165
+ this.getPresetLikeList(query, sort, req, (err, likes) => err ? reject(err) : resolve(likes));
3166
+ });
3167
+ }
3168
+ });
3169
+ PresetSchema.virtual("id").get(function() {
3170
+ return this._id.toHexString();
3171
+ });
3172
+ PresetSchema.virtual("preset_version").get(function() {
3173
+ const v = this.version;
3174
+ return v && Number.isInteger(v.major) && Number.isInteger(v.minor) ? `${v.major}.${v.minor}` : "1.0";
3175
+ }).set(function(presetVersion) {
3176
+ const versionDetail = presetVersion.split(".");
3177
+ this.set("version.major", parseInt(versionDetail[0], 10));
3178
+ this.set("version.minor", parseInt(versionDetail[1], 10));
3179
+ });
3180
+ return {
3181
+ Preset: mongoose.model("Preset", PresetSchema),
3182
+ PresetComment: mongoose.model("PresetComment", PresetUserComment),
3183
+ PresetLike: mongoose.model("PresetLike", PresetUserLike),
3184
+ PresetDownload: mongoose.model("PresetDownload", PresetUserDownload),
3185
+ PopularPresetList: mongoose.model("PopularPresetList", PopularPresetList),
3186
+ FakePresetLike: mongoose.model("FakePresetLike", FakePresetLikeSchema),
3187
+ PresetTagsMeta: mongoose.model("PresetTagsMeta", PresetTagsMetaSchema),
3188
+ PresetSongMapping: mongoose.model("PresetSongMapping", PresetSongMappingSchema)
3189
+ };
3190
+ }
3191
+ //#endregion
3192
+ //#region src/models/promotion.ts
3193
+ const log$3 = debug("model:promotion");
3194
+ const CAMPAIGN_TYPE = ["free_upgrade"];
3195
+ function buildPromotion(mongoose) {
3196
+ const { Schema } = mongoose;
3197
+ const ObjectId = Schema.Types.ObjectId;
3198
+ const LicenseCampaignMetaSchema = new Schema({
3199
+ campaign_type: {
3200
+ type: String,
3201
+ enum: CAMPAIGN_TYPE,
3202
+ required: true
3203
+ },
3204
+ campaign_detail: {
3205
+ type: Schema.Types.Mixed,
3206
+ required: true
3207
+ },
3208
+ status: {
3209
+ type: Number,
3210
+ default: 1,
3211
+ required: true
3212
+ },
3213
+ start_date: {
3214
+ type: Date,
3215
+ default: null
3216
+ },
3217
+ end_date: {
3218
+ type: Date,
3219
+ default: null
3220
+ },
3221
+ created_on: {
3222
+ type: Date,
3223
+ default: Date.now
3224
+ }
3225
+ }, { collection: "license_campaign_meta" });
3226
+ const LicenseCampaignRecordSchema = new Schema({
3227
+ user_id: {
3228
+ type: ObjectId,
3229
+ ref: "auth_user",
3230
+ required: true
3231
+ },
3232
+ campaign_event_id: {
3233
+ type: ObjectId,
3234
+ ref: "license_campaign_meta",
3235
+ required: true
3236
+ },
3237
+ status: {
3238
+ type: Number,
3239
+ default: 1,
3240
+ required: true
3241
+ },
3242
+ created_on: {
3243
+ type: Date,
3244
+ default: Date.now,
3245
+ required: true
3246
+ }
3247
+ }, { collection: "license_campaign_record" });
3248
+ const PublicAnnouncementSchema = new Schema({
3249
+ title: {
3250
+ type: String,
3251
+ required: true
3252
+ },
3253
+ content: {
3254
+ type: String,
3255
+ required: true
3256
+ },
3257
+ type: {
3258
+ type: String,
3259
+ enum: ["announcement", "freetrial"]
3260
+ },
3261
+ country: [{ type: String }],
3262
+ image: {
3263
+ type: String,
3264
+ default: null
3265
+ },
3266
+ image_type: { type: String },
3267
+ message_type: { type: String },
3268
+ action_button: [{
3269
+ type: {
3270
+ type: String,
3271
+ required: true
3272
+ },
3273
+ text: {
3274
+ type: String,
3275
+ required: true
3276
+ },
3277
+ value: { type: Schema.Types.Mixed },
3278
+ read: {
3279
+ type: Boolean,
3280
+ required: true
3281
+ }
3282
+ }],
3283
+ show_countdown: {
3284
+ type: Boolean,
3285
+ default: false,
3286
+ required: true
3287
+ },
3288
+ announce_for: [{
3289
+ type: String,
3290
+ required: true
3291
+ }],
3292
+ target_audience_device: [{
3293
+ type: String,
3294
+ required: true
3295
+ }],
3296
+ device_os: { type: String },
3297
+ device_list: [{ type: String }],
3298
+ announce_license_tier: [{
3299
+ type: String,
3300
+ required: true
3301
+ }],
3302
+ announce_mobile_iap: [{
3303
+ type: String,
3304
+ required: true
3305
+ }],
3306
+ user_list_num: {
3307
+ type: Number,
3308
+ default: 0
3309
+ },
3310
+ user_list_status: {
3311
+ type: Number,
3312
+ default: 0
3313
+ },
3314
+ user_list_failure: {
3315
+ type: Number,
3316
+ default: 0
3317
+ },
3318
+ user_list_log: {
3319
+ type: String,
3320
+ default: null
3321
+ },
3322
+ user_list_filename: {
3323
+ type: String,
3324
+ default: null
3325
+ },
3326
+ start_time: {
3327
+ type: Date,
3328
+ default: null
3329
+ },
3330
+ end_time: {
3331
+ type: Date,
3332
+ default: null
3333
+ },
3334
+ locale: {
3335
+ type: String,
3336
+ required: true
3337
+ },
3338
+ order: {
3339
+ type: Number,
3340
+ default: 1
3341
+ },
3342
+ created_on: {
3343
+ type: Date,
3344
+ default: Date.now,
3345
+ required: true
3346
+ }
3347
+ }, { collection: "public_announcement" });
3348
+ PublicAnnouncementSchema.static({ async getAvailableAnnouncement(params) {
3349
+ try {
3350
+ const nowTime = /* @__PURE__ */ new Date();
3351
+ const defaultQuery = { $and: [{ $or: [{ start_time: null }, { start_time: { $lt: nowTime } }] }, { $or: [{ end_time: null }, { end_time: { $gt: nowTime } }] }] };
3352
+ if (!_.isUndefined(params)) {
3353
+ log$3("getAvailableAnnouncement:querystring %o", params);
3354
+ const { pname, locale, ta_device, country, device_list, device_os } = params;
3355
+ if (!_.isUndefined(pname) && _.isString(pname)) defaultQuery.$and.push({ announce_for: { $in: _.concat([pname], "all") } });
3356
+ if (!_.isUndefined(locale) && _.isString(locale)) defaultQuery.$and.push({ locale });
3357
+ if (!_.isUndefined(ta_device) && _.isString(ta_device)) defaultQuery.$and.push({ target_audience_device: { $in: _.concat(ta_device.split(","), "all") } });
3358
+ if (!_.isUndefined(country) && _.isString(country)) defaultQuery.$and.push({ $or: [{ country: { $all: country.split(",") } }, { country: { $in: ["all"] } }] });
3359
+ else defaultQuery.$and.push({ $or: [{ country: { $in: ["all"] } }, { country: { $exists: false } }] });
3360
+ if (!_.isUndefined(device_list)) if (_.isString(device_list) && !_.isEmpty(device_list)) {
3361
+ const tmpDeviceList = device_list.split(",");
3362
+ const deviceList = [];
3363
+ const allTypeSet = /* @__PURE__ */ new Set();
3364
+ const uniqueProductLineSet = /* @__PURE__ */ new Set();
3365
+ tmpDeviceList.forEach((deviceItem) => {
3366
+ const optionItem = deviceItem.split("_");
3367
+ uniqueProductLineSet.add(optionItem[0]);
3368
+ if (optionItem.length === 2 && optionItem[1] === "all") allTypeSet.add(optionItem[0]);
3369
+ else deviceList.push(deviceItem);
3370
+ });
3371
+ if (allTypeSet.size > 0) allTypeSet.forEach((item) => deviceList.push(new RegExp(`^${item}_`)));
3372
+ if (uniqueProductLineSet.size > 0) uniqueProductLineSet.forEach((item) => deviceList.push(`${item}_all`));
3373
+ defaultQuery.$and.push({ device_list: { $in: deviceList } });
3374
+ } else defaultQuery.$and.push({ device_list: { $in: ["none"] } });
3375
+ if (!_.isUndefined(device_os) && _.isString(device_os)) defaultQuery.$and.push({ $or: [{ device_os: "all" }, { device_os }] });
3376
+ }
3377
+ let ret;
3378
+ if (!_.has(params, "email") || _.has(params, "email") && _.isUndefined(params.email)) {
3379
+ defaultQuery.$and.push({ user_list_num: 0 });
3380
+ defaultQuery.$and.push({ action_button: { $elemMatch: { type: { $ne: "free_trial" } } } });
3381
+ ret = await this.find(defaultQuery).lean().exec();
3382
+ } else {
3383
+ const noUserQuery = _.cloneDeep(defaultQuery);
3384
+ noUserQuery.$and.push({ user_list_num: 0 });
3385
+ const hasUserQuery = _.cloneDeep(defaultQuery);
3386
+ hasUserQuery.$and.push({ user_list_num: { $gt: 0 } });
3387
+ const [noUserData, hasUserData] = await Promise.all([this.aggregate([{ $match: noUserQuery }]).exec(), this.aggregate([
3388
+ { $match: hasUserQuery },
3389
+ { $lookup: {
3390
+ from: "public_announcement_userlist",
3391
+ localField: "_id",
3392
+ foreignField: "pa_id",
3393
+ as: "paUserList"
3394
+ } },
3395
+ { $unwind: "$paUserList" },
3396
+ { $match: {
3397
+ "paUserList.email": String(params.email).toLowerCase(),
3398
+ "paUserList.status": 1
3399
+ } }
3400
+ ]).exec()]);
3401
+ ret = _.concat(noUserData, hasUserData);
3402
+ }
3403
+ return _.orderBy(ret, ["order", "start_time"], ["desc", "asc"]);
3404
+ } catch (err) {
3405
+ log$3("ERR:getAvailableAnnouncement %j", err);
3406
+ throw err;
3407
+ }
3408
+ } });
3409
+ const PublicAnnouncementFreeTrialSchema = new Schema({
3410
+ pa_id: {
3411
+ type: ObjectId,
3412
+ ref: "PublicAnnouncement",
3413
+ required: true
3414
+ },
3415
+ user_id: {
3416
+ type: ObjectId,
3417
+ ref: "User",
3418
+ required: true
3419
+ },
3420
+ product_id: {
3421
+ type: ObjectId,
3422
+ ref: "Product",
3423
+ default: null
3424
+ },
3425
+ expansion_id: {
3426
+ type: ObjectId,
3427
+ ref: "Product",
3428
+ default: null
3429
+ },
3430
+ status: {
3431
+ type: Number,
3432
+ default: 1
3433
+ },
3434
+ expire_on: {
3435
+ type: Date,
3436
+ required: true
3437
+ },
3438
+ created_on: {
3439
+ type: Date,
3440
+ default: Date.now
3441
+ }
3442
+ }, { collection: "public_announcement_free_trial" });
3443
+ PublicAnnouncementFreeTrialSchema.index({
3444
+ pa_id: 1,
3445
+ user_id: 1,
3446
+ product_id: 1,
3447
+ expansion_id: 1
3448
+ }, { unique: true });
3449
+ PublicAnnouncementFreeTrialSchema.static({ async getCurrentActiveFreeTrial(params) {
3450
+ try {
3451
+ const { userId } = params;
3452
+ if (!userId) throw new Error("Missing userId");
3453
+ const nowTime = /* @__PURE__ */ new Date();
3454
+ return await this.aggregate([
3455
+ { $match: { user_id: new mongoose.Types.ObjectId(userId) } },
3456
+ { $lookup: {
3457
+ from: "public_announcement",
3458
+ localField: "pa_id",
3459
+ foreignField: "_id",
3460
+ as: "pa"
3461
+ } },
3462
+ { $unwind: {
3463
+ path: "$pa",
3464
+ preserveNullAndEmptyArrays: false
3465
+ } },
3466
+ { $match: { $and: [{ $or: [{ "pa.start_time": null }, { "pa.start_time": { $lt: nowTime } }] }, { $or: [{ "pa.end_time": null }, { "pa.end_time": { $gt: nowTime } }] }] } }
3467
+ ]).exec();
3468
+ } catch (err) {
3469
+ log$3("ERR:getAvailableFreeTrial %o", err);
3470
+ throw err;
3471
+ }
3472
+ } });
3473
+ const PublicAnnouncementUserListSchema = new Schema({
3474
+ pa_id: {
3475
+ type: ObjectId,
3476
+ ref: "PublicAnnouncement",
3477
+ required: true
3478
+ },
3479
+ email: {
3480
+ type: String,
3481
+ required: true
3482
+ },
3483
+ user_id: {
3484
+ type: ObjectId,
3485
+ ref: "User"
3486
+ },
3487
+ status: {
3488
+ type: Number,
3489
+ default: 1,
3490
+ required: true
3491
+ },
3492
+ created_on: {
3493
+ type: Date,
3494
+ default: Date.now,
3495
+ required: true
3496
+ }
3497
+ }, { collection: "public_announcement_userlist" });
3498
+ PublicAnnouncementUserListSchema.static({ async getAvailableAnnouncementByEmail(email) {
3499
+ try {
3500
+ const nowTime = /* @__PURE__ */ new Date();
3501
+ return this.aggregate([
3502
+ { $match: {
3503
+ email: String(email).toLowerCase(),
3504
+ status: 1
3505
+ } },
3506
+ { $lookup: {
3507
+ from: "public_announcement",
3508
+ localField: "pa_id",
3509
+ foreignField: "_id",
3510
+ as: "pa"
3511
+ } },
3512
+ { $unwind: "$pa" },
3513
+ { $match: { $and: [
3514
+ { $or: [{ "pa.start_time": null }, { "pa.start_time": { $lt: nowTime } }] },
3515
+ { $or: [{ "pa.end_time": null }, { "pa.end_time": { $gt: nowTime } }] },
3516
+ { "pa.user_list_num": { $gt: 0 } }
3517
+ ] } }
3518
+ ]).exec();
3519
+ } catch (err) {
3520
+ log$3("ERR:getAnnouncementFromUserList:%o", err);
3521
+ throw err;
3522
+ }
3523
+ } });
3524
+ const PublicAnnouncementUserListLog = new Schema({
3525
+ pa_id: {
3526
+ type: ObjectId,
3527
+ ref: "PublicAnnouncement",
3528
+ required: true
3529
+ },
3530
+ message: { type: Schema.Types.Mixed }
3531
+ }, { collection: "public_announcement_userlist_log" });
3532
+ const IasBuyOptionSchema = new Schema({
3533
+ type: {
3534
+ type: String,
3535
+ required: true
3536
+ },
3537
+ sku: {
3538
+ type: String,
3539
+ required: true
3540
+ },
3541
+ description: {
3542
+ type: String,
3543
+ required: true
3544
+ },
3545
+ cta_name: {
3546
+ type: String,
3547
+ required: true
3548
+ },
3549
+ created_on: {
3550
+ type: Date,
3551
+ default: Date.now,
3552
+ required: true
3553
+ },
3554
+ locale: {
3555
+ type: String,
3556
+ required: true
3557
+ }
3558
+ }, { collection: "ias_buy_option" });
3559
+ const IasStorePageMetaSchema = new Schema({
3560
+ product: {
3561
+ type: String,
3562
+ required: true
3563
+ },
3564
+ expansion: {
3565
+ type: Boolean,
3566
+ default: false
3567
+ },
3568
+ license: {
3569
+ type: String,
3570
+ required: true
3571
+ },
3572
+ buy_option: [{
3573
+ type: ObjectId,
3574
+ ref: "IasBuyOption",
3575
+ required: true
3576
+ }],
3577
+ created_on: {
3578
+ type: Date,
3579
+ default: Date.now,
3580
+ required: true
3581
+ },
3582
+ promotion_target_audiance: {
3583
+ type: String,
3584
+ required: true
3585
+ },
3586
+ promotion_discount: {
3587
+ type: String,
3588
+ required: true
3589
+ },
3590
+ product_description: {
3591
+ type: String,
3592
+ required: true
3593
+ },
3594
+ quote_image: {
3595
+ type: String,
3596
+ required: true
3597
+ },
3598
+ locale: {
3599
+ type: String,
3600
+ required: true
3601
+ },
3602
+ cta_image: { type: String },
3603
+ cta_button_text: { type: String },
3604
+ cta_button_show_svg: { type: Boolean },
3605
+ cta_button_action_url: { type: String }
3606
+ }, { collection: "ias_store_page_meta" });
3607
+ const IasPromotionSchema = new Schema({
3608
+ start_time: {
3609
+ type: Date,
3610
+ required: true
3611
+ },
3612
+ end_time: {
3613
+ type: Date,
3614
+ required: true
3615
+ },
3616
+ quote_image: {
3617
+ type: String,
3618
+ required: true
3619
+ },
3620
+ created_on: {
3621
+ type: Date,
3622
+ default: Date.now,
3623
+ required: true
3624
+ }
3625
+ }, { collection: "ias_promotion" });
3626
+ const SpecialOfferSchema = new Schema({
3627
+ title: {
3628
+ type: String,
3629
+ required: true
3630
+ },
3631
+ content: {
3632
+ type: String,
3633
+ required: true
3634
+ },
3635
+ image: {
3636
+ type: String,
3637
+ default: null
3638
+ },
3639
+ action_button: [{
3640
+ type: {
3641
+ type: String,
3642
+ required: true
3643
+ },
3644
+ text: {
3645
+ type: String,
3646
+ required: true
3647
+ },
3648
+ value: { type: Schema.Types.Mixed },
3649
+ read: {
3650
+ type: Boolean,
3651
+ required: true
3652
+ }
3653
+ }],
3654
+ announce_for: [{
3655
+ type: String,
3656
+ required: true
3657
+ }],
3658
+ target_audience_device: [{
3659
+ type: String,
3660
+ required: true
3661
+ }],
3662
+ start_time: {
3663
+ type: Date,
3664
+ default: null
3665
+ },
3666
+ end_time: {
3667
+ type: Date,
3668
+ default: null
3669
+ },
3670
+ created_on: {
3671
+ type: Date,
3672
+ default: Date.now,
3673
+ required: true
3674
+ },
3675
+ locale: {
3676
+ type: String,
3677
+ required: true
3678
+ }
3679
+ }, { collection: "special_offer" });
3680
+ const PromotionSchema = new Schema({
3681
+ name: {
3682
+ type: String,
3683
+ required: true
3684
+ },
3685
+ start_date: {
3686
+ type: Date,
3687
+ required: true
3688
+ },
3689
+ end_date: {
3690
+ type: Date,
3691
+ required: true
3692
+ },
3693
+ items: [{
3694
+ sku: {
3695
+ type: String,
3696
+ required: true
3697
+ },
3698
+ sale_price: {
3699
+ type: Number,
3700
+ required: true
3701
+ },
3702
+ price: {
3703
+ type: Number,
3704
+ required: true
3705
+ },
3706
+ name: {
3707
+ type: String,
3708
+ required: true
3709
+ }
3710
+ }]
3711
+ }, { collection: "promotion" });
3712
+ const CampaignUserInfoSchema = new Schema({
3713
+ campaign: {
3714
+ type: String,
3715
+ required: true
3716
+ },
3717
+ email: {
3718
+ type: String,
3719
+ required: true
3720
+ },
3721
+ name: { type: String },
3722
+ redeem: { type: String },
3723
+ metadata: { type: Schema.Types.Mixed },
3724
+ created_on: {
3725
+ type: Date,
3726
+ default: Date.now
3727
+ }
3728
+ }, { collection: "campaign_user_info" });
3729
+ CampaignUserInfoSchema.index({ campaign: 1 });
3730
+ CampaignUserInfoSchema.index({
3731
+ campaign: 1,
3732
+ email: 1
3733
+ }, { unique: true });
3734
+ CampaignUserInfoSchema.index({
3735
+ campaign: 1,
3736
+ email: 1,
3737
+ redeem: 1
3738
+ });
3739
+ const CouponPromotionSchema = new Schema({
3740
+ email: {
3741
+ type: String,
3742
+ required: true
3743
+ },
3744
+ code: {
3745
+ type: String,
3746
+ required: true
3747
+ },
3748
+ referral: { type: String },
3749
+ event: { type: String },
3750
+ type: { type: String },
3751
+ metadata: { type: Schema.Types.Mixed },
3752
+ status: {
3753
+ type: Number,
3754
+ default: 1,
3755
+ required: true
3756
+ },
3757
+ created_on: {
3758
+ type: Date,
3759
+ default: Date.now,
3760
+ required: true
3761
+ }
3762
+ }, { collection: "coupon_promotion" });
3763
+ CouponPromotionSchema.index({
3764
+ email: 1,
3765
+ event: 1
3766
+ });
3767
+ CouponPromotionSchema.index({
3768
+ email: 1,
3769
+ event: 1,
3770
+ referral: 1
3771
+ }, { unique: true });
3772
+ const TierSalesMetaSchema = new Schema({
3773
+ campaign_key: {
3774
+ type: String,
3775
+ required: true
3776
+ },
3777
+ current_tier_sales_rule: {
3778
+ type: ObjectId,
3779
+ ref: "TierSalesRules"
3780
+ },
3781
+ current_tier_startdate: {
3782
+ type: Date,
3783
+ default: null
3784
+ },
3785
+ process_log: [{
3786
+ from_tier_id: {
3787
+ type: ObjectId,
3788
+ ref: "TierSalesRules",
3789
+ required: true
3790
+ },
3791
+ to_tier_id: {
3792
+ type: ObjectId,
3793
+ ref: "TierSalesRules",
3794
+ required: true
3795
+ },
3796
+ updated_on: {
3797
+ type: Date,
3798
+ required: true
3799
+ }
3800
+ }],
3801
+ status: {
3802
+ type: Number,
3803
+ default: 1
3804
+ },
3805
+ updated_on: {
3806
+ type: Date,
3807
+ default: Date.now
3808
+ }
3809
+ }, { collection: "tier_sales_meta" });
3810
+ const TierSalesRulesSchema = new Schema({
3811
+ campaign_key: {
3812
+ type: String,
3813
+ required: true
3814
+ },
3815
+ tier_level: {
3816
+ type: Number,
3817
+ default: 1,
3818
+ required: true
3819
+ },
3820
+ tier_type: {
3821
+ type: Number,
3822
+ default: 0
3823
+ },
3824
+ pre_rule_id: {
3825
+ type: ObjectId,
3826
+ ref: "TierSalesRules"
3827
+ },
3828
+ price: {
3829
+ type: Number,
3830
+ required: true
3831
+ },
3832
+ ta_sku: {
3833
+ type: String,
3834
+ required: true
3835
+ },
3836
+ next_sku: {
3837
+ type: String,
3838
+ default: null
3839
+ },
3840
+ goal: [{
3841
+ type: { type: String },
3842
+ value: { type: Number }
3843
+ }],
3844
+ force_switch_tier: {
3845
+ type: Date,
3846
+ default: null
3847
+ },
3848
+ is_final_tier: {
3849
+ type: Boolean,
3850
+ default: false
3851
+ },
3852
+ status: {
3853
+ type: Number,
3854
+ default: 1
3855
+ },
3856
+ created_on: {
3857
+ type: Date,
3858
+ default: Date.now
3859
+ }
3860
+ }, { collection: "tier_sales_rules" });
3861
+ const GearTrialMetaSchema = new Schema({
3862
+ title: {
3863
+ type: String,
3864
+ required: true
3865
+ },
3866
+ announce_for: [{
3867
+ type: String,
3868
+ required: true
3869
+ }],
3870
+ target_audience_device: [{
3871
+ type: String,
3872
+ required: true
3873
+ }],
3874
+ locale: {
3875
+ type: String,
3876
+ required: true
3877
+ },
3878
+ target_product_sku: [{
3879
+ type: String,
3880
+ required: true
3881
+ }],
3882
+ expires: {
3883
+ type: Number,
3884
+ required: true
3885
+ },
3886
+ status: {
3887
+ type: Number,
3888
+ default: 1,
3889
+ required: true
3890
+ },
3891
+ created_on: {
3892
+ type: Date,
3893
+ default: Date.now,
3894
+ required: true
3895
+ }
3896
+ }, { collection: "gear_trial_meta" });
3897
+ GearTrialMetaSchema.index({ status: 1 });
3898
+ const GearTrialRecordSchema = new Schema({
3899
+ gear_trial_id: {
3900
+ type: ObjectId,
3901
+ ref: "gear_trial_meta",
3902
+ required: true
3903
+ },
3904
+ user_id: {
3905
+ type: ObjectId,
3906
+ ref: "auth_user",
3907
+ required: true
3908
+ },
3909
+ expire_on: {
3910
+ type: Date,
3911
+ required: true
3912
+ },
3913
+ created_on: {
3914
+ type: Date,
3915
+ default: Date.now,
3916
+ required: true
3917
+ }
3918
+ }, { collection: "gear_trial_record" });
3919
+ GearTrialRecordSchema.index({
3920
+ gear_trial_id: 1,
3921
+ user_id: 1
3922
+ });
3923
+ GearTrialRecordSchema.index({
3924
+ user_id: 1,
3925
+ expire_on: 1
3926
+ });
3927
+ const YotpoLoyaltySchema = new Schema({
3928
+ user_id: {
3929
+ type: ObjectId,
3930
+ ref: "auth_user",
3931
+ required: true
3932
+ },
3933
+ main_region: {
3934
+ type: String,
3935
+ required: true
3936
+ },
3937
+ active_regions: [{
3938
+ code: {
3939
+ type: String,
3940
+ required: true
3941
+ },
3942
+ points_balance: {
3943
+ type: Number,
3944
+ default: 0,
3945
+ required: true
3946
+ },
3947
+ points_earned: {
3948
+ type: Number,
3949
+ default: 0,
3950
+ required: true
3951
+ }
3952
+ }],
3953
+ history: [{
3954
+ created_at: {
3955
+ type: String,
3956
+ required: true
3957
+ },
3958
+ date: {
3959
+ type: String,
3960
+ required: true
3961
+ },
3962
+ completed_on: {
3963
+ type: String,
3964
+ default: null
3965
+ },
3966
+ action: {
3967
+ type: String,
3968
+ default: null
3969
+ },
3970
+ points: {
3971
+ type: Number,
3972
+ default: 0
3973
+ },
3974
+ status: {
3975
+ type: String,
3976
+ default: null
3977
+ },
3978
+ action_name: {
3979
+ type: String,
3980
+ default: null
3981
+ },
3982
+ order_ids: [{ type: String }]
3983
+ }],
3984
+ created_on: {
3985
+ type: Date,
3986
+ default: Date.now,
3987
+ required: true
3988
+ },
3989
+ updated_on: {
3990
+ type: Date,
3991
+ default: Date.now,
3992
+ required: true
3993
+ }
3994
+ }, { collection: "auth_yotpo_loyalty" });
3995
+ YotpoLoyaltySchema.index({ user_id: 1 }, { unique: true });
3996
+ YotpoLoyaltySchema.index({
3997
+ user_id: 1,
3998
+ main_region: 1,
3999
+ "active_regions.code": 1
4000
+ });
4001
+ const YotpoLoyaltyBackupSchema = new Schema({
4002
+ email: {
4003
+ type: String,
4004
+ required: true
4005
+ },
4006
+ store_code: {
4007
+ type: String,
4008
+ required: true
4009
+ },
4010
+ status: {
4011
+ type: Number,
4012
+ default: 1,
4013
+ required: true
4014
+ },
4015
+ created_on: {
4016
+ type: Date,
4017
+ default: Date.now,
4018
+ required: true
4019
+ }
4020
+ }, { collection: "yotpo_loyalty_backup" });
4021
+ YotpoLoyaltyBackupSchema.index({
4022
+ email: 1,
4023
+ store_code: 1
4024
+ }, { unique: true });
4025
+ const FreeTrialSchema = new Schema({
4026
+ campaign_name: {
4027
+ type: String,
4028
+ required: true
4029
+ },
4030
+ campaign_code: {
4031
+ type: String,
4032
+ required: true
4033
+ },
4034
+ content: {
4035
+ type: String,
4036
+ default: null
4037
+ },
4038
+ products: [{
4039
+ type: ObjectId,
4040
+ ref: "Product",
4041
+ required: true
4042
+ }],
4043
+ status: {
4044
+ type: Number,
4045
+ default: 1,
4046
+ required: true
4047
+ },
4048
+ start_time: {
4049
+ type: Date,
4050
+ required: true
4051
+ },
4052
+ end_time: {
4053
+ type: Date,
4054
+ required: true
4055
+ },
4056
+ trial_days: {
4057
+ type: Number,
4058
+ default: 0
4059
+ },
4060
+ not_allow_same_product: {
4061
+ type: Boolean,
4062
+ default: false
4063
+ },
4064
+ created_by: {
4065
+ type: String,
4066
+ default: null
4067
+ },
4068
+ terminate: {
4069
+ reason: { type: String },
4070
+ user: { type: String },
4071
+ time: { type: Date }
4072
+ },
4073
+ metadata: {
4074
+ type: Schema.Types.Mixed,
4075
+ default: null
4076
+ },
4077
+ created_on: {
4078
+ type: Date,
4079
+ default: Date.now,
4080
+ required: true
4081
+ },
4082
+ updated_on: {
4083
+ type: Date,
4084
+ default: Date.now,
4085
+ required: true
4086
+ }
4087
+ }, { collection: "free_trial_campaign" });
4088
+ FreeTrialSchema.index({ campaign_code: 1 }, { unique: true });
4089
+ return {
4090
+ LicenseCampaignMeta: mongoose.model("LicenseCampaignMeta", LicenseCampaignMetaSchema),
4091
+ LicenseCampaignRecord: mongoose.model("LicenseCampaignRecord", LicenseCampaignRecordSchema),
4092
+ PublicAnnouncement: mongoose.model("PublicAnnouncement", PublicAnnouncementSchema),
4093
+ PublicAnnouncementUserList: mongoose.model("PublicAnnouncementUserList", PublicAnnouncementUserListSchema),
4094
+ PublicAnnouncementUserListLog: mongoose.model("PublicAnnouncementUserListLog", PublicAnnouncementUserListLog),
4095
+ PublicAnnouncementFreeTrial: mongoose.model("PublicAnnouncementFreeTrial", PublicAnnouncementFreeTrialSchema),
4096
+ IasBuyOption: mongoose.model("IasBuyOption", IasBuyOptionSchema),
4097
+ IasStorePageMeta: mongoose.model("IasStorePageMeta", IasStorePageMetaSchema),
4098
+ IasPromotion: mongoose.model("IasPromotion", IasPromotionSchema),
4099
+ SpecialOffer: mongoose.model("SpecialOffer", SpecialOfferSchema),
4100
+ Promotion: mongoose.model("Promotion", PromotionSchema),
4101
+ CampaignUserInfo: mongoose.model("CampaignUserInfo", CampaignUserInfoSchema),
4102
+ CouponPromotion: mongoose.model("CouponPromotion", CouponPromotionSchema),
4103
+ TierSalesMeta: mongoose.model("TierSalesMeta", TierSalesMetaSchema),
4104
+ TierSalesRules: mongoose.model("TierSalesRules", TierSalesRulesSchema),
4105
+ GearTrialMeta: mongoose.model("GearTrialMeta", GearTrialMetaSchema),
4106
+ GearTrialRecord: mongoose.model("GearTrialRecord", GearTrialRecordSchema),
4107
+ YotpoLoyalty: mongoose.model("YotpoLoyalty", YotpoLoyaltySchema),
4108
+ YotpoLoyaltyBackup: mongoose.model("YotpoLoyaltyBackup", YotpoLoyaltyBackupSchema),
4109
+ FreeTrial: mongoose.model("FreeTrial", FreeTrialSchema)
4110
+ };
4111
+ }
4112
+ //#endregion
4113
+ //#region src/models/redeem.ts
4114
+ function buildRedeem(mongoose) {
4115
+ const { Schema } = mongoose;
4116
+ const ObjectId = Schema.Types.ObjectId;
4117
+ const RedeemCodeSchema = new Schema({
4118
+ code: {
4119
+ type: String,
4120
+ required: true
4121
+ },
4122
+ product_id: {
4123
+ type: ObjectId,
4124
+ ref: "Product",
4125
+ default: null
4126
+ },
4127
+ bundle_id: {
4128
+ type: ObjectId,
4129
+ ref: "Bundle",
4130
+ default: null
4131
+ },
4132
+ user_id: {
4133
+ type: ObjectId,
4134
+ ref: "User",
4135
+ default: null
4136
+ },
4137
+ status: {
4138
+ type: Number,
4139
+ default: 1,
4140
+ required: true
4141
+ },
4142
+ expire_on: {
4143
+ type: Date,
4144
+ default: null
4145
+ },
4146
+ trial_due_date: {
4147
+ type: Date,
4148
+ default: null
4149
+ },
4150
+ tracker: {
4151
+ type: String,
4152
+ required: true
4153
+ },
4154
+ metadata: { type: Schema.Types.Mixed },
4155
+ created_on: {
4156
+ type: Date,
4157
+ default: Date.now
4158
+ },
4159
+ updated_on: {
4160
+ type: Date,
4161
+ default: Date.now
4162
+ }
4163
+ }, { collection: "pg_redeem_code" });
4164
+ RedeemCodeSchema.index({
4165
+ code: 1,
4166
+ status: 1
4167
+ }, { unique: true });
4168
+ RedeemCodeSchema.index({
4169
+ code: 1,
4170
+ product_id: 1,
4171
+ status: 1
4172
+ });
4173
+ RedeemCodeSchema.index({
4174
+ code: 1,
4175
+ bundle_id: 1,
4176
+ status: 1
4177
+ });
4178
+ RedeemCodeSchema.index({ tracker: 1 });
4179
+ return mongoose.model("RedeemCode", RedeemCodeSchema);
4180
+ }
4181
+ //#endregion
4182
+ //#region src/models/toneTheme.ts
4183
+ function buildToneTheme(mongoose) {
4184
+ const { Schema } = mongoose;
4185
+ const ToneThemeSchema = new Schema({
4186
+ title: {
4187
+ type: String,
4188
+ required: true,
4189
+ trim: true
4190
+ },
4191
+ description: {
4192
+ type: String,
4193
+ trim: true,
4194
+ default: null
4195
+ },
4196
+ cover_image_url: {
4197
+ type: String,
4198
+ default: null
4199
+ },
4200
+ thumb_url: {
4201
+ type: String,
4202
+ default: null
4203
+ },
4204
+ order: {
4205
+ type: Number,
4206
+ default: 0
4207
+ },
4208
+ is_shown: {
4209
+ type: Boolean,
4210
+ default: false
4211
+ },
4212
+ list_for: {
4213
+ type: String,
4214
+ required: true
4215
+ },
4216
+ category: {
4217
+ type: String,
4218
+ default: null
4219
+ },
4220
+ tags: [{ type: String }],
4221
+ created_on: {
4222
+ type: Date,
4223
+ default: Date.now
4224
+ },
4225
+ updated_on: {
4226
+ type: Date,
4227
+ default: Date.now
4228
+ }
4229
+ }, {
4230
+ collection: "tone_theme",
4231
+ toJSON: { virtuals: true },
4232
+ toObject: { virtuals: true }
4233
+ });
4234
+ ToneThemeSchema.index({
4235
+ list_for: 1,
4236
+ is_shown: 1,
4237
+ order: 1,
4238
+ created_on: -1
4239
+ });
4240
+ ToneThemeSchema.index({ category: 1 });
4241
+ ToneThemeSchema.index({ tags: 1 });
4242
+ ToneThemeSchema.plugin(mongooseLeanVirtuals);
4243
+ ToneThemeSchema.virtual("id").get(function() {
4244
+ return this._id.toHexString();
4245
+ });
4246
+ return mongoose.model("ToneTheme", ToneThemeSchema);
4247
+ }
4248
+ //#endregion
4249
+ //#region src/models/toneThemeFeaturedList.ts
4250
+ function buildToneThemeFeaturedList(mongoose) {
4251
+ const { Schema } = mongoose;
4252
+ const ObjectId = Schema.Types.ObjectId;
4253
+ const ToneThemeFeaturedListSchema = new Schema({
4254
+ tone_theme_id: {
4255
+ type: ObjectId,
4256
+ ref: "ToneTheme",
4257
+ required: true
4258
+ },
4259
+ featured_list_id: {
4260
+ type: ObjectId,
4261
+ ref: "FeaturedList",
4262
+ required: true
4263
+ },
4264
+ order: {
4265
+ type: Number,
4266
+ default: 0
4267
+ },
4268
+ created_on: {
4269
+ type: Date,
4270
+ default: Date.now
4271
+ }
4272
+ }, { collection: "tone_theme_featured_list" });
4273
+ ToneThemeFeaturedListSchema.index({
4274
+ tone_theme_id: 1,
4275
+ order: 1
4276
+ });
4277
+ ToneThemeFeaturedListSchema.index({ featured_list_id: 1 });
4278
+ ToneThemeFeaturedListSchema.index({
4279
+ tone_theme_id: 1,
4280
+ featured_list_id: 1
4281
+ }, { unique: true });
4282
+ return mongoose.model("ToneThemeFeaturedList", ToneThemeFeaturedListSchema);
4283
+ }
4284
+ //#endregion
4285
+ //#region src/models/user.ts
4286
+ const log$2 = debug("model:user");
4287
+ function encryptPassword(pwd) {
4288
+ if (!pwd) return "";
4289
+ const encryptType = "sha1";
4290
+ const salt = randomString({
4291
+ length: 5,
4292
+ numeric: true,
4293
+ letters: true
4294
+ }).toLowerCase();
4295
+ return `${encryptType}$${salt}$${crypto.createHash(encryptType).update(String(salt) + String(pwd)).digest("hex")}`;
4296
+ }
4297
+ function verifyPassword(pwd, pwdArr) {
4298
+ if (!pwd) return false;
4299
+ return crypto.createHash(pwdArr[0]).update(String(pwdArr[1]) + String(pwd)).digest("hex") === pwdArr[2];
4300
+ }
4301
+ const FLAG_EMAIL_EVENTS = [
4302
+ "bounce",
4303
+ "complaint",
4304
+ "reject"
4305
+ ];
4306
+ const jamupLitePattern = /JamUp.+Lite\/([0-9.]+)/;
4307
+ const biasAmpPattern = /Bias\/([0-9.]+)/;
4308
+ const biasAmpiPhonePattern = /Bias.+iPhone\/([0-9.]+)/;
4309
+ const biasFxPattern = /BIAS.+FX\/([0-9.]+).+iPad/;
4310
+ const biasPedalPattern = /BIAS.+Pedal\/([0-9.]+).+iPad/;
4311
+ function buildUser(mongoose) {
4312
+ const { Schema } = mongoose;
4313
+ const ObjectId = Schema.Types.ObjectId;
4314
+ const UserEmailStatusSchema = new Schema({
4315
+ user_id: {
4316
+ type: ObjectId,
4317
+ ref: "User",
4318
+ required: true
4319
+ },
4320
+ email_status: {
4321
+ type: String,
4322
+ enum: FLAG_EMAIL_EVENTS,
4323
+ required: true
4324
+ },
4325
+ creted_on: {
4326
+ type: Date,
4327
+ default: Date.now,
4328
+ required: true
4329
+ }
4330
+ }, { collection: "user_email_status" });
4331
+ const UserProfileSchema = new Schema({
4332
+ user_id: {
4333
+ type: ObjectId,
4334
+ ref: "User",
4335
+ required: true
4336
+ },
4337
+ full_name: {
4338
+ type: String,
4339
+ default: null
4340
+ },
4341
+ first_name: {
4342
+ type: String,
4343
+ default: null
4344
+ },
4345
+ last_name: {
4346
+ type: String,
4347
+ default: null
4348
+ },
4349
+ title: {
4350
+ type: String,
4351
+ default: null
4352
+ },
4353
+ gender: {
4354
+ type: String,
4355
+ default: null
4356
+ },
4357
+ country: {
4358
+ type: String,
4359
+ default: null
4360
+ },
4361
+ postal: {
4362
+ type: String,
4363
+ default: null
4364
+ },
4365
+ city: {
4366
+ type: String,
4367
+ default: null
4368
+ },
4369
+ state: {
4370
+ type: String,
4371
+ default: null
4372
+ },
4373
+ address: {
4374
+ type: String,
4375
+ default: null
4376
+ },
4377
+ phone: {
4378
+ type: String,
4379
+ default: null
4380
+ },
4381
+ description: { type: String },
4382
+ bc_token: {
4383
+ type: String,
4384
+ default: null
4385
+ },
4386
+ facebook_access_token: {
4387
+ type: String,
4388
+ default: null
4389
+ },
4390
+ reset_password_token: {
4391
+ type: String,
4392
+ default: null
4393
+ },
4394
+ profile_image_url: {
4395
+ type: String,
4396
+ default: null
4397
+ },
4398
+ profile_thumb_url: {
4399
+ type: String,
4400
+ default: null
4401
+ },
4402
+ facebook_image_url: {
4403
+ type: String,
4404
+ default: null
4405
+ },
4406
+ facebook_thumb_url: {
4407
+ type: String,
4408
+ default: null
4409
+ },
4410
+ reset_password_expire: {
4411
+ type: Date,
4412
+ default: null
4413
+ },
4414
+ is_email_verified: {
4415
+ type: Boolean,
4416
+ default: false
4417
+ },
4418
+ is_registered: {
4419
+ type: Boolean,
4420
+ default: true
4421
+ },
4422
+ stripe_customer_id: {
4423
+ type: String,
4424
+ default: null
4425
+ },
4426
+ selected_country: {
4427
+ type: String,
4428
+ default: null
4429
+ },
4430
+ date_joined: {
4431
+ type: Date,
4432
+ default: Date.now
4433
+ },
4434
+ search_mark: {
4435
+ type: Number,
4436
+ default: 0
4437
+ }
4438
+ }, {
4439
+ collection: "jamup_userprofile",
4440
+ toJSON: { virtuals: true },
4441
+ toObject: { virtuals: true }
4442
+ });
4443
+ UserProfileSchema.index({ user_id: 1 }, { unique: true });
4444
+ UserProfileSchema.index({ reset_password_token: 1 });
4445
+ UserProfileSchema.index({
4446
+ full_name: "text",
4447
+ first_name: "text",
4448
+ last_name: "text"
4449
+ });
4450
+ const UserSchema = new Schema({
4451
+ username: {
4452
+ type: String,
4453
+ trim: true,
4454
+ required: true
4455
+ },
4456
+ email: {
4457
+ type: String,
4458
+ trim: true,
4459
+ match: [/^\S+@\S+\.\S+$/, "Invalid Email Address"],
4460
+ required: true
4461
+ },
4462
+ password: {
4463
+ type: String,
4464
+ default: null,
4465
+ set: encryptPassword,
4466
+ required: true
4467
+ },
4468
+ user_status: {
4469
+ type: Number,
4470
+ default: 1
4471
+ },
4472
+ user_role: {
4473
+ type: Number,
4474
+ default: 1
4475
+ },
4476
+ is_active: {
4477
+ type: Boolean,
4478
+ default: false
4479
+ },
4480
+ activate_token: {
4481
+ type: String,
4482
+ default: null
4483
+ },
4484
+ activate_expire: {
4485
+ type: Date,
4486
+ default: null
4487
+ },
4488
+ fb_access_token: {
4489
+ type: String,
4490
+ default: null
4491
+ },
4492
+ facebook_id: {
4493
+ type: String,
4494
+ default: null
4495
+ },
4496
+ shopify_id: {
4497
+ type: Schema.Types.Mixed,
4498
+ default: null
4499
+ },
4500
+ bk_username: [{ type: String }],
4501
+ auth: {
4502
+ firebase_id: { type: String },
4503
+ google_id: { type: String },
4504
+ apple_id: { type: String },
4505
+ wechat_id: { type: String }
4506
+ },
4507
+ metadata: {
4508
+ gdpr: {
4509
+ type: Boolean,
4510
+ default: false
4511
+ },
4512
+ country: {
4513
+ type: String,
4514
+ default: null
4515
+ }
4516
+ },
4517
+ date_joined: {
4518
+ type: Date,
4519
+ default: Date.now
4520
+ },
4521
+ last_login: {
4522
+ type: Date,
4523
+ default: Date.now
4524
+ }
4525
+ }, {
4526
+ collection: "auth_user",
4527
+ toJSON: { virtuals: true },
4528
+ toObject: { virtuals: true }
4529
+ });
4530
+ UserSchema.index({ shopify_id: 1 });
4531
+ UserSchema.index({ username: 1 });
4532
+ UserSchema.index({ email: 1 }, { unique: true });
4533
+ UserSchema.index({ "auth.firebase_id": 1 });
4534
+ UserSchema.index({ "auth.apple_id": 1 });
4535
+ UserSchema.index({ "auth.facebook_id": 1 });
4536
+ const UserProductSchema = new Schema({
4537
+ user_id: {
4538
+ type: ObjectId,
4539
+ ref: "User",
4540
+ required: true
4541
+ },
4542
+ product: {
4543
+ type: String,
4544
+ required: true
4545
+ },
4546
+ platform: {
4547
+ type: String,
4548
+ required: true
4549
+ },
4550
+ metadata: {
4551
+ type: Schema.Types.Mixed,
4552
+ default: null
4553
+ },
4554
+ last_login: {
4555
+ type: Date,
4556
+ default: Date.now
4557
+ }
4558
+ }, { collection: "user_product_meta" });
4559
+ const UserProductTrackerSchema = new Schema({
4560
+ user_id: {
4561
+ type: ObjectId,
4562
+ ref: "User",
4563
+ required: true
4564
+ },
4565
+ product: {
4566
+ type: String,
4567
+ required: true
4568
+ },
4569
+ status: {
4570
+ type: Number,
4571
+ default: 1
4572
+ },
4573
+ created_on: {
4574
+ type: Date,
4575
+ default: Date.now
4576
+ }
4577
+ }, { collection: "user_product_tracker" });
4578
+ UserProductTrackerSchema.index({
4579
+ user_id: 1,
4580
+ product: 1
4581
+ }, { unique: true });
4582
+ const ApikeySchema = new Schema({
4583
+ apikey: {
4584
+ type: String,
4585
+ required: true,
4586
+ unique: true
4587
+ },
4588
+ permission: {
4589
+ type: [String],
4590
+ required: true
4591
+ },
4592
+ description: {
4593
+ type: String,
4594
+ required: true
4595
+ },
4596
+ status: {
4597
+ type: Boolean,
4598
+ required: true
4599
+ },
4600
+ created_on: {
4601
+ type: Date,
4602
+ default: Date.now
4603
+ },
4604
+ call_number: { type: Number },
4605
+ partner_name: { type: String },
4606
+ updated_on: {
4607
+ type: Date,
4608
+ default: Date.now
4609
+ }
4610
+ }, { collection: "auth_apikey" });
4611
+ const UserReferralSchema = new Schema({
4612
+ user_id: {
4613
+ type: ObjectId,
4614
+ ref: "User",
4615
+ required: true
4616
+ },
4617
+ referred_user_email: { type: String },
4618
+ created_on: {
4619
+ type: Date,
4620
+ default: Date.now
4621
+ },
4622
+ updated_on: {
4623
+ type: Date,
4624
+ default: Date.now
4625
+ }
4626
+ }, { collection: "user_referral" });
4627
+ const UserTokenSchema = new Schema({
4628
+ tid: {
4629
+ type: String,
4630
+ trim: true,
4631
+ required: true
4632
+ },
4633
+ expired_at: {
4634
+ type: Date,
4635
+ required: true
4636
+ },
4637
+ created_on: {
4638
+ type: Date,
4639
+ default: Date.now
4640
+ }
4641
+ }, {
4642
+ collection: "auth_token",
4643
+ toJSON: { virtuals: true },
4644
+ toObject: { virtuals: true }
4645
+ });
4646
+ UserTokenSchema.index({ tid: 1 }, { unique: true });
4647
+ UserTokenSchema.index({ expired_at: 1 }, { expireAfterSeconds: 0 });
4648
+ const UserFollowSchema = new Schema({
4649
+ user_id: {
4650
+ type: Schema.Types.ObjectId,
4651
+ ref: "User",
4652
+ required: true,
4653
+ index: true
4654
+ },
4655
+ follow_user_id: {
4656
+ type: Schema.Types.ObjectId,
4657
+ ref: "User",
4658
+ required: true,
4659
+ index: true
4660
+ },
4661
+ follow_platform: { type: String }
4662
+ }, {
4663
+ timestamps: {
4664
+ createdAt: true,
4665
+ updatedAt: false
4666
+ },
4667
+ versionKey: false,
4668
+ collection: "jamup_userFollow",
4669
+ toJSON: { virtuals: true },
4670
+ toObject: { virtuals: true }
4671
+ });
4672
+ UserFollowSchema.index({
4673
+ user_id: 1,
4674
+ follow_platform: 1,
4675
+ follow_user_id: 1
4676
+ }, { unique: true });
4677
+ UserSchema.method({
4678
+ authenticate(plainText) {
4679
+ const passwordArr = this.password.split("$");
4680
+ if (passwordArr.length !== 3) return false;
4681
+ return verifyPassword(plainText, passwordArr);
4682
+ },
4683
+ makeSalt() {
4684
+ return `${Math.round((/* @__PURE__ */ new Date()).valueOf() * Math.random())}`;
4685
+ }
4686
+ });
4687
+ UserTokenSchema.static({
4688
+ async createTokenMeta() {
4689
+ try {
4690
+ const tid = crypto.randomUUID();
4691
+ const nowDate = /* @__PURE__ */ new Date();
4692
+ const expiredAt = new Date((/* @__PURE__ */ new Date()).setDate(nowDate.getDate() + 14));
4693
+ return await this.create({
4694
+ tid,
4695
+ expired_at: expiredAt,
4696
+ created_on: nowDate
4697
+ });
4698
+ } catch (err) {
4699
+ log$2("ERR:createTokenMeta:%j", err);
4700
+ throw err;
4701
+ }
4702
+ },
4703
+ async isValid(tid) {
4704
+ const userTokenMeta = await this.findOne({ tid }).exec();
4705
+ return Boolean(userTokenMeta);
4706
+ },
4707
+ async revokeToken(tid) {
4708
+ return this.findOneAndDelete({ tid }).exec();
4709
+ }
4710
+ });
4711
+ UserSchema.static({
4712
+ async _async_auth(username, password) {
4713
+ const normalized = String(username).toLowerCase().replace("@", "_");
4714
+ let user;
4715
+ try {
4716
+ user = await this.findOne({ username: normalized }).exec();
4717
+ } catch (err) {
4718
+ throw new ApiErrorCode("internal_sysmtem_error", err.message);
4719
+ }
4720
+ if (!user) {
4721
+ let bgUser;
4722
+ try {
4723
+ bgUser = await this.findOne({ bk_username: { $in: [normalized] } }).exec();
4724
+ } catch (err) {
4725
+ throw new ApiErrorCode("INTERNAL_SYSMTEM_ERROR", err.message);
4726
+ }
4727
+ if (!bgUser) throw new ApiErrorCode("USER_NOT_EXIST", `Cannot find user ${normalized}`);
4728
+ else if (!bgUser.authenticate(password)) throw new ApiErrorCode("USER_UNAUTHORIZED", "unauthorized");
4729
+ return bgUser;
4730
+ }
4731
+ if (user.user_status === 2) throw new ApiErrorCode("ACCOUNT_WERE_DELETED", `Account ${user.email} already deleted`);
4732
+ if (!user.authenticate(password)) {
4733
+ if (user.facebook_id !== null) throw new ApiErrorCode("USER_OAUTH_LOGIN", "Please use FB to login your account");
4734
+ throw new ApiErrorCode("USER_UNAUTHORIZED", "unauthorized");
4735
+ }
4736
+ return user;
4737
+ },
4738
+ auth(uname, password, cb) {
4739
+ if (!cb) return this._async_auth(uname, password);
4740
+ this._async_auth(uname, password).then((user) => cb(null, user), (err) => cb(err));
4741
+ },
4742
+ login(query, cb) {
4743
+ if (!_.isObject(query)) return cb(/* @__PURE__ */ new Error("Invalid login parameter"));
4744
+ this.findOne(query).then((u) => cb(null, u), (err) => cb(err));
4745
+ },
4746
+ loginForPassport(query, cb) {
4747
+ if (!_.isObject(query)) return cb(/* @__PURE__ */ new Error("Invalid login parameter"));
4748
+ const q = query;
4749
+ if (_.has(q, "username")) q.username = String(_.trim(q.username)).toLowerCase();
4750
+ if (_.has(q, "email")) q.email = String(_.trim(q.email)).toLowerCase();
4751
+ this.findOne(q).then((user) => {
4752
+ if (!user) return cb({
4753
+ status: 404,
4754
+ errorMessage: "Cannot found target user"
4755
+ });
4756
+ if (user.user_status === 2) return cb({
4757
+ status: 410,
4758
+ errorMessage: "This account already deleted"
4759
+ });
4760
+ return cb(null, user);
4761
+ }, (err) => cb({
4762
+ status: 500,
4763
+ errorMessage: err
4764
+ }));
4765
+ },
4766
+ list(options, cb) {
4767
+ const criteria = options.criteria ?? {};
4768
+ const sort = options.sort ?? { createdAt: -1 };
4769
+ const limit = options.limit === 0 ? 0 : options.limit ?? 10;
4770
+ const page = options.page ?? 0;
4771
+ this.find(criteria).select("name username email").sort(sort).skip(limit * page).limit(limit).then((docs) => cb(null, docs), (err) => cb(err));
4772
+ },
4773
+ getUser(username, cb) {
4774
+ this.findOne({ username: String(username).toLowerCase() }).sort({ date_joined: -1 }).select("email user_role user_status auth").then((u) => cb(null, u), (err) => cb(err));
4775
+ },
4776
+ async getUserPromise(username) {
4777
+ return await this.findOne({ username: String(username).toLowerCase() }).select("email user_role user_status auth").exec();
4778
+ },
4779
+ getUserById(id, cb) {
4780
+ this.findById(id).then((u) => cb(null, u), (err) => cb(err));
4781
+ },
4782
+ async getUserFullData(username) {
4783
+ const cursor = this.aggregate([
4784
+ { $match: {
4785
+ username: String(username).toLowerCase(),
4786
+ user_status: 1
4787
+ } },
4788
+ { $lookup: {
4789
+ from: "jamup_userprofile",
4790
+ localField: "_id",
4791
+ foreignField: "user_id",
4792
+ as: "userprofile"
4793
+ } },
4794
+ { $unwind: "$userprofile" }
4795
+ ]).cursor({ batchSize: 100 });
4796
+ const results = [];
4797
+ for await (const doc of cursor) results.push(doc);
4798
+ return results;
4799
+ },
4800
+ async getUserDetail(query, projectFields = {}) {
4801
+ try {
4802
+ if (!_.isObject(query)) throw new Error("Query need to be object!");
4803
+ const queryCondition = _.assign({}, { user_status: 1 }, query);
4804
+ const defaultProjectFields = _.assign({}, {
4805
+ _id: 1,
4806
+ email: 1,
4807
+ username: 1,
4808
+ date_joined: 1,
4809
+ last_login: 1,
4810
+ "userprofile.first_name": 1,
4811
+ "userprofile.last_name": 1,
4812
+ "userprofile.full_name": 1
4813
+ }, projectFields);
4814
+ return await this.aggregate([
4815
+ { $match: queryCondition },
4816
+ { $lookup: {
4817
+ from: "jamup_userprofile",
4818
+ localField: "_id",
4819
+ foreignField: "user_id",
4820
+ as: "userprofile"
4821
+ } },
4822
+ { $unwind: "$userprofile" },
4823
+ { $project: defaultProjectFields }
4824
+ ]).exec();
4825
+ } catch (err) {
4826
+ log$2("ERR:getUserDetail %o", err);
4827
+ throw err;
4828
+ }
4829
+ },
4830
+ async getDealerDetail(query) {
4831
+ try {
4832
+ if (!_.isObject(query)) throw new Error("Query need to be object!");
4833
+ const queryCondition = _.assign({}, { user_status: 1 }, query);
4834
+ return await this.aggregate([
4835
+ { $match: queryCondition },
4836
+ { $lookup: {
4837
+ from: "jamup_userprofile",
4838
+ localField: "_id",
4839
+ foreignField: "user_id",
4840
+ as: "userprofile"
4841
+ } },
4842
+ { $unwind: "$userprofile" },
4843
+ { $lookup: {
4844
+ from: "b2b_dealer_profile",
4845
+ localField: "_id",
4846
+ foreignField: "dealer_id",
4847
+ as: "dealer"
4848
+ } },
4849
+ { $unwind: "$dealer" }
4850
+ ]).exec();
4851
+ } catch (err) {
4852
+ log$2("ERR:getDealerDetail %o", err);
4853
+ throw err;
4854
+ }
4855
+ },
4856
+ createUser(userObj, cb) {
4857
+ this.create(userObj).then((u) => cb(null, u), (err) => cb(err));
4858
+ },
4859
+ deleteUser(userId, cb) {
4860
+ this.deleteOne({ _id: userId }).then((r) => cb(null, r), (err) => cb(err));
4861
+ },
4862
+ findUserByEmail(email, cb) {
4863
+ this.find({ email: String(email).toLowerCase() }).then((docs) => cb(null, docs), (err) => cb(err));
4864
+ },
4865
+ async findByEmail(email) {
4866
+ try {
4867
+ if (!isEmail(email)) throw new Error(`Invalid email format: ${email}`);
4868
+ return await this.findOne({
4869
+ email: String(email).toLowerCase(),
4870
+ user_status: 1
4871
+ }).exec();
4872
+ } catch (err) {
4873
+ log$2("ERR:findByEmail:%o", email);
4874
+ throw err;
4875
+ }
4876
+ }
4877
+ });
4878
+ UserProfileSchema.static({
4879
+ createProfile(profileObj, cb) {
4880
+ if (!profileObj.user_id) return cb(/* @__PURE__ */ new Error("Missing User Id"));
4881
+ if (!mongoose.Types.ObjectId.isValid(profileObj.user_id)) return cb(/* @__PURE__ */ new Error("Invalid User Id"));
4882
+ this.create(profileObj).then((doc) => cb(null, doc), (err) => cb(err));
4883
+ },
4884
+ updateProfileByUserId(userId, updateData, cb) {
4885
+ this.findOneAndUpdate({ user_id: userId }, { $set: updateData }, { new: true }).then((doc) => cb(null, doc), (err) => cb(err));
4886
+ },
4887
+ async updateProfile(userId, updateProfileData) {
4888
+ try {
4889
+ return await this.findOneAndUpdate({ user_id: new mongoose.Types.ObjectId(userId) }, { $set: updateProfileData }, { new: true }).exec();
4890
+ } catch (err) {
4891
+ log$2("ERR:updateProfile:%o", err);
4892
+ throw err;
4893
+ }
4894
+ },
4895
+ getUserProfile(userId, cb) {
4896
+ this.getUserProfilePromise(userId).then((data) => cb(null, data), (err) => cb(err));
4897
+ },
4898
+ async getUserProfilePromise(userId) {
4899
+ const selectField = [
4900
+ "full_name",
4901
+ "profile_image_url",
4902
+ "profile_thumb_url",
4903
+ "first_name",
4904
+ "last_name",
4905
+ "title",
4906
+ "gender",
4907
+ "date_joined"
4908
+ ].join(" ");
4909
+ return await this.findOne({ user_id: userId }).select(selectField).exec();
4910
+ },
4911
+ getUserProfileDetail(userId, cb) {
4912
+ if (!cb) return this.findOne({ user_id: userId }).exec();
4913
+ this.findOne({ user_id: userId }).then((d) => cb(null, d), (err) => cb(err));
4914
+ },
4915
+ getUserProfileByCondition(condition, cb) {
4916
+ this.findOne(condition).then((d) => cb(null, d), (err) => cb(err));
4917
+ }
4918
+ });
4919
+ UserProductSchema.static({ detectUserProduct(userAgent) {
4920
+ const preUserAgent = String(userAgent);
4921
+ if (jamupLitePattern.test(preUserAgent)) return {
4922
+ product: "jamup-lite",
4923
+ platform: "iOS"
4924
+ };
4925
+ if (biasAmpPattern.test(preUserAgent)) return {
4926
+ product: "biasamp",
4927
+ platform: "iOS"
4928
+ };
4929
+ if (biasAmpiPhonePattern.test(preUserAgent)) return {
4930
+ product: "biasamp",
4931
+ platform: "iOS"
4932
+ };
4933
+ if (biasFxPattern.test(preUserAgent)) return {
4934
+ product: "biasfx",
4935
+ platform: "iOS"
4936
+ };
4937
+ if (biasPedalPattern.test(preUserAgent)) return {
4938
+ product: "pedal",
4939
+ platform: "iOS"
4940
+ };
4941
+ return false;
4942
+ } });
4943
+ UserSchema.virtual("id").get(function() {
4944
+ return this._id.toHexString();
4945
+ });
4946
+ return {
4947
+ User: mongoose.model("User", UserSchema),
4948
+ UserProfile: mongoose.model("UserProfile", UserProfileSchema),
4949
+ ApiKey: mongoose.model("ApiKey", ApikeySchema),
4950
+ UserProduct: mongoose.model("UserProduct", UserProductSchema),
4951
+ UserProductTracker: mongoose.model("UserProductTracker", UserProductTrackerSchema),
4952
+ UserReferral: mongoose.model("UserReferral", UserReferralSchema),
4953
+ UserEmailStatus: mongoose.model("UserEmailStatus", UserEmailStatusSchema),
4954
+ UserToken: mongoose.model("UserToken", UserTokenSchema),
4955
+ UserFollow: mongoose.model("UserFollow", UserFollowSchema)
4956
+ };
4957
+ }
4958
+ //#endregion
4959
+ //#region src/models/userTrack.ts
4960
+ const log$1 = debug("model:usertrack");
4961
+ const LICENSE_ADDITION = {
4962
+ PRO: 0,
4963
+ DEMO: 1,
4964
+ STD: 2,
4965
+ LITE: 3,
4966
+ ELITE: 4
4967
+ };
4968
+ const PRODUCTS = [
4969
+ "fx2",
4970
+ "amp2",
4971
+ "fx1",
4972
+ "amp1"
4973
+ ];
4974
+ function buildUserTrack(mongoose) {
4975
+ const { Schema } = mongoose;
4976
+ const ObjectId = Schema.Types.ObjectId;
4977
+ const GenericDataSchema = new Schema({
4978
+ data_type: {
4979
+ type: String,
4980
+ required: true
4981
+ },
4982
+ data_key: {
4983
+ type: String,
4984
+ required: true
4985
+ },
4986
+ data_value: {
4987
+ type: Schema.Types.Mixed,
4988
+ required: true
4989
+ },
4990
+ data_meta: {
4991
+ type: Schema.Types.Mixed,
4992
+ default: null
4993
+ },
4994
+ created_on: {
4995
+ type: Date,
4996
+ default: Date.now
4997
+ }
4998
+ }, { collection: "pg_generic_data" });
4999
+ const IapEventSchema = new Schema({
5000
+ user_id: {
5001
+ type: ObjectId,
5002
+ ref: "User",
5003
+ required: true
5004
+ },
5005
+ app_name: {
5006
+ type: String,
5007
+ required: true
5008
+ },
5009
+ onesignal_id: {
5010
+ type: String,
5011
+ default: null
5012
+ },
5013
+ redeem_code: {
5014
+ type: ObjectId,
5015
+ default: null
5016
+ },
5017
+ created_on: {
5018
+ type: Date,
5019
+ default: Date.now
5020
+ },
5021
+ region: {
5022
+ type: String,
5023
+ required: false
5024
+ }
5025
+ }, { collection: "pg_iap_event" });
5026
+ const DemoUserTrackerSchema = new Schema({
5027
+ user_id: {
5028
+ type: ObjectId,
5029
+ ref: "User",
5030
+ required: true
5031
+ },
5032
+ product: {
5033
+ type: String,
5034
+ required: true
5035
+ },
5036
+ addition: {
5037
+ type: Number,
5038
+ required: true
5039
+ },
5040
+ started_at: {
5041
+ type: Date,
5042
+ required: true
5043
+ },
5044
+ updated_at: {
5045
+ type: Date,
5046
+ default: null
5047
+ },
5048
+ created_on: {
5049
+ type: Date,
5050
+ default: Date.now
5051
+ }
5052
+ }, { collection: "demo_user_tracker" });
5053
+ DemoUserTrackerSchema.index({
5054
+ user_id: 1,
5055
+ product: 1,
5056
+ addition: 1,
5057
+ started_at: 1,
5058
+ updated_at: 1
5059
+ }, { unique: true });
5060
+ DemoUserTrackerSchema.static({ async saveTrackData(data) {
5061
+ try {
5062
+ const { product, addition } = data;
5063
+ const userID = new mongoose.Types.ObjectId(data.userID);
5064
+ const startedAt = parseInt(String(data.startedAt), 10);
5065
+ if (!mongoose.Types.ObjectId.isValid(userID) || PRODUCTS.indexOf(product) === -1 || Object.keys(LICENSE_ADDITION).indexOf(addition) === -1) throw new Error(`Invalid parameter: ${String(userID)}/${product}/${addition}`);
5066
+ else if (!startedAt || !moment(startedAt).isValid()) throw new Error(`Invalid timestamp format: ${startedAt}/${data.updatedAt}`);
5067
+ const trackData = {
5068
+ user_id: userID,
5069
+ product,
5070
+ addition: LICENSE_ADDITION[addition],
5071
+ started_at: moment(startedAt).toDate()
5072
+ };
5073
+ if (_.has(data, "updatedAt")) {
5074
+ const updatedAt = parseInt(String(data.updatedAt), 10);
5075
+ if (updatedAt && moment(updatedAt).isValid()) trackData.updated_at = moment(updatedAt).toDate();
5076
+ }
5077
+ if (!await this.findOne(trackData).exec()) await this.create(trackData);
5078
+ return true;
5079
+ } catch (err) {
5080
+ log$1("ERR:handleLicensePurchaseData:%j", err);
5081
+ throw err;
5082
+ }
5083
+ } });
5084
+ return {
5085
+ GenericData: mongoose.model("GenericData", GenericDataSchema),
5086
+ IapEvent: mongoose.model("IapEvent", IapEventSchema),
5087
+ DemoUserTracker: mongoose.model("DemoUserTracker", DemoUserTrackerSchema)
5088
+ };
5089
+ }
5090
+ //#endregion
5091
+ //#region src/index.ts
5092
+ const log = debug("model:init");
5093
+ const ALL_BUILDERS = [
5094
+ buildBanks,
5095
+ buildDevice,
5096
+ buildFeaturedList,
5097
+ buildHardware,
5098
+ buildHomeConfig,
5099
+ buildOauth,
5100
+ buildPartner,
5101
+ buildPayment,
5102
+ buildPgConfig,
5103
+ buildPreset,
5104
+ buildPromotion,
5105
+ buildRedeem,
5106
+ buildToneTheme,
5107
+ buildToneThemeFeaturedList,
5108
+ buildUser,
5109
+ buildUserTrack
5110
+ ];
5111
+ function register(mongoose, type) {
5112
+ log("Loading type: %s", type);
5113
+ if (type === "account") {
5114
+ buildUser(mongoose);
5115
+ buildOauth(mongoose);
5116
+ buildPartner(mongoose);
5117
+ buildPayment(mongoose);
5118
+ buildHardware(mongoose);
5119
+ return;
5120
+ }
5121
+ if (type === "hardware") {
5122
+ buildHardware(mongoose);
5123
+ return;
5124
+ }
5125
+ for (const build of ALL_BUILDERS) build(mongoose);
5126
+ }
5127
+ //#endregion
5128
+ export { register as default, register };