@exyconn/common 2.1.0 → 2.3.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.
Files changed (142) hide show
  1. package/README.md +969 -261
  2. package/dist/client/hooks/index.d.mts +1042 -0
  3. package/dist/client/hooks/index.d.ts +1042 -0
  4. package/dist/client/hooks/index.js +2276 -0
  5. package/dist/client/hooks/index.js.map +1 -0
  6. package/dist/client/hooks/index.mjs +2217 -0
  7. package/dist/client/hooks/index.mjs.map +1 -0
  8. package/dist/client/http/index.d.mts +217 -49
  9. package/dist/client/http/index.d.ts +217 -49
  10. package/dist/client/http/index.js +473 -94
  11. package/dist/client/http/index.js.map +1 -1
  12. package/dist/client/http/index.mjs +441 -84
  13. package/dist/client/http/index.mjs.map +1 -1
  14. package/dist/client/index.d.mts +6 -4
  15. package/dist/client/index.d.ts +6 -4
  16. package/dist/client/index.js +481 -319
  17. package/dist/client/index.js.map +1 -1
  18. package/dist/client/index.mjs +449 -290
  19. package/dist/client/index.mjs.map +1 -1
  20. package/dist/client/utils/index.d.mts +3 -279
  21. package/dist/client/utils/index.d.ts +3 -279
  22. package/dist/client/web/index.d.mts +1461 -0
  23. package/dist/client/web/index.d.ts +1461 -0
  24. package/dist/client/web/index.js +2681 -0
  25. package/dist/client/web/index.js.map +1 -0
  26. package/dist/client/web/index.mjs +2618 -0
  27. package/dist/client/web/index.mjs.map +1 -0
  28. package/dist/data/brand-identity.d.mts +149 -0
  29. package/dist/data/brand-identity.d.ts +149 -0
  30. package/dist/data/brand-identity.js +235 -0
  31. package/dist/data/brand-identity.js.map +1 -0
  32. package/dist/data/brand-identity.mjs +220 -0
  33. package/dist/data/brand-identity.mjs.map +1 -0
  34. package/dist/data/countries.d.mts +61 -0
  35. package/dist/data/countries.d.ts +61 -0
  36. package/dist/data/countries.js +987 -0
  37. package/dist/data/countries.js.map +1 -0
  38. package/dist/data/countries.mjs +971 -0
  39. package/dist/data/countries.mjs.map +1 -0
  40. package/dist/data/currencies.d.mts +19 -0
  41. package/dist/data/currencies.d.ts +19 -0
  42. package/dist/data/currencies.js +162 -0
  43. package/dist/data/currencies.js.map +1 -0
  44. package/dist/data/currencies.mjs +153 -0
  45. package/dist/data/currencies.mjs.map +1 -0
  46. package/dist/data/index.d.mts +7 -0
  47. package/dist/data/index.d.ts +7 -0
  48. package/dist/data/index.js +2087 -0
  49. package/dist/data/index.js.map +1 -0
  50. package/dist/data/index.mjs +1948 -0
  51. package/dist/data/index.mjs.map +1 -0
  52. package/dist/data/phone-codes.d.mts +15 -0
  53. package/dist/data/phone-codes.d.ts +15 -0
  54. package/dist/data/phone-codes.js +219 -0
  55. package/dist/data/phone-codes.js.map +1 -0
  56. package/dist/data/phone-codes.mjs +211 -0
  57. package/dist/data/phone-codes.mjs.map +1 -0
  58. package/dist/data/regex.d.mts +287 -0
  59. package/dist/data/regex.d.ts +287 -0
  60. package/dist/data/regex.js +306 -0
  61. package/dist/data/regex.js.map +1 -0
  62. package/dist/data/regex.mjs +208 -0
  63. package/dist/data/regex.mjs.map +1 -0
  64. package/dist/data/timezones.d.mts +16 -0
  65. package/dist/data/timezones.d.ts +16 -0
  66. package/dist/data/timezones.js +98 -0
  67. package/dist/data/timezones.js.map +1 -0
  68. package/dist/data/timezones.mjs +89 -0
  69. package/dist/data/timezones.mjs.map +1 -0
  70. package/dist/index-BZf42T3R.d.mts +305 -0
  71. package/dist/index-CF0D8PGE.d.ts +305 -0
  72. package/dist/index-Ckhm_HaX.d.mts +138 -0
  73. package/dist/index-DKn4raO7.d.ts +222 -0
  74. package/dist/index-NS8dS0p9.d.mts +222 -0
  75. package/dist/index-Nqm5_lwT.d.ts +188 -0
  76. package/dist/index-br6POSyA.d.ts +138 -0
  77. package/dist/index-jBi3V6e5.d.mts +188 -0
  78. package/dist/index.d.mts +21 -580
  79. package/dist/index.d.ts +21 -580
  80. package/dist/index.js +1839 -347
  81. package/dist/index.js.map +1 -1
  82. package/dist/index.mjs +1850 -359
  83. package/dist/index.mjs.map +1 -1
  84. package/dist/packageCheck-B_qfsD6R.d.ts +280 -0
  85. package/dist/packageCheck-C2_FT_Rl.d.mts +280 -0
  86. package/dist/server/configs/index.d.mts +602 -0
  87. package/dist/server/configs/index.d.ts +602 -0
  88. package/dist/server/configs/index.js +707 -0
  89. package/dist/server/configs/index.js.map +1 -0
  90. package/dist/server/configs/index.mjs +665 -0
  91. package/dist/server/configs/index.mjs.map +1 -0
  92. package/dist/server/index.d.mts +4 -1
  93. package/dist/server/index.d.ts +4 -1
  94. package/dist/server/index.js +1330 -0
  95. package/dist/server/index.js.map +1 -1
  96. package/dist/server/index.mjs +1286 -2
  97. package/dist/server/index.mjs.map +1 -1
  98. package/dist/server/middleware/index.d.mts +283 -2
  99. package/dist/server/middleware/index.d.ts +283 -2
  100. package/dist/server/middleware/index.js +761 -0
  101. package/dist/server/middleware/index.js.map +1 -1
  102. package/dist/server/middleware/index.mjs +751 -1
  103. package/dist/server/middleware/index.mjs.map +1 -1
  104. package/dist/shared/config/index.d.mts +40 -0
  105. package/dist/shared/config/index.d.ts +40 -0
  106. package/dist/shared/config/index.js +58 -0
  107. package/dist/shared/config/index.js.map +1 -0
  108. package/dist/shared/config/index.mjs +51 -0
  109. package/dist/shared/config/index.mjs.map +1 -0
  110. package/dist/shared/constants/index.d.mts +593 -0
  111. package/dist/shared/constants/index.d.ts +593 -0
  112. package/dist/shared/constants/index.js +391 -0
  113. package/dist/shared/constants/index.js.map +1 -0
  114. package/dist/shared/constants/index.mjs +360 -0
  115. package/dist/shared/constants/index.mjs.map +1 -0
  116. package/dist/shared/index.d.mts +5 -1
  117. package/dist/shared/index.d.ts +5 -1
  118. package/dist/shared/types/index.d.mts +140 -0
  119. package/dist/shared/types/index.d.ts +140 -0
  120. package/dist/shared/types/index.js +4 -0
  121. package/dist/shared/types/index.js.map +1 -0
  122. package/dist/shared/types/index.mjs +3 -0
  123. package/dist/shared/types/index.mjs.map +1 -0
  124. package/dist/shared/utils/index.d.mts +255 -0
  125. package/dist/shared/utils/index.d.ts +255 -0
  126. package/dist/shared/utils/index.js +623 -0
  127. package/dist/shared/utils/index.js.map +1 -0
  128. package/dist/shared/utils/index.mjs +324 -0
  129. package/dist/shared/utils/index.mjs.map +1 -0
  130. package/dist/shared/validation/index.d.mts +258 -0
  131. package/dist/shared/validation/index.d.ts +258 -0
  132. package/dist/shared/validation/index.js +185 -0
  133. package/dist/shared/validation/index.js.map +1 -0
  134. package/dist/shared/validation/index.mjs +172 -0
  135. package/dist/shared/validation/index.mjs.map +1 -0
  136. package/package.json +127 -62
  137. package/dist/index-BcxL4_V4.d.ts +0 -2946
  138. package/dist/index-DEzgM15j.d.ts +0 -67
  139. package/dist/index-DNFVgQx8.d.ts +0 -1375
  140. package/dist/index-DbV04Dx8.d.mts +0 -67
  141. package/dist/index-DfqEP6Oe.d.mts +0 -1375
  142. package/dist/index-bvvCev9Q.d.mts +0 -2946
@@ -6,6 +6,7 @@ var path = require('path');
6
6
  var mongoose = require('mongoose');
7
7
  var jwt = require('jsonwebtoken');
8
8
  var fs = require('fs');
9
+ var rateLimit = require('express-rate-limit');
9
10
 
10
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
12
 
@@ -14,6 +15,7 @@ var DailyRotateFile__default = /*#__PURE__*/_interopDefault(DailyRotateFile);
14
15
  var path__default = /*#__PURE__*/_interopDefault(path);
15
16
  var mongoose__default = /*#__PURE__*/_interopDefault(mongoose);
16
17
  var jwt__default = /*#__PURE__*/_interopDefault(jwt);
18
+ var rateLimit__default = /*#__PURE__*/_interopDefault(rateLimit);
17
19
 
18
20
  // src/server/enums/status.ts
19
21
  var StatusCode = /* @__PURE__ */ ((StatusCode2) => {
@@ -426,6 +428,629 @@ var requireOrganization = (req, res, next) => {
426
428
  next();
427
429
  };
428
430
 
431
+ // src/server/middleware/queryParser.middleware.ts
432
+ var queryParser = (req, _, next) => {
433
+ const { page, limit, sort, sortBy, sortOrder, search, filter, ...otherParams } = req.query;
434
+ const parsed = {
435
+ page: Math.max(Number(page) || 1, 1),
436
+ limit: Math.min(Number(limit) || 10, 100),
437
+ filter: {}
438
+ };
439
+ if (typeof sort === "string") {
440
+ const [field, order] = sort.split(":");
441
+ parsed.sort = {
442
+ field,
443
+ order: order === "asc" ? "asc" : "desc"
444
+ };
445
+ } else if (typeof sortBy === "string") {
446
+ parsed.sort = {
447
+ field: sortBy,
448
+ order: sortOrder === "asc" ? "asc" : "desc"
449
+ };
450
+ }
451
+ if (typeof search === "string") {
452
+ parsed.search = search;
453
+ }
454
+ if (typeof filter === "object" && filter !== null) {
455
+ Object.entries(filter).forEach(([key, value]) => {
456
+ if (value !== "all") {
457
+ parsed.filter[key] = value;
458
+ }
459
+ });
460
+ }
461
+ Object.entries(otherParams).forEach(([key, value]) => {
462
+ if (typeof value === "string" && value !== "all" && !["page", "limit", "sort", "sortBy", "sortOrder", "search"].includes(key)) {
463
+ parsed.filter[key] = value;
464
+ }
465
+ });
466
+ req.parsedQuery = parsed;
467
+ next();
468
+ };
469
+
470
+ // src/server/middleware/utils/schemaMeta.util.ts
471
+ var getZodTypeName = (schema) => {
472
+ const typeName = schema._def?.typeName;
473
+ switch (typeName) {
474
+ case "ZodString":
475
+ return "string";
476
+ case "ZodNumber":
477
+ return "number";
478
+ case "ZodBoolean":
479
+ return "boolean";
480
+ case "ZodDate":
481
+ return "date";
482
+ case "ZodArray":
483
+ return "array";
484
+ case "ZodObject":
485
+ return "object";
486
+ case "ZodOptional":
487
+ case "ZodNullable":
488
+ return schema._def?.innerType ? getZodTypeName(schema._def.innerType) : "unknown";
489
+ case "ZodDefault":
490
+ return schema._def?.innerType ? getZodTypeName(schema._def.innerType) : "unknown";
491
+ case "ZodEnum":
492
+ return "enum";
493
+ case "ZodUnion":
494
+ return "union";
495
+ default:
496
+ return "unknown";
497
+ }
498
+ };
499
+ var isZodRequired = (schema) => {
500
+ const typeName = schema._def?.typeName;
501
+ return typeName !== "ZodOptional" && typeName !== "ZodNullable";
502
+ };
503
+ var extractSchemaMeta = (model, zodSchema) => {
504
+ const columns = [];
505
+ if (zodSchema && zodSchema.shape) {
506
+ const shape = zodSchema.shape;
507
+ for (const [key, value] of Object.entries(shape)) {
508
+ if (key.startsWith("_")) continue;
509
+ columns.push({
510
+ name: key,
511
+ datatype: getZodTypeName(value),
512
+ required: isZodRequired(value)
513
+ });
514
+ }
515
+ return columns;
516
+ }
517
+ try {
518
+ const schema = model.schema;
519
+ const paths = schema.paths;
520
+ for (const [key, pathInfo] of Object.entries(paths)) {
521
+ if (key.startsWith("_") || key === "__v") continue;
522
+ const schemaType = pathInfo;
523
+ columns.push({
524
+ name: key,
525
+ datatype: (schemaType.instance || "unknown").toLowerCase(),
526
+ required: schemaType.isRequired || false
527
+ });
528
+ }
529
+ } catch {
530
+ }
531
+ return columns;
532
+ };
533
+
534
+ // src/server/middleware/pagination.middleware.ts
535
+ var queryPagination = (model, options = {}, withOrgId = true) => {
536
+ return async (req, res, next) => {
537
+ try {
538
+ const { page, limit, sort, search, filter } = req.parsedQuery;
539
+ const query = {};
540
+ Object.entries(filter).forEach(([key, value]) => {
541
+ if (options.regexFilterFields?.includes(key)) {
542
+ query[key] = { $regex: value, $options: "i" };
543
+ } else {
544
+ query[key] = value;
545
+ }
546
+ });
547
+ const organizationId = req.headers["x-organization-id"];
548
+ if (organizationId && typeof organizationId === "string" && withOrgId) {
549
+ query.organizationId = organizationId;
550
+ }
551
+ if (search && options.searchFields?.length) {
552
+ query.$or = options.searchFields.map((field) => ({
553
+ [field]: { $regex: search, $options: "i" }
554
+ }));
555
+ }
556
+ const sortQuery = sort ? { [sort.field]: sort.order } : { createdAt: "desc" };
557
+ const skip = (page - 1) * limit;
558
+ const [data, total] = await Promise.all([
559
+ model.find(query).sort(sortQuery).skip(skip).limit(limit),
560
+ model.countDocuments(query)
561
+ ]);
562
+ res.paginatedResult = {
563
+ data,
564
+ meta: {
565
+ page,
566
+ limit,
567
+ total,
568
+ totalPages: Math.ceil(total / limit)
569
+ },
570
+ columns: extractSchemaMeta(model, options.validatorSchema)
571
+ };
572
+ next();
573
+ } catch (error) {
574
+ next(error);
575
+ }
576
+ };
577
+ };
578
+ var isZodError = (error) => {
579
+ return error !== null && typeof error === "object" && "errors" in error && Array.isArray(error.errors);
580
+ };
581
+ var getOrgId = (req, orgField = "organizationId") => {
582
+ const orgReq = req;
583
+ return orgReq.organizationId || req.headers["x-organization-id"] || req.query[orgField];
584
+ };
585
+ var buildOrgFilter = (req, config) => {
586
+ const filter = {};
587
+ if (config.withOrganization !== false) {
588
+ const orgId = getOrgId(req, config.orgField);
589
+ if (orgId) {
590
+ filter[config.orgField || "organizationId"] = orgId;
591
+ }
592
+ }
593
+ return filter;
594
+ };
595
+ var formatZodError = (error) => {
596
+ return error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
597
+ };
598
+ function createCrudControllers(config) {
599
+ const {
600
+ model,
601
+ resourceName,
602
+ createSchema,
603
+ updateSchema,
604
+ searchFields = [],
605
+ regexFilterFields = [],
606
+ withOrganization = true,
607
+ orgField = "organizationId",
608
+ transformCreate,
609
+ transformUpdate,
610
+ afterCreate,
611
+ afterUpdate,
612
+ afterDelete,
613
+ excludeFields = [],
614
+ populateFields = [],
615
+ buildQuery
616
+ } = config;
617
+ const getAll = async (req, res, _next) => {
618
+ try {
619
+ const paginatedRes = res;
620
+ if (paginatedRes.paginatedResult) {
621
+ successResponse(res, paginatedRes.paginatedResult, `${resourceName} list fetched successfully`);
622
+ return;
623
+ }
624
+ const page = parseInt(req.query.page) || 1;
625
+ const limit = parseInt(req.query.limit) || 10;
626
+ const sortField = req.query.sortBy || "createdAt";
627
+ const sortOrder = req.query.sortOrder || "desc";
628
+ const search = req.query.search;
629
+ let query = {};
630
+ if (withOrganization) {
631
+ const orgId = getOrgId(req, orgField);
632
+ if (orgId) {
633
+ query[orgField] = orgId;
634
+ }
635
+ }
636
+ if (search && searchFields.length > 0) {
637
+ query.$or = searchFields.map((field) => ({
638
+ [field]: { $regex: search, $options: "i" }
639
+ }));
640
+ }
641
+ const filterableParams = Object.keys(req.query).filter(
642
+ (key) => !["page", "limit", "sortBy", "sortOrder", "search"].includes(key)
643
+ );
644
+ filterableParams.forEach((key) => {
645
+ const value = req.query[key];
646
+ if (value !== void 0 && value !== "" && value !== "all") {
647
+ if (regexFilterFields.includes(key)) {
648
+ query[key] = { $regex: value, $options: "i" };
649
+ } else {
650
+ query[key] = value;
651
+ }
652
+ }
653
+ });
654
+ if (buildQuery) {
655
+ query = buildQuery(req, query);
656
+ }
657
+ const sortQuery = { [sortField]: sortOrder };
658
+ const skip = (page - 1) * limit;
659
+ let projection = {};
660
+ if (excludeFields.length > 0) {
661
+ projection = excludeFields.reduce(
662
+ (acc, field) => ({ ...acc, [field]: 0 }),
663
+ {}
664
+ );
665
+ }
666
+ let dbQuery = model.find(query, projection);
667
+ if (populateFields.length > 0) {
668
+ populateFields.forEach((field) => {
669
+ dbQuery = dbQuery.populate(field);
670
+ });
671
+ }
672
+ const [data, total] = await Promise.all([
673
+ dbQuery.sort(sortQuery).skip(skip).limit(limit),
674
+ model.countDocuments(query)
675
+ ]);
676
+ successResponse(
677
+ res,
678
+ {
679
+ data,
680
+ meta: {
681
+ page,
682
+ limit,
683
+ total,
684
+ totalPages: Math.ceil(total / limit)
685
+ },
686
+ columns: extractSchemaMeta(model, createSchema)
687
+ },
688
+ `${resourceName} list fetched successfully`
689
+ );
690
+ } catch (error) {
691
+ logger.error(`Error in getAll ${resourceName}`, {
692
+ error: error instanceof Error ? error.message : "Unknown error"
693
+ });
694
+ errorResponse(res, `Failed to fetch ${resourceName.toLowerCase()} list`);
695
+ }
696
+ };
697
+ const getById = async (req, res, _next) => {
698
+ try {
699
+ const { id } = req.params;
700
+ if (!id || !mongoose.Types.ObjectId.isValid(id)) {
701
+ badRequestResponse(res, "Invalid ID format");
702
+ return;
703
+ }
704
+ const query = {
705
+ _id: new mongoose.Types.ObjectId(id),
706
+ ...buildOrgFilter(req, { ...config, withOrganization, orgField })
707
+ };
708
+ let dbQuery = model.findOne(query);
709
+ if (populateFields.length > 0) {
710
+ populateFields.forEach((field) => {
711
+ dbQuery = dbQuery.populate(field);
712
+ });
713
+ }
714
+ const doc = await dbQuery;
715
+ if (!doc) {
716
+ notFoundResponse(res, `${resourceName} not found`);
717
+ return;
718
+ }
719
+ successResponse(res, doc, `${resourceName} fetched successfully`);
720
+ } catch (error) {
721
+ logger.error(`Error in getById ${resourceName}`, {
722
+ error: error instanceof Error ? error.message : "Unknown error",
723
+ id: req.params.id
724
+ });
725
+ errorResponse(res, `Failed to fetch ${resourceName.toLowerCase()}`);
726
+ }
727
+ };
728
+ const create = async (req, res, _next) => {
729
+ try {
730
+ let input = req.body;
731
+ if (createSchema) {
732
+ try {
733
+ input = createSchema.parse(input);
734
+ } catch (error) {
735
+ if (isZodError(error)) {
736
+ badRequestResponse(res, formatZodError(error));
737
+ return;
738
+ }
739
+ throw error;
740
+ }
741
+ }
742
+ if (transformCreate) {
743
+ input = transformCreate(input, req);
744
+ }
745
+ if (withOrganization) {
746
+ const orgId = getOrgId(req, orgField);
747
+ if (orgId) {
748
+ input[orgField] = orgId;
749
+ }
750
+ }
751
+ const doc = new model(input);
752
+ await doc.save();
753
+ if (afterCreate) {
754
+ await afterCreate(doc, req);
755
+ }
756
+ logger.info(`${resourceName} created successfully`, {
757
+ id: doc._id,
758
+ [orgField]: input[orgField]
759
+ });
760
+ createdResponse(res, doc, `${resourceName} created successfully`);
761
+ } catch (error) {
762
+ logger.error(`Error in create ${resourceName}`, {
763
+ error: error instanceof Error ? error.message : "Unknown error"
764
+ });
765
+ if (error.code === 11e3) {
766
+ badRequestResponse(res, `A ${resourceName.toLowerCase()} with this data already exists`);
767
+ return;
768
+ }
769
+ errorResponse(res, `Failed to create ${resourceName.toLowerCase()}`);
770
+ }
771
+ };
772
+ const update = async (req, res, _next) => {
773
+ try {
774
+ const { id } = req.params;
775
+ if (!id || !mongoose.Types.ObjectId.isValid(id)) {
776
+ badRequestResponse(res, "Invalid ID format");
777
+ return;
778
+ }
779
+ let input = req.body;
780
+ if (updateSchema) {
781
+ try {
782
+ input = updateSchema.parse(input);
783
+ } catch (error) {
784
+ if (isZodError(error)) {
785
+ badRequestResponse(res, formatZodError(error));
786
+ return;
787
+ }
788
+ throw error;
789
+ }
790
+ }
791
+ if (transformUpdate) {
792
+ input = transformUpdate(input, req);
793
+ }
794
+ const query = {
795
+ _id: new mongoose.Types.ObjectId(id),
796
+ ...buildOrgFilter(req, { ...config, withOrganization, orgField })
797
+ };
798
+ const doc = await model.findOneAndUpdate(query, { $set: input }, { new: true });
799
+ if (!doc) {
800
+ notFoundResponse(res, `${resourceName} not found`);
801
+ return;
802
+ }
803
+ if (afterUpdate) {
804
+ await afterUpdate(doc, req);
805
+ }
806
+ logger.info(`${resourceName} updated successfully`, { id });
807
+ successResponse(res, doc, `${resourceName} updated successfully`);
808
+ } catch (error) {
809
+ logger.error(`Error in update ${resourceName}`, {
810
+ error: error instanceof Error ? error.message : "Unknown error",
811
+ id: req.params.id
812
+ });
813
+ errorResponse(res, `Failed to update ${resourceName.toLowerCase()}`);
814
+ }
815
+ };
816
+ const deleteOne = async (req, res, _next) => {
817
+ try {
818
+ const { id } = req.params;
819
+ if (!id || !mongoose.Types.ObjectId.isValid(id)) {
820
+ badRequestResponse(res, "Invalid ID format");
821
+ return;
822
+ }
823
+ const query = {
824
+ _id: new mongoose.Types.ObjectId(id),
825
+ ...buildOrgFilter(req, { ...config, withOrganization, orgField })
826
+ };
827
+ const result = await model.deleteOne(query);
828
+ if (result.deletedCount === 0) {
829
+ notFoundResponse(res, `${resourceName} not found`);
830
+ return;
831
+ }
832
+ if (afterDelete) {
833
+ await afterDelete(id, req);
834
+ }
835
+ logger.info(`${resourceName} deleted successfully`, { id });
836
+ noContentResponse(res, null, `${resourceName} deleted successfully`);
837
+ } catch (error) {
838
+ logger.error(`Error in delete ${resourceName}`, {
839
+ error: error instanceof Error ? error.message : "Unknown error",
840
+ id: req.params.id
841
+ });
842
+ errorResponse(res, `Failed to delete ${resourceName.toLowerCase()}`);
843
+ }
844
+ };
845
+ const bulkDelete = async (req, res, _next) => {
846
+ try {
847
+ const bulkReq = req;
848
+ const { deleteIds = [], deleteAll = false } = bulkReq;
849
+ const baseFilter = buildOrgFilter(req, { ...config, withOrganization, orgField });
850
+ let filter;
851
+ if (deleteAll) {
852
+ filter = baseFilter;
853
+ } else if (deleteIds.length > 0) {
854
+ filter = {
855
+ ...baseFilter,
856
+ _id: { $in: deleteIds.map((id) => new mongoose.Types.ObjectId(id)) }
857
+ };
858
+ } else {
859
+ badRequestResponse(res, "No IDs provided for deletion");
860
+ return;
861
+ }
862
+ const result = await model.deleteMany(filter);
863
+ if (afterDelete && deleteIds.length > 0) {
864
+ await Promise.all(deleteIds.map((id) => afterDelete(id, req)));
865
+ }
866
+ logger.info(`${resourceName}(s) bulk deleted successfully`, {
867
+ deletedCount: result.deletedCount,
868
+ deleteAll
869
+ });
870
+ successResponse(
871
+ res,
872
+ { deletedCount: result.deletedCount },
873
+ `${result.deletedCount} ${resourceName.toLowerCase()}(s) deleted successfully`
874
+ );
875
+ } catch (error) {
876
+ logger.error(`Error in bulkDelete ${resourceName}`, {
877
+ error: error instanceof Error ? error.message : "Unknown error"
878
+ });
879
+ errorResponse(res, `Failed to delete ${resourceName.toLowerCase()}(s)`);
880
+ }
881
+ };
882
+ return {
883
+ getAll,
884
+ getById,
885
+ create,
886
+ update,
887
+ deleteOne,
888
+ bulkDelete
889
+ };
890
+ }
891
+ function createPaginationMiddleware(model, config = {}) {
892
+ const {
893
+ searchFields = [],
894
+ regexFilterFields = [],
895
+ withOrganization = true,
896
+ orgField = "organizationId"
897
+ } = config;
898
+ return async (req, res, next) => {
899
+ try {
900
+ const page = parseInt(req.query.page) || 1;
901
+ const limit = parseInt(req.query.limit) || 10;
902
+ const sortField = req.query.sortBy || "createdAt";
903
+ const sortOrder = req.query.sortOrder || "desc";
904
+ const search = req.query.search;
905
+ const query = {};
906
+ if (withOrganization) {
907
+ const orgId = getOrgId(req, orgField);
908
+ if (orgId) {
909
+ query[orgField] = orgId;
910
+ }
911
+ }
912
+ if (search && searchFields.length > 0) {
913
+ query.$or = searchFields.map((field) => ({
914
+ [field]: { $regex: search, $options: "i" }
915
+ }));
916
+ }
917
+ const filterableParams = Object.keys(req.query).filter(
918
+ (key) => !["page", "limit", "sortBy", "sortOrder", "search"].includes(key)
919
+ );
920
+ filterableParams.forEach((key) => {
921
+ const value = req.query[key];
922
+ if (value !== void 0 && value !== "" && value !== "all") {
923
+ if (regexFilterFields.includes(key)) {
924
+ query[key] = { $regex: value, $options: "i" };
925
+ } else {
926
+ query[key] = value;
927
+ }
928
+ }
929
+ });
930
+ const sortQuery = { [sortField]: sortOrder };
931
+ const skip = (page - 1) * limit;
932
+ const [data, total] = await Promise.all([
933
+ model.find(query).sort(sortQuery).skip(skip).limit(limit),
934
+ model.countDocuments(query)
935
+ ]);
936
+ const paginatedRes = res;
937
+ paginatedRes.paginatedResult = {
938
+ data,
939
+ meta: {
940
+ page,
941
+ limit,
942
+ total,
943
+ totalPages: Math.ceil(total / limit)
944
+ },
945
+ columns: extractSchemaMeta(model, config.createSchema)
946
+ };
947
+ next();
948
+ } catch (error) {
949
+ next(error);
950
+ }
951
+ };
952
+ }
953
+ var parseBulkDelete = (req, res, next) => {
954
+ try {
955
+ const bulkReq = req;
956
+ let ids = [];
957
+ if (Array.isArray(req.body)) {
958
+ ids = req.body;
959
+ } else if (req.body && Array.isArray(req.body.ids)) {
960
+ ids = req.body.ids;
961
+ } else if (req.body && typeof req.body === "object") {
962
+ if (Array.isArray(req.body.data)) {
963
+ ids = req.body.data;
964
+ }
965
+ }
966
+ if (ids.length === 0) {
967
+ return badRequestResponse(
968
+ res,
969
+ 'Request body must contain an array of IDs. Use ["*"] to delete all records or ["id1", "id2"] to delete specific records.'
970
+ );
971
+ }
972
+ if (ids.length === 1 && ids[0] === "*") {
973
+ bulkReq.deleteAll = true;
974
+ bulkReq.deleteIds = [];
975
+ logger.info("Bulk delete: Deleting all records");
976
+ return next();
977
+ }
978
+ const validIds = [];
979
+ const invalidIds = [];
980
+ for (const id of ids) {
981
+ if (typeof id === "string" && mongoose.Types.ObjectId.isValid(id)) {
982
+ validIds.push(id);
983
+ } else {
984
+ invalidIds.push(id);
985
+ }
986
+ }
987
+ if (invalidIds.length > 0) {
988
+ return badRequestResponse(
989
+ res,
990
+ `Invalid ID format(s): ${invalidIds.slice(0, 5).join(", ")}${invalidIds.length > 5 ? "..." : ""}. All IDs must be valid MongoDB ObjectIds.`
991
+ );
992
+ }
993
+ if (validIds.length === 0) {
994
+ return badRequestResponse(res, "No valid IDs provided for deletion.");
995
+ }
996
+ bulkReq.deleteAll = false;
997
+ bulkReq.deleteIds = validIds;
998
+ logger.info(`Bulk delete: Deleting ${validIds.length} record(s)`);
999
+ next();
1000
+ } catch (error) {
1001
+ logger.error("Error in parseBulkDelete middleware", error);
1002
+ return badRequestResponse(res, "Failed to parse delete request");
1003
+ }
1004
+ };
1005
+ var buildDeleteFilter = (req, organizationId) => {
1006
+ const filter = {
1007
+ organizationId: new mongoose.Types.ObjectId(organizationId)
1008
+ };
1009
+ if (!req.deleteAll && req.deleteIds && req.deleteIds.length > 0) {
1010
+ filter._id = {
1011
+ $in: req.deleteIds.map((id) => new mongoose.Types.ObjectId(id))
1012
+ };
1013
+ }
1014
+ return filter;
1015
+ };
1016
+ var createBulkDeleteHandler = (Model2, modelName) => {
1017
+ return async (req, res) => {
1018
+ const bulkReq = req;
1019
+ const organizationId = req.headers["x-organization-id"];
1020
+ if (!organizationId) {
1021
+ return badRequestResponse(res, "Organization ID is required");
1022
+ }
1023
+ try {
1024
+ const filter = buildDeleteFilter(bulkReq, organizationId);
1025
+ const result = await Model2.deleteMany(filter);
1026
+ const deletedCount = result.deletedCount || 0;
1027
+ logger.info(`Bulk delete completed: ${deletedCount} ${modelName}(s) deleted`, {
1028
+ organizationId,
1029
+ deleteAll: bulkReq.deleteAll,
1030
+ requestedIds: bulkReq.deleteIds?.length || "all",
1031
+ deletedCount
1032
+ });
1033
+ return res.status(200).json({
1034
+ message: `Successfully deleted ${deletedCount} ${modelName}(s)`,
1035
+ data: {
1036
+ deletedCount,
1037
+ deleteAll: bulkReq.deleteAll
1038
+ },
1039
+ status: "success",
1040
+ statusCode: 200
1041
+ });
1042
+ } catch (error) {
1043
+ logger.error(`Error in bulk delete ${modelName}`, error);
1044
+ return res.status(500).json({
1045
+ message: `Failed to delete ${modelName}(s)`,
1046
+ data: null,
1047
+ status: "error",
1048
+ statusCode: 500
1049
+ });
1050
+ }
1051
+ };
1052
+ };
1053
+
429
1054
  // src/server/utils/filter-builder.ts
430
1055
  var buildFilter = (options) => {
431
1056
  const {
@@ -720,44 +1345,749 @@ var packageCheckServer = {
720
1345
  print: printPackageCheckSummary
721
1346
  };
722
1347
 
1348
+ // src/server/configs/cors.config.ts
1349
+ var DEFAULT_CORS_CONFIG = {
1350
+ productionOrigins: [],
1351
+ developmentOrigins: [
1352
+ "http://localhost:3000",
1353
+ "http://localhost:4000",
1354
+ "http://localhost:5000",
1355
+ "http://localhost:5173",
1356
+ "http://localhost:8080",
1357
+ "http://127.0.0.1:3000",
1358
+ "http://127.0.0.1:4000",
1359
+ "http://127.0.0.1:5000",
1360
+ "http://127.0.0.1:5173",
1361
+ "http://127.0.0.1:8080"
1362
+ ],
1363
+ allowedSubdomains: [],
1364
+ originPatterns: [],
1365
+ allowNoOrigin: true,
1366
+ allowAllInDev: true,
1367
+ credentials: true,
1368
+ methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"],
1369
+ allowedHeaders: [
1370
+ "Content-Type",
1371
+ "Authorization",
1372
+ "X-Requested-With",
1373
+ "Accept",
1374
+ "Origin",
1375
+ "X-API-Key",
1376
+ "X-Organization-Id",
1377
+ "X-Request-Id"
1378
+ ],
1379
+ exposedHeaders: [
1380
+ "Content-Range",
1381
+ "X-Content-Range",
1382
+ "X-Total-Count",
1383
+ "X-Request-Id"
1384
+ ],
1385
+ maxAge: 86400
1386
+ // 24 hours
1387
+ };
1388
+ var createCorsOptions = (config = {}) => {
1389
+ const finalConfig = { ...DEFAULT_CORS_CONFIG, ...config };
1390
+ const {
1391
+ productionOrigins,
1392
+ developmentOrigins,
1393
+ allowedSubdomains,
1394
+ originPatterns,
1395
+ allowNoOrigin,
1396
+ allowAllInDev,
1397
+ customValidator,
1398
+ credentials,
1399
+ methods,
1400
+ allowedHeaders,
1401
+ exposedHeaders,
1402
+ maxAge
1403
+ } = finalConfig;
1404
+ const allOrigins = /* @__PURE__ */ new Set([...productionOrigins, ...developmentOrigins]);
1405
+ const originHandler = (origin, callback) => {
1406
+ if (!origin) {
1407
+ callback(null, allowNoOrigin);
1408
+ return;
1409
+ }
1410
+ if (allOrigins.has(origin)) {
1411
+ callback(null, true);
1412
+ return;
1413
+ }
1414
+ if (allowedSubdomains.some((subdomain) => origin.endsWith(subdomain))) {
1415
+ callback(null, true);
1416
+ return;
1417
+ }
1418
+ if (originPatterns.some((pattern) => pattern.test(origin))) {
1419
+ callback(null, true);
1420
+ return;
1421
+ }
1422
+ if (customValidator && customValidator(origin)) {
1423
+ callback(null, true);
1424
+ return;
1425
+ }
1426
+ if (process.env.NODE_ENV !== "production" && allowAllInDev) {
1427
+ callback(null, true);
1428
+ return;
1429
+ }
1430
+ if (process.env.NODE_ENV === "production") {
1431
+ callback(new Error(`Origin ${origin} not allowed by CORS`));
1432
+ return;
1433
+ }
1434
+ callback(null, true);
1435
+ };
1436
+ return {
1437
+ origin: originHandler,
1438
+ credentials,
1439
+ methods,
1440
+ allowedHeaders,
1441
+ exposedHeaders,
1442
+ maxAge
1443
+ };
1444
+ };
1445
+ var createBrandCorsOptions = (brandDomain, additionalConfig = {}) => {
1446
+ const productionOrigins = [
1447
+ `https://${brandDomain}`,
1448
+ `https://www.${brandDomain}`
1449
+ ];
1450
+ const allowedSubdomains = [`.${brandDomain}`];
1451
+ return createCorsOptions({
1452
+ productionOrigins,
1453
+ allowedSubdomains,
1454
+ ...additionalConfig
1455
+ });
1456
+ };
1457
+ var createMultiBrandCorsOptions = (domains, additionalConfig = {}) => {
1458
+ const productionOrigins = domains.flatMap((domain) => [
1459
+ `https://${domain}`,
1460
+ `https://www.${domain}`
1461
+ ]);
1462
+ const allowedSubdomains = domains.map((domain) => `.${domain}`);
1463
+ return createCorsOptions({
1464
+ productionOrigins,
1465
+ allowedSubdomains,
1466
+ ...additionalConfig
1467
+ });
1468
+ };
1469
+ var EXYCONN_CORS_CONFIG = {
1470
+ productionOrigins: [
1471
+ "https://exyconn.com",
1472
+ "https://www.exyconn.com",
1473
+ "https://botify.life",
1474
+ "https://www.botify.life",
1475
+ "https://partywings.fun",
1476
+ "https://www.partywings.fun",
1477
+ "https://sibera.work",
1478
+ "https://www.sibera.work",
1479
+ "https://spentiva.com",
1480
+ "https://www.spentiva.com"
1481
+ ],
1482
+ allowedSubdomains: [
1483
+ ".exyconn.com",
1484
+ ".botify.life",
1485
+ ".partywings.fun",
1486
+ ".sibera.work",
1487
+ ".spentiva.com"
1488
+ ],
1489
+ developmentOrigins: [
1490
+ "http://localhost:3000",
1491
+ "http://localhost:4000",
1492
+ "http://localhost:4001",
1493
+ "http://localhost:4002",
1494
+ "http://localhost:4003",
1495
+ "http://localhost:4004",
1496
+ "http://localhost:4005",
1497
+ "http://localhost:5173",
1498
+ "http://127.0.0.1:3000",
1499
+ "http://127.0.0.1:4000",
1500
+ "http://127.0.0.1:5173"
1501
+ ]
1502
+ };
1503
+ var STRICT_CORS_CONFIG = {
1504
+ allowNoOrigin: false,
1505
+ allowAllInDev: false,
1506
+ methods: ["GET", "POST", "PUT", "DELETE"]
1507
+ };
1508
+ var PERMISSIVE_CORS_CONFIG = {
1509
+ allowNoOrigin: true,
1510
+ allowAllInDev: true,
1511
+ originPatterns: [/localhost/, /127\.0\.0\.1/]
1512
+ };
1513
+ var corsOptions = createCorsOptions(EXYCONN_CORS_CONFIG);
1514
+ var DEFAULT_RATE_LIMIT_TIERS = {
1515
+ STANDARD: {
1516
+ windowMs: 15 * 60 * 1e3,
1517
+ // 15 minutes
1518
+ maxRequests: 100,
1519
+ message: "Too many requests, please try again later.",
1520
+ skipSuccessfulRequests: false,
1521
+ skipFailedRequests: false
1522
+ },
1523
+ STRICT: {
1524
+ windowMs: 15 * 60 * 1e3,
1525
+ // 15 minutes
1526
+ maxRequests: 20,
1527
+ message: "Too many requests, please try again later.",
1528
+ skipSuccessfulRequests: false,
1529
+ skipFailedRequests: false
1530
+ },
1531
+ DDOS: {
1532
+ windowMs: 60 * 1e3,
1533
+ // 1 minute
1534
+ maxRequests: 60,
1535
+ message: "Rate limit exceeded. Please slow down.",
1536
+ skipSuccessfulRequests: false,
1537
+ skipFailedRequests: false
1538
+ },
1539
+ // Additional presets
1540
+ VERY_STRICT: {
1541
+ windowMs: 60 * 60 * 1e3,
1542
+ // 1 hour
1543
+ maxRequests: 5,
1544
+ message: "Too many attempts. Please try again in an hour.",
1545
+ skipSuccessfulRequests: false,
1546
+ skipFailedRequests: false
1547
+ },
1548
+ RELAXED: {
1549
+ windowMs: 15 * 60 * 1e3,
1550
+ // 15 minutes
1551
+ maxRequests: 500,
1552
+ message: "Rate limit exceeded.",
1553
+ skipSuccessfulRequests: false,
1554
+ skipFailedRequests: false
1555
+ },
1556
+ API: {
1557
+ windowMs: 60 * 1e3,
1558
+ // 1 minute
1559
+ maxRequests: 30,
1560
+ message: "API rate limit exceeded.",
1561
+ skipSuccessfulRequests: false,
1562
+ skipFailedRequests: false
1563
+ }
1564
+ };
1565
+ var defaultKeyGenerator = (req) => {
1566
+ const forwarded = req.headers["x-forwarded-for"];
1567
+ const ip = forwarded ? Array.isArray(forwarded) ? forwarded[0] : forwarded.split(",")[0].trim() : req.ip || req.socket.remoteAddress || "unknown";
1568
+ return ip;
1569
+ };
1570
+ var createPrefixedKeyGenerator = (prefix) => (req) => {
1571
+ return `${prefix}:${defaultKeyGenerator(req)}`;
1572
+ };
1573
+ var createUserKeyGenerator = (getUserId) => (req) => {
1574
+ const userId = getUserId(req);
1575
+ return userId || defaultKeyGenerator(req);
1576
+ };
1577
+ var createApiKeyGenerator = (headerName = "x-api-key") => (req) => {
1578
+ const apiKey = req.headers[headerName.toLowerCase()];
1579
+ return apiKey || defaultKeyGenerator(req);
1580
+ };
1581
+ var createRateLimitResponse = (message, retryAfter) => ({
1582
+ status: "error",
1583
+ statusCode: 429,
1584
+ message,
1585
+ ...retryAfter
1586
+ });
1587
+ var createRateLimiter = (tierConfig, options = {}) => {
1588
+ const {
1589
+ standardHeaders = true,
1590
+ legacyHeaders = false,
1591
+ keyGenerator = defaultKeyGenerator,
1592
+ skip,
1593
+ handler
1594
+ } = options;
1595
+ return rateLimit__default.default({
1596
+ windowMs: tierConfig.windowMs,
1597
+ max: tierConfig.maxRequests,
1598
+ message: createRateLimitResponse(tierConfig.message),
1599
+ standardHeaders,
1600
+ legacyHeaders,
1601
+ keyGenerator,
1602
+ skip,
1603
+ handler,
1604
+ skipSuccessfulRequests: tierConfig.skipSuccessfulRequests,
1605
+ skipFailedRequests: tierConfig.skipFailedRequests
1606
+ });
1607
+ };
1608
+ var createStandardRateLimiter = (config = {}, options = {}) => {
1609
+ const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.STANDARD, ...config };
1610
+ return createRateLimiter(tierConfig, options);
1611
+ };
1612
+ var createStrictRateLimiter = (config = {}, options = {}) => {
1613
+ const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.STRICT, ...config };
1614
+ return createRateLimiter(tierConfig, options);
1615
+ };
1616
+ var createDdosRateLimiter = (config = {}, options = {}) => {
1617
+ const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.DDOS, ...config };
1618
+ return createRateLimiter(tierConfig, options);
1619
+ };
1620
+ var createApiRateLimiter = (config = {}, options = {}) => {
1621
+ const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.API, ...config };
1622
+ return createRateLimiter(tierConfig, {
1623
+ keyGenerator: createApiKeyGenerator(),
1624
+ ...options
1625
+ });
1626
+ };
1627
+ var RateLimiterBuilder = class {
1628
+ constructor(preset = "STANDARD") {
1629
+ const presetConfig = DEFAULT_RATE_LIMIT_TIERS[preset];
1630
+ this.config = {
1631
+ windowMs: presetConfig.windowMs,
1632
+ maxRequests: presetConfig.maxRequests,
1633
+ message: presetConfig.message,
1634
+ skipSuccessfulRequests: presetConfig.skipSuccessfulRequests ?? false,
1635
+ skipFailedRequests: presetConfig.skipFailedRequests ?? false
1636
+ };
1637
+ this.options = {};
1638
+ }
1639
+ /**
1640
+ * Set window duration
1641
+ */
1642
+ windowMs(ms) {
1643
+ this.config.windowMs = ms;
1644
+ return this;
1645
+ }
1646
+ /**
1647
+ * Set window duration in minutes
1648
+ */
1649
+ windowMinutes(minutes) {
1650
+ this.config.windowMs = minutes * 60 * 1e3;
1651
+ return this;
1652
+ }
1653
+ /**
1654
+ * Set window duration in hours
1655
+ */
1656
+ windowHours(hours) {
1657
+ this.config.windowMs = hours * 60 * 60 * 1e3;
1658
+ return this;
1659
+ }
1660
+ /**
1661
+ * Set maximum requests
1662
+ */
1663
+ max(requests) {
1664
+ this.config.maxRequests = requests;
1665
+ return this;
1666
+ }
1667
+ /**
1668
+ * Set error message
1669
+ */
1670
+ message(msg) {
1671
+ this.config.message = msg;
1672
+ return this;
1673
+ }
1674
+ /**
1675
+ * Skip successful requests
1676
+ */
1677
+ skipSuccessful(skip = true) {
1678
+ this.config.skipSuccessfulRequests = skip;
1679
+ return this;
1680
+ }
1681
+ /**
1682
+ * Skip failed requests
1683
+ */
1684
+ skipFailed(skip = true) {
1685
+ this.config.skipFailedRequests = skip;
1686
+ return this;
1687
+ }
1688
+ /**
1689
+ * Set key generator
1690
+ */
1691
+ keyBy(generator) {
1692
+ this.options.keyGenerator = generator;
1693
+ return this;
1694
+ }
1695
+ /**
1696
+ * Key by IP (default)
1697
+ */
1698
+ keyByIp() {
1699
+ this.options.keyGenerator = defaultKeyGenerator;
1700
+ return this;
1701
+ }
1702
+ /**
1703
+ * Key by API key
1704
+ */
1705
+ keyByApiKey(headerName) {
1706
+ this.options.keyGenerator = createApiKeyGenerator(headerName);
1707
+ return this;
1708
+ }
1709
+ /**
1710
+ * Skip certain requests
1711
+ */
1712
+ skipWhen(predicate) {
1713
+ this.options.skip = predicate;
1714
+ return this;
1715
+ }
1716
+ /**
1717
+ * Build the rate limiter
1718
+ */
1719
+ build() {
1720
+ return createRateLimiter(this.config, this.options);
1721
+ }
1722
+ };
1723
+ var rateLimiter = (preset) => {
1724
+ return new RateLimiterBuilder(preset);
1725
+ };
1726
+ var RATE_LIMIT_CONFIG = {
1727
+ STANDARD: DEFAULT_RATE_LIMIT_TIERS.STANDARD,
1728
+ STRICT: DEFAULT_RATE_LIMIT_TIERS.STRICT,
1729
+ DDOS: DEFAULT_RATE_LIMIT_TIERS.DDOS
1730
+ };
1731
+ var standardRateLimiter = createStandardRateLimiter();
1732
+ var strictRateLimiter = createStrictRateLimiter();
1733
+ var ddosProtectionLimiter = createDdosRateLimiter();
1734
+
1735
+ // src/server/configs/server.config.ts
1736
+ var DEFAULT_SERVER_CONFIG = {
1737
+ name: "app-server",
1738
+ version: "1.0.0",
1739
+ environment: process.env.NODE_ENV || "development",
1740
+ port: parseInt(process.env.PORT || "3000", 10),
1741
+ host: process.env.HOST || "0.0.0.0",
1742
+ basePath: "/api",
1743
+ debug: process.env.DEBUG === "true",
1744
+ trustProxy: true
1745
+ };
1746
+ var DEFAULT_DATABASE_CONFIG = {
1747
+ uri: process.env.DATABASE_URL || process.env.MONGODB_URI || "",
1748
+ name: process.env.DATABASE_NAME || "app_db",
1749
+ maxPoolSize: process.env.NODE_ENV === "production" ? 50 : 10,
1750
+ minPoolSize: process.env.NODE_ENV === "production" ? 10 : 5,
1751
+ socketTimeoutMS: 45e3,
1752
+ serverSelectionTimeoutMS: 1e4,
1753
+ maxIdleTimeMS: 1e4,
1754
+ retryWrites: true,
1755
+ retryReads: true,
1756
+ writeConcern: "majority"
1757
+ };
1758
+ var DEFAULT_AUTH_CONFIG = {
1759
+ jwtSecret: process.env.JWT_SECRET || "",
1760
+ jwtExpiresIn: process.env.JWT_EXPIRES_IN || "7d",
1761
+ refreshTokenExpiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || "30d",
1762
+ enableRefreshTokens: true,
1763
+ apiKeyHeader: "x-api-key",
1764
+ orgHeader: "x-organization-id"
1765
+ };
1766
+ var DEFAULT_LOGGING_CONFIG = {
1767
+ level: process.env.LOG_LEVEL || "info",
1768
+ logsDir: process.env.LOGS_DIR || "logs",
1769
+ maxSize: "20m",
1770
+ maxFiles: "14d",
1771
+ errorMaxFiles: "30d",
1772
+ console: true,
1773
+ file: process.env.NODE_ENV === "production",
1774
+ json: process.env.NODE_ENV === "production"
1775
+ };
1776
+ var DEFAULT_CORS_ORIGINS = {
1777
+ production: [],
1778
+ development: [
1779
+ "http://localhost:3000",
1780
+ "http://localhost:4000",
1781
+ "http://localhost:5173",
1782
+ "http://127.0.0.1:3000",
1783
+ "http://127.0.0.1:4000",
1784
+ "http://127.0.0.1:5173"
1785
+ ],
1786
+ patterns: []
1787
+ };
1788
+ var DEFAULT_RATE_LIMIT_CONFIG = {
1789
+ enabled: true,
1790
+ standard: {
1791
+ windowMs: 15 * 60 * 1e3,
1792
+ // 15 minutes
1793
+ maxRequests: 100,
1794
+ message: "Too many requests, please try again later."
1795
+ },
1796
+ strict: {
1797
+ windowMs: 15 * 60 * 1e3,
1798
+ // 15 minutes
1799
+ maxRequests: 20,
1800
+ message: "Too many requests, please try again later."
1801
+ },
1802
+ ddos: {
1803
+ windowMs: 60 * 1e3,
1804
+ // 1 minute
1805
+ maxRequests: 60,
1806
+ message: "Rate limit exceeded. Please slow down.",
1807
+ skipSuccessfulRequests: false
1808
+ }
1809
+ };
1810
+ function deepMerge(target, source) {
1811
+ const result = { ...target };
1812
+ for (const key in source) {
1813
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
1814
+ const sourceValue = source[key];
1815
+ const targetValue = target[key];
1816
+ if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) {
1817
+ result[key] = deepMerge(
1818
+ targetValue,
1819
+ sourceValue
1820
+ );
1821
+ } else if (sourceValue !== void 0) {
1822
+ result[key] = sourceValue;
1823
+ }
1824
+ }
1825
+ }
1826
+ return result;
1827
+ }
1828
+ var ConfigBuilder = class {
1829
+ constructor() {
1830
+ this.config = {
1831
+ server: { ...DEFAULT_SERVER_CONFIG },
1832
+ database: { ...DEFAULT_DATABASE_CONFIG },
1833
+ auth: { ...DEFAULT_AUTH_CONFIG },
1834
+ logging: { ...DEFAULT_LOGGING_CONFIG },
1835
+ cors: { ...DEFAULT_CORS_ORIGINS },
1836
+ rateLimit: { ...DEFAULT_RATE_LIMIT_CONFIG }
1837
+ };
1838
+ }
1839
+ /**
1840
+ * Set server configuration
1841
+ */
1842
+ setServer(config) {
1843
+ this.config.server = deepMerge(this.config.server, config);
1844
+ return this;
1845
+ }
1846
+ /**
1847
+ * Set database configuration
1848
+ */
1849
+ setDatabase(config) {
1850
+ this.config.database = deepMerge(this.config.database, config);
1851
+ return this;
1852
+ }
1853
+ /**
1854
+ * Set auth configuration
1855
+ */
1856
+ setAuth(config) {
1857
+ this.config.auth = deepMerge(this.config.auth, config);
1858
+ return this;
1859
+ }
1860
+ /**
1861
+ * Set logging configuration
1862
+ */
1863
+ setLogging(config) {
1864
+ this.config.logging = deepMerge(this.config.logging, config);
1865
+ return this;
1866
+ }
1867
+ /**
1868
+ * Set CORS origins
1869
+ */
1870
+ setCorsOrigins(config) {
1871
+ this.config.cors = deepMerge(this.config.cors, config);
1872
+ return this;
1873
+ }
1874
+ /**
1875
+ * Add CORS production origin
1876
+ */
1877
+ addProductionOrigin(origin) {
1878
+ if (!this.config.cors.production.includes(origin)) {
1879
+ this.config.cors.production.push(origin);
1880
+ }
1881
+ return this;
1882
+ }
1883
+ /**
1884
+ * Add CORS development origin
1885
+ */
1886
+ addDevelopmentOrigin(origin) {
1887
+ if (!this.config.cors.development.includes(origin)) {
1888
+ this.config.cors.development.push(origin);
1889
+ }
1890
+ return this;
1891
+ }
1892
+ /**
1893
+ * Add CORS pattern
1894
+ */
1895
+ addCorsPattern(pattern) {
1896
+ if (!this.config.cors.patterns.includes(pattern)) {
1897
+ this.config.cors.patterns.push(pattern);
1898
+ }
1899
+ return this;
1900
+ }
1901
+ /**
1902
+ * Set rate limit configuration
1903
+ */
1904
+ setRateLimit(config) {
1905
+ this.config.rateLimit = deepMerge(this.config.rateLimit, config);
1906
+ return this;
1907
+ }
1908
+ /**
1909
+ * Add custom rate limit tier
1910
+ */
1911
+ addRateLimitTier(name, tier) {
1912
+ if (!this.config.rateLimit.custom) {
1913
+ this.config.rateLimit.custom = {};
1914
+ }
1915
+ this.config.rateLimit.custom[name] = tier;
1916
+ return this;
1917
+ }
1918
+ /**
1919
+ * Set custom configuration
1920
+ */
1921
+ setCustom(key, value) {
1922
+ if (!this.config.custom) {
1923
+ this.config.custom = {};
1924
+ }
1925
+ this.config.custom[key] = value;
1926
+ return this;
1927
+ }
1928
+ /**
1929
+ * Load configuration from environment variables
1930
+ */
1931
+ loadFromEnv() {
1932
+ if (process.env.SERVER_NAME) this.config.server.name = process.env.SERVER_NAME;
1933
+ if (process.env.SERVER_VERSION) this.config.server.version = process.env.SERVER_VERSION;
1934
+ if (process.env.PORT) this.config.server.port = parseInt(process.env.PORT, 10);
1935
+ if (process.env.HOST) this.config.server.host = process.env.HOST;
1936
+ if (process.env.BASE_PATH) this.config.server.basePath = process.env.BASE_PATH;
1937
+ if (process.env.DATABASE_URL) this.config.database.uri = process.env.DATABASE_URL;
1938
+ if (process.env.MONGODB_URI) this.config.database.uri = process.env.MONGODB_URI;
1939
+ if (process.env.DATABASE_NAME) this.config.database.name = process.env.DATABASE_NAME;
1940
+ if (process.env.JWT_SECRET) this.config.auth.jwtSecret = process.env.JWT_SECRET;
1941
+ if (process.env.JWT_EXPIRES_IN) this.config.auth.jwtExpiresIn = process.env.JWT_EXPIRES_IN;
1942
+ if (process.env.LOG_LEVEL) this.config.logging.level = process.env.LOG_LEVEL;
1943
+ if (process.env.LOGS_DIR) this.config.logging.logsDir = process.env.LOGS_DIR;
1944
+ if (process.env.CORS_ORIGINS) {
1945
+ const origins = process.env.CORS_ORIGINS.split(",").map((o) => o.trim());
1946
+ this.config.cors.production.push(...origins);
1947
+ }
1948
+ return this;
1949
+ }
1950
+ /**
1951
+ * Validate configuration
1952
+ */
1953
+ validate() {
1954
+ const errors = [];
1955
+ if (!this.config.server.name) errors.push("Server name is required");
1956
+ if (this.config.server.port < 1 || this.config.server.port > 65535) {
1957
+ errors.push("Server port must be between 1 and 65535");
1958
+ }
1959
+ if (this.config.server.environment === "production") {
1960
+ if (!this.config.auth.jwtSecret || this.config.auth.jwtSecret.length < 32) {
1961
+ errors.push("JWT secret must be at least 32 characters in production");
1962
+ }
1963
+ }
1964
+ return { valid: errors.length === 0, errors };
1965
+ }
1966
+ /**
1967
+ * Build the final configuration
1968
+ */
1969
+ build() {
1970
+ return { ...this.config };
1971
+ }
1972
+ };
1973
+ var createConfig = () => {
1974
+ return new ConfigBuilder();
1975
+ };
1976
+ var buildConfig = (partial = {}) => {
1977
+ const builder = createConfig().loadFromEnv();
1978
+ if (partial.server) builder.setServer(partial.server);
1979
+ if (partial.database) builder.setDatabase(partial.database);
1980
+ if (partial.auth) builder.setAuth(partial.auth);
1981
+ if (partial.logging) builder.setLogging(partial.logging);
1982
+ if (partial.cors) builder.setCorsOrigins(partial.cors);
1983
+ if (partial.rateLimit) builder.setRateLimit(partial.rateLimit);
1984
+ return builder.build();
1985
+ };
1986
+ var isProduction = (config) => {
1987
+ return (config?.environment || process.env.NODE_ENV) === "production";
1988
+ };
1989
+ var isDevelopment = (config) => {
1990
+ return (config?.environment || process.env.NODE_ENV) === "development";
1991
+ };
1992
+ var isTest = (config) => {
1993
+ return (config?.environment || process.env.NODE_ENV) === "test";
1994
+ };
1995
+ var getDatabaseOptions = (config) => {
1996
+ return {
1997
+ maxPoolSize: config.maxPoolSize,
1998
+ minPoolSize: config.minPoolSize,
1999
+ socketTimeoutMS: config.socketTimeoutMS,
2000
+ serverSelectionTimeoutMS: config.serverSelectionTimeoutMS,
2001
+ maxIdleTimeMS: config.maxIdleTimeMS,
2002
+ retryWrites: config.retryWrites,
2003
+ retryReads: config.retryReads,
2004
+ w: config.writeConcern
2005
+ };
2006
+ };
2007
+
2008
+ exports.ConfigBuilder = ConfigBuilder;
2009
+ exports.DEFAULT_AUTH_CONFIG = DEFAULT_AUTH_CONFIG;
2010
+ exports.DEFAULT_CORS_CONFIG = DEFAULT_CORS_CONFIG;
2011
+ exports.DEFAULT_CORS_ORIGINS = DEFAULT_CORS_ORIGINS;
2012
+ exports.DEFAULT_DATABASE_CONFIG = DEFAULT_DATABASE_CONFIG;
2013
+ exports.DEFAULT_LOGGING_CONFIG = DEFAULT_LOGGING_CONFIG;
2014
+ exports.DEFAULT_RATE_LIMIT_CONFIG = DEFAULT_RATE_LIMIT_CONFIG;
2015
+ exports.DEFAULT_RATE_LIMIT_TIERS = DEFAULT_RATE_LIMIT_TIERS;
2016
+ exports.DEFAULT_SERVER_CONFIG = DEFAULT_SERVER_CONFIG;
2017
+ exports.EXYCONN_CORS_CONFIG = EXYCONN_CORS_CONFIG;
2018
+ exports.PERMISSIVE_CORS_CONFIG = PERMISSIVE_CORS_CONFIG;
2019
+ exports.RATE_LIMIT_CONFIG = RATE_LIMIT_CONFIG;
2020
+ exports.RateLimiterBuilder = RateLimiterBuilder;
2021
+ exports.STRICT_CORS_CONFIG = STRICT_CORS_CONFIG;
723
2022
  exports.StatusCode = StatusCode;
724
2023
  exports.StatusMessage = StatusMessage;
725
2024
  exports.authenticateApiKey = authenticateApiKey;
726
2025
  exports.authenticateJWT = authenticateJWT;
727
2026
  exports.badRequestResponse = badRequestResponse;
2027
+ exports.buildConfig = buildConfig;
2028
+ exports.buildDeleteFilter = buildDeleteFilter;
728
2029
  exports.buildFilter = buildFilter;
729
2030
  exports.buildPagination = buildPagination;
730
2031
  exports.buildPaginationMeta = buildPaginationMeta;
731
2032
  exports.checkPackageServer = checkPackageServer;
732
2033
  exports.conflictResponse = conflictResponse;
733
2034
  exports.connectDB = connectDB;
2035
+ exports.corsOptions = corsOptions;
2036
+ exports.createApiKeyGenerator = createApiKeyGenerator;
2037
+ exports.createApiRateLimiter = createApiRateLimiter;
2038
+ exports.createBrandCorsOptions = createBrandCorsOptions;
2039
+ exports.createBulkDeleteHandler = createBulkDeleteHandler;
2040
+ exports.createConfig = createConfig;
2041
+ exports.createCorsOptions = createCorsOptions;
2042
+ exports.createCrudControllers = createCrudControllers;
2043
+ exports.createDdosRateLimiter = createDdosRateLimiter;
734
2044
  exports.createLogger = createLogger;
735
2045
  exports.createMorganStream = createMorganStream;
2046
+ exports.createMultiBrandCorsOptions = createMultiBrandCorsOptions;
2047
+ exports.createPaginationMiddleware = createPaginationMiddleware;
2048
+ exports.createPrefixedKeyGenerator = createPrefixedKeyGenerator;
2049
+ exports.createRateLimiter = createRateLimiter;
2050
+ exports.createStandardRateLimiter = createStandardRateLimiter;
2051
+ exports.createStrictRateLimiter = createStrictRateLimiter;
2052
+ exports.createUserKeyGenerator = createUserKeyGenerator;
736
2053
  exports.createdResponse = createdResponse;
2054
+ exports.ddosProtectionLimiter = ddosProtectionLimiter;
2055
+ exports.defaultKeyGenerator = defaultKeyGenerator;
737
2056
  exports.disconnectDB = disconnectDB;
738
2057
  exports.errorResponse = errorResponse;
739
2058
  exports.extractColumns = extractColumns;
740
2059
  exports.extractOrganization = extractOrganization;
2060
+ exports.extractSchemaMeta = extractSchemaMeta;
741
2061
  exports.forbiddenResponse = forbiddenResponse;
742
2062
  exports.formatPackageCheckResult = formatPackageCheckResult;
743
2063
  exports.generateNcuCommand = generateNcuCommand;
744
2064
  exports.getConnectionStatus = getConnectionStatus;
2065
+ exports.getDatabaseOptions = getDatabaseOptions;
2066
+ exports.isDevelopment = isDevelopment;
2067
+ exports.isProduction = isProduction;
2068
+ exports.isTest = isTest;
745
2069
  exports.logger = logger;
746
2070
  exports.noContentResponse = noContentResponse;
747
2071
  exports.notFoundResponse = notFoundResponse;
748
2072
  exports.omitFields = omitFields;
749
2073
  exports.optionalAuthenticateJWT = optionalAuthenticateJWT;
750
2074
  exports.packageCheckServer = packageCheckServer;
2075
+ exports.parseBulkDelete = parseBulkDelete;
751
2076
  exports.pickFields = pickFields;
752
2077
  exports.printPackageCheckSummary = printPackageCheckSummary;
2078
+ exports.queryPagination = queryPagination;
2079
+ exports.queryParser = queryParser;
753
2080
  exports.rateLimitResponse = rateLimitResponse;
2081
+ exports.rateLimiter = rateLimiter;
754
2082
  exports.requireOrganization = requireOrganization;
755
2083
  exports.sanitizeDocument = sanitizeDocument;
756
2084
  exports.sanitizeUser = sanitizeUser;
757
2085
  exports.simpleLogger = simpleLogger;
2086
+ exports.standardRateLimiter = standardRateLimiter;
758
2087
  exports.statusCode = statusCode;
759
2088
  exports.statusMessage = statusMessage;
760
2089
  exports.stream = stream;
2090
+ exports.strictRateLimiter = strictRateLimiter;
761
2091
  exports.successResponse = successResponse;
762
2092
  exports.successResponseArr = successResponseArr;
763
2093
  exports.unauthorizedResponse = unauthorizedResponse;