@infuro/cms-core 1.0.35 → 1.0.38

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 (69) hide show
  1. package/dist/admin.cjs +1222 -1024
  2. package/dist/admin.d.cts +6 -0
  3. package/dist/admin.d.ts +6 -0
  4. package/dist/admin.js +1224 -1026
  5. package/dist/api.cjs +40 -39
  6. package/dist/api.js +8 -7
  7. package/dist/auth.cjs +51 -51
  8. package/dist/auth.d.cts +3 -3
  9. package/dist/auth.d.ts +3 -3
  10. package/dist/auth.js +3 -3
  11. package/dist/chunk-22FFHLTO.cjs +2356 -0
  12. package/dist/{chunk-CNTSLED2.cjs → chunk-E6WVP7BK.cjs} +75 -14
  13. package/dist/chunk-HCIRL37O.js +21 -0
  14. package/dist/chunk-HT5DI3XW.cjs +82 -0
  15. package/dist/{chunk-EF2JBPDM.js → chunk-KFFB6EMZ.js} +9 -0
  16. package/dist/chunk-NNQBWDCI.js +2325 -0
  17. package/dist/{chunk-TIAEXJLI.cjs → chunk-OIGCAGH7.cjs} +7 -7
  18. package/dist/{chunk-4TRPKBKO.js → chunk-P7QCLE5W.js} +52 -24
  19. package/dist/{chunk-2XE4X74C.js → chunk-PYUXVU75.js} +7801 -6871
  20. package/dist/{chunk-EQPITOP6.js → chunk-SBRBR3VO.js} +70 -9
  21. package/dist/{chunk-N44BJZ4C.cjs → chunk-SPQFNIYJ.cjs} +9 -0
  22. package/dist/chunk-UCKN4BBY.cjs +24 -0
  23. package/dist/{chunk-6KPQWRFW.js → chunk-UXMDSNIG.js} +1 -1
  24. package/dist/{chunk-BGJAV67S.cjs → chunk-VJDUKAJ3.cjs} +8207 -7261
  25. package/dist/{chunk-3KPNTQBV.cjs → chunk-WMXPKKXU.cjs} +52 -23
  26. package/dist/chunk-X47PBV4I.js +79 -0
  27. package/dist/cli.cjs +4 -2
  28. package/dist/cli.js +4 -2
  29. package/dist/emit-order-notification-trigger-D2N3KFHL.cjs +16 -0
  30. package/dist/emit-order-notification-trigger-IVLWKKJL.js +3 -0
  31. package/dist/{event-order-defaults-DUGDVGJI.cjs → event-order-defaults-EMWBIGL3.cjs} +9 -9
  32. package/dist/{event-order-defaults-6E543227.js → event-order-defaults-SDRNAZHC.js} +1 -1
  33. package/dist/index.cjs +805 -1059
  34. package/dist/index.d.cts +834 -728
  35. package/dist/index.d.ts +834 -728
  36. package/dist/index.js +385 -724
  37. package/dist/migrations/1775500000000-RssFeedsAndArticles.ts +0 -1
  38. package/dist/migrations/1775700000000-JobSchedules.ts +0 -1
  39. package/dist/migrations/1781220000000-AddWhatsappTriggerBindings.ts +23 -0
  40. package/dist/migrations/1781230000000-AddOrderNotificationTriggers.ts +46 -0
  41. package/dist/migrations/1781240000000-DropEventOrderNotificationColumns.ts +29 -0
  42. package/dist/migrations/1781250000000-RemoveOrderRefundedTrigger.ts +36 -0
  43. package/dist/migrations/1781260000000-RenameWhatsappTriggerBindingsToOrderNotificationBindings.ts +92 -0
  44. package/dist/migrations/1781270000000-ComboVendorId.ts +58 -0
  45. package/dist/order-notification-dispatcher-RU7BTGGX.cjs +18 -0
  46. package/dist/order-notification-dispatcher-XWIJYDGA.js +5 -0
  47. package/dist/{rbac-debug-db-D7ME7QUY.cjs → rbac-debug-db-KHG4FJC3.cjs} +2 -2
  48. package/dist/{rbac-debug-db-MTCTVNNM.js → rbac-debug-db-QWHOENBR.js} +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunk-46GKADYE.cjs +0 -84
  51. package/dist/chunk-7YIBMQDR.cjs +0 -86
  52. package/dist/chunk-J7XHLOLO.js +0 -830
  53. package/dist/chunk-JBRR5UYL.js +0 -87
  54. package/dist/chunk-JHF4YYRC.cjs +0 -188
  55. package/dist/chunk-PAMVINEX.cjs +0 -577
  56. package/dist/chunk-QM733NUK.js +0 -186
  57. package/dist/chunk-R73BKDRO.cjs +0 -90
  58. package/dist/chunk-TFUOHI37.cjs +0 -839
  59. package/dist/chunk-XDSNZUW7.js +0 -564
  60. package/dist/chunk-ZQ4X6OAU.js +0 -82
  61. package/dist/chunk-ZSVCP6N3.js +0 -83
  62. package/dist/email-FKR4FXFI.js +0 -5
  63. package/dist/email-KVKVPNCL.cjs +0 -58
  64. package/dist/event-order-notifications-5GSAOYVS.cjs +0 -27
  65. package/dist/event-order-notifications-IIIF23D5.js +0 -10
  66. package/dist/layout-36UFTHC5.cjs +0 -11
  67. package/dist/layout-AOBN26XG.js +0 -2
  68. package/dist/whatsapp-queue-3DXA4MXU.js +0 -2
  69. package/dist/whatsapp-queue-KG65MXD5.cjs +0 -15
package/dist/index.js CHANGED
@@ -1,24 +1,22 @@
1
- export { dispatchEventOrderNotifications } from './chunk-QM733NUK.js';
2
- export { EmailService, emailPlugin, emailTemplates, renderEmail } from './chunk-J7XHLOLO.js';
3
- export { renderLayout } from './chunk-ZQ4X6OAU.js';
4
- import './chunk-CRFV5WJK.js';
5
- export { queueWhatsApp, registerWhatsAppQueueProcessor } from './chunk-ZSVCP6N3.js';
6
- import { BlogGeneratorService, loadUserVendorContext, getPublicSettingsGroup } from './chunk-2XE4X74C.js';
7
- export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyVendorCustomersContactFilter, assertCaptchaOk, assertContactAllowedForVendorOrder, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, calculateOrderRefundPreview, calculateRefundFromPolicy, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventNotificationHandlers, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRssArticleSummaryFromItem, hashOtpCode, hydrateVendorSessionUser, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queueSms, registerJobRunnerWorker, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash } from './chunk-2XE4X74C.js';
8
- import { PgBossService } from './chunk-GO7PPYNU.js';
9
- export { JOB_RUNNER_QUEUE, PgBossService } from './chunk-GO7PPYNU.js';
10
- export { buildEventOrderTemplateVariables } from './chunk-JBRR5UYL.js';
1
+ export { emitOrderNotificationTrigger, fireOrderNotificationTrigger } from './chunk-X47PBV4I.js';
2
+ import { checkMultiVendorEnabled, loadUserVendorContext, getPublicSettingsGroup, BlogGeneratorService } from './chunk-PYUXVU75.js';
3
+ export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyVendorCustomersContactFilter, assertCaptchaOk, assertContactAllowedForVendorOrder, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRssArticleSummaryFromItem, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging } from './chunk-PYUXVU75.js';
4
+ import { mergeEmailLayoutCompanyDetails, parseEmailRecipientsFromConfig } from './chunk-NNQBWDCI.js';
5
+ export { EmailService, buildEventOrderTemplateVariables, emailPlugin, emailTemplates, getCompanyDetailsFromSettings, initWhatsappTriggerDispatcher, joinRecipientsForSend, mergeEmailLayoutCompanyDetails, parseEmailRecipientsFromConfig, queueWhatsApp, registerWhatsAppQueueProcessor, renderEmail, renderLayout, resendOrderNotification, serializeEmailRecipients } from './chunk-NNQBWDCI.js';
6
+ export { KNOWN_NOTIFICATION_TRIGGERS, notificationTriggerEmitter } from './chunk-HCIRL37O.js';
11
7
  import './chunk-JXF23MPG.js';
12
8
  import './chunk-WEMDMVHQ.js';
13
- import { mergeEmailLayoutCompanyDetails, parseEmailRecipientsFromConfig } from './chunk-XDSNZUW7.js';
14
- export { getCompanyDetailsFromSettings, joinRecipientsForSend, mergeEmailLayoutCompanyDetails, parseEmailRecipientsFromConfig, serializeEmailRecipients } from './chunk-XDSNZUW7.js';
15
9
  import { queueOrderPlacedEmails } from './chunk-5ELSW2UP.js';
16
10
  export { queueEmail, queueOrderPlacedEmails, queueVendorOnboardEmails, registerEmailQueueProcessor } from './chunk-5ELSW2UP.js';
17
11
  import './chunk-MQBT33IV.js';
18
- export { EVENT_ORDER_MESSAGE_TEMPLATE_DEFAULTS, EVENT_ORDER_TEMPLATE_KEY, EVENT_ORDER_TEMPLATE_VARIABLES, resolveEventOrderTemplate } from './chunk-EF2JBPDM.js';
19
- export { createCmsMiddleware, defaultPublicApiMethods, getNextAuthOptions, getStorefrontNextAuthOptions, isAuthDebugClientEnabled, isAuthDebugEnabled, logAuth, logAuthClient, nextAuthCookieDebugInfo, seedAdministratorPermissions, summarizeSessionUserForLog } from './chunk-EQPITOP6.js';
20
- export { OPEN_ENDPOINTS, PERMISSION_REQUIRED_ENDPOINTS, RBAC_ADMIN_ONLY_ENTITIES, canManageRoles, createAuthHelpers, createCmsAuthBundle, getRequiredPermission, isOpenEndpoint, isPublicMethod, sessionHasEntityAccess } from './chunk-6KPQWRFW.js';
21
- export { ADMIN_GROUP_NAME, SUPER_ADMIN_GROUP_ID, VENDOR_ADMIN_GROUP_ID, VENDOR_OWNER_GROUP_NAME, VENDOR_SCOPED_STORE_ENTITIES, VENDOR_STORE_RBAC_ENTITIES, canManageVendorRoles, canManageVendorTeam, canOnboardVendors, explainSessionEntityAccess, getPermissionableEntityKeys, hasEntityPermission, isPlatformAdministrator, isRbacDebugEnabled, isSuperAdmin, isSuperAdminGroupName, isVendorAdmin, isVendorGroupName, isVendorOwner, isVendorPortalUser, isVendorStaff, logEntityAccessDecision, logRbac, permissionRowsToRecord, resolveVendorScopeFromSessionUser, summarizeEntityPerms, vendorPortalFlagsFromUser } from './chunk-4TRPKBKO.js';
12
+ import './chunk-CRFV5WJK.js';
13
+ export { EVENT_ORDER_MESSAGE_TEMPLATE_DEFAULTS, EVENT_ORDER_TEMPLATE_KEY, EVENT_ORDER_TEMPLATE_VARIABLES, resolveEventOrderTemplate } from './chunk-KFFB6EMZ.js';
14
+ import { PgBossService } from './chunk-GO7PPYNU.js';
15
+ export { JOB_RUNNER_QUEUE, PgBossService } from './chunk-GO7PPYNU.js';
16
+ export { createCmsMiddleware, defaultPublicApiMethods, getNextAuthOptions, getStorefrontNextAuthOptions, isAuthDebugClientEnabled, isAuthDebugEnabled, logAuth, logAuthClient, nextAuthCookieDebugInfo, seedAdministratorPermissions, summarizeSessionUserForLog } from './chunk-SBRBR3VO.js';
17
+ export { OPEN_ENDPOINTS, PERMISSION_REQUIRED_ENDPOINTS, RBAC_ADMIN_ONLY_ENTITIES, canManageRoles, createAuthHelpers, createCmsAuthBundle, getRequiredPermission, isOpenEndpoint, isPublicMethod, sessionHasEntityAccess } from './chunk-UXMDSNIG.js';
18
+ import { isSuperAdmin, isVendorGroupName } from './chunk-P7QCLE5W.js';
19
+ export { ADMIN_GROUP_NAME, SUPER_ADMIN_GROUP_ID, VENDOR_ADMIN_GROUP_ID, VENDOR_OWNER_GROUP_NAME, VENDOR_SCOPED_STORE_ENTITIES, VENDOR_STORE_RBAC_ENTITIES, canManageVendorRoles, canManageVendorTeam, canOnboardVendors, explainSessionEntityAccess, getPermissionableEntityKeys, hasEntityPermission, isPlatformAdministrator, isRbacDebugEnabled, isSuperAdmin, isSuperAdminGroupName, isVendorAdmin, isVendorGroupName, isVendorOwner, isVendorPortalUser, isVendorStaff, logEntityAccessDecision, logRbac, permissionRowsToRecord, resolveVendorScopeFromSessionUser, summarizeEntityPerms, vendorPortalFlagsFromUser } from './chunk-P7QCLE5W.js';
22
20
  export { queueErpPaidOrderForOrderId } from './chunk-TDV6EJHV.js';
23
21
  import { erpSafeWebhookUrl, logErp, warnErp, errorErp } from './chunk-SYBOCAWB.js';
24
22
  export { queueErp, registerErpQueueProcessor } from './chunk-SYBOCAWB.js';
@@ -27,8 +25,6 @@ import { __name } from './chunk-SHUYVCID.js';
27
25
  import { google } from 'googleapis';
28
26
  import Redis from 'ioredis';
29
27
  import { LRUCache } from 'lru-cache';
30
- import { Worker, Queue } from 'bullmq';
31
- import fastq from 'fastq';
32
28
  import { clsx } from 'clsx';
33
29
  import { twMerge } from 'tailwind-merge';
34
30
  import { NextResponse } from 'next/server';
@@ -36,40 +32,6 @@ import 'reflect-metadata';
36
32
  import { DataSource } from 'typeorm';
37
33
  import bcrypt from 'bcryptjs';
38
34
 
39
- // src/plugins/registry.ts
40
- var noopLogger = {
41
- info: /* @__PURE__ */ __name(() => {
42
- }, "info"),
43
- warn: /* @__PURE__ */ __name(() => {
44
- }, "warn"),
45
- error: /* @__PURE__ */ __name(() => {
46
- }, "error")
47
- };
48
- async function createCmsApp(options) {
49
- const { dataSource, config = {}, plugins = [], logger = noopLogger } = options;
50
- const context = {
51
- dataSource,
52
- config,
53
- logger
54
- };
55
- const registry = /* @__PURE__ */ new Map();
56
- for (const plugin of plugins) {
57
- try {
58
- const instance = await plugin.init(context);
59
- registry.set(plugin.name, instance !== void 0 ? instance : plugin);
60
- } catch (err) {
61
- logger.warn(`Plugin "${plugin.name}" failed to init: ${err instanceof Error ? err.message : err}`);
62
- }
63
- }
64
- return {
65
- dataSource,
66
- getPlugin(name) {
67
- return registry.get(name);
68
- }
69
- };
70
- }
71
- __name(createCmsApp, "createCmsApp");
72
-
73
35
  // src/plugins/erp/erp-submission.ts
74
36
  var ERPSubmissionService = class {
75
37
  static {
@@ -1039,268 +1001,6 @@ function smsPlugin(config = {}) {
1039
1001
  }
1040
1002
  __name(smsPlugin, "smsPlugin");
1041
1003
 
1042
- // src/plugins/whatsapp/whatsapp-service.ts
1043
- function norm2(v) {
1044
- const t = v?.trim();
1045
- return t || void 0;
1046
- }
1047
- __name(norm2, "norm");
1048
- function pick2(plugin, db, env, ...keys) {
1049
- for (const key of keys) {
1050
- const pk = key;
1051
- if (pk in plugin && plugin[pk] !== void 0 && String(plugin[pk]).trim() !== "") {
1052
- const v = norm2(String(plugin[pk]));
1053
- if (v) return v;
1054
- }
1055
- if (norm2(db[key])) return norm2(db[key]);
1056
- if (norm2(env[key])) return norm2(env[key]);
1057
- }
1058
- return void 0;
1059
- }
1060
- __name(pick2, "pick");
1061
- function mergeWhatsAppConfigLayers(env, db, plugin) {
1062
- return {
1063
- accessToken: pick2(plugin, db, env, "accessToken", "WHATSAPP_ACCESS_TOKEN"),
1064
- phoneNumberId: pick2(plugin, db, env, "phoneNumberId", "WHATSAPP_PHONE_NUMBER_ID"),
1065
- apiVersion: pick2(plugin, db, env, "apiVersion", "WHATSAPP_API_VERSION") ?? "v21.0"
1066
- };
1067
- }
1068
- __name(mergeWhatsAppConfigLayers, "mergeWhatsAppConfigLayers");
1069
- function isWhatsAppPluginEnabled(db) {
1070
- const v = (db.enabled ?? "true").toLowerCase();
1071
- return v !== "false" && v !== "0";
1072
- }
1073
- __name(isWhatsAppPluginEnabled, "isWhatsAppPluginEnabled");
1074
- function whatsAppConfigured(config) {
1075
- return Boolean(config.accessToken?.trim() && config.phoneNumberId?.trim());
1076
- }
1077
- __name(whatsAppConfigured, "whatsAppConfigured");
1078
- function normalizeWhatsAppRecipient(to) {
1079
- const digits = to.replace(/\D/g, "");
1080
- return digits;
1081
- }
1082
- __name(normalizeWhatsAppRecipient, "normalizeWhatsAppRecipient");
1083
- function maskPhone(digits) {
1084
- if (digits.length <= 4) return "****";
1085
- return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
1086
- }
1087
- __name(maskPhone, "maskPhone");
1088
- function logWhatsApp(level, message, extra) {
1089
- const line = extra ? `${message} ${JSON.stringify(extra)}` : message;
1090
- if (level === "info") console.log(`[whatsapp] ${line}`);
1091
- else if (level === "warn") console.warn(`[whatsapp] ${line}`);
1092
- else console.error(`[whatsapp] ${line}`);
1093
- }
1094
- __name(logWhatsApp, "logWhatsApp");
1095
- var WhatsAppService = class {
1096
- static {
1097
- __name(this, "WhatsAppService");
1098
- }
1099
- getWhatsAppSettings;
1100
- config;
1101
- getMessageTemplateRow;
1102
- constructor(env, plugin = {}, getWhatsAppSettings, getMessageTemplateRow) {
1103
- this.getWhatsAppSettings = getWhatsAppSettings;
1104
- this.getMessageTemplateRow = getMessageTemplateRow;
1105
- this.config = mergeWhatsAppConfigLayers(env, {}, plugin);
1106
- }
1107
- async mergedConfig() {
1108
- let db = {};
1109
- try {
1110
- db = await this.getWhatsAppSettings?.() ?? {};
1111
- } catch {
1112
- }
1113
- return mergeWhatsAppConfigLayers((typeof process !== "undefined" ? process.env : {}) ?? {}, db, this.config);
1114
- }
1115
- async send(opts) {
1116
- const cfg = await this.mergedConfig();
1117
- if (!whatsAppConfigured(cfg)) {
1118
- logWhatsApp("warn", "send skipped: not configured (WHATSAPP_ACCESS_TOKEN + WHATSAPP_PHONE_NUMBER_ID)");
1119
- return false;
1120
- }
1121
- const to = normalizeWhatsAppRecipient(opts.to);
1122
- if (!to) {
1123
- logWhatsApp("warn", "send skipped: invalid recipient", {
1124
- rawTo: opts.to
1125
- });
1126
- return false;
1127
- }
1128
- logWhatsApp("info", "send started", {
1129
- to: maskPhone(to),
1130
- templateKey: opts.templateKey ?? null,
1131
- externalTemplateRef: opts.externalTemplateRef ?? null,
1132
- phoneNumberId: cfg.phoneNumberId
1133
- });
1134
- let templateName = opts.externalTemplateRef?.trim();
1135
- let body = opts.body?.trim() ?? "";
1136
- const variables = opts.variables ?? {};
1137
- if (opts.templateKey?.trim() && this.getMessageTemplateRow) {
1138
- const { resolveEventOrderTemplate: resolveEventOrderTemplate2 } = await import('./event-order-defaults-6E543227.js');
1139
- const resolved = await resolveEventOrderTemplate2("whatsapp", async (ch, key) => {
1140
- const row = await this.getMessageTemplateRow(ch, key);
1141
- if (!row) return null;
1142
- return {
1143
- subject: null,
1144
- body: row.body,
1145
- externalTemplateRef: row.externalTemplateRef,
1146
- enabled: row.enabled
1147
- };
1148
- });
1149
- if (resolved && !resolved.enabled) {
1150
- logWhatsApp("warn", "send skipped: template disabled in message_templates", {
1151
- templateKey: opts.templateKey
1152
- });
1153
- return false;
1154
- }
1155
- if (resolved) {
1156
- body = resolved.body;
1157
- if (resolved.externalTemplateRef) templateName = resolved.externalTemplateRef;
1158
- }
1159
- }
1160
- const { applyTemplateVars } = await import('./interpolate-WDUWDF3I.js');
1161
- if (body) body = applyTemplateVars(body, variables);
1162
- if (templateName) {
1163
- const paramValues = opts.templateParamOrder?.length ? opts.templateParamOrder.map((key) => String(variables[key] ?? "").slice(0, 1024)) : Object.keys(variables).length ? Object.values(variables) : body ? [
1164
- body
1165
- ] : [];
1166
- logWhatsApp("info", "sending template message", {
1167
- to: maskPhone(to),
1168
- templateName,
1169
- paramCount: paramValues.length,
1170
- paramOrder: opts.templateParamOrder ?? null
1171
- });
1172
- return this.sendTemplateMessage(cfg, to, templateName, paramValues);
1173
- }
1174
- if (!body) {
1175
- logWhatsApp("warn", "send skipped: no template name and empty body", {
1176
- to: maskPhone(to)
1177
- });
1178
- return false;
1179
- }
1180
- logWhatsApp("info", "sending text message", {
1181
- to: maskPhone(to),
1182
- bodyLength: body.length
1183
- });
1184
- return this.sendTextMessage(cfg, to, body);
1185
- }
1186
- async sendTextMessage(cfg, to, body) {
1187
- const url = `https://graph.facebook.com/${cfg.apiVersion}/${cfg.phoneNumberId}/messages`;
1188
- const res = await fetch(url, {
1189
- method: "POST",
1190
- headers: {
1191
- Authorization: `Bearer ${cfg.accessToken}`,
1192
- "Content-Type": "application/json"
1193
- },
1194
- body: JSON.stringify({
1195
- messaging_product: "whatsapp",
1196
- to,
1197
- type: "text",
1198
- text: {
1199
- body
1200
- }
1201
- })
1202
- });
1203
- if (!res.ok) {
1204
- const errText = await res.text().catch(() => "");
1205
- logWhatsApp("error", "text send failed", {
1206
- status: res.status,
1207
- to: maskPhone(to),
1208
- error: errText
1209
- });
1210
- return false;
1211
- }
1212
- const data = await res.json().catch(() => ({}));
1213
- logWhatsApp("info", "text send succeeded", {
1214
- to: maskPhone(to),
1215
- messageId: data.messages?.[0]?.id ?? null
1216
- });
1217
- return true;
1218
- }
1219
- async sendTemplateMessage(cfg, to, templateName, paramValues) {
1220
- const components = paramValues.length > 0 ? [
1221
- {
1222
- type: "body",
1223
- parameters: paramValues.map((text) => ({
1224
- type: "text",
1225
- text: String(text).slice(0, 1024)
1226
- }))
1227
- }
1228
- ] : void 0;
1229
- const url = `https://graph.facebook.com/${cfg.apiVersion}/${cfg.phoneNumberId}/messages`;
1230
- const payload = {
1231
- messaging_product: "whatsapp",
1232
- to,
1233
- type: "template",
1234
- template: {
1235
- name: templateName,
1236
- language: {
1237
- code: "en"
1238
- },
1239
- ...components ? {
1240
- components
1241
- } : {}
1242
- }
1243
- };
1244
- const res = await fetch(url, {
1245
- method: "POST",
1246
- headers: {
1247
- Authorization: `Bearer ${cfg.accessToken}`,
1248
- "Content-Type": "application/json"
1249
- },
1250
- body: JSON.stringify(payload)
1251
- });
1252
- if (!res.ok) {
1253
- const errText = await res.text().catch(() => "");
1254
- logWhatsApp("error", "template send failed", {
1255
- status: res.status,
1256
- to: maskPhone(to),
1257
- templateName,
1258
- paramCount: paramValues.length,
1259
- error: errText
1260
- });
1261
- return false;
1262
- }
1263
- const data = await res.json().catch(() => ({}));
1264
- logWhatsApp("info", "template send succeeded", {
1265
- to: maskPhone(to),
1266
- templateName,
1267
- messageId: data.messages?.[0]?.id ?? null
1268
- });
1269
- return true;
1270
- }
1271
- };
1272
-
1273
- // src/plugins/whatsapp/index.ts
1274
- function whatsappPlugin(config = {}) {
1275
- return {
1276
- name: "whatsapp",
1277
- version: "1.0.0",
1278
- async init(context) {
1279
- const env = context.config;
1280
- const { getWhatsAppSettings, getMessageTemplateRow, ...staticRest } = config;
1281
- let db = {};
1282
- try {
1283
- db = await getWhatsAppSettings?.() ?? {};
1284
- } catch {
1285
- }
1286
- const merged = mergeWhatsAppConfigLayers(env, db, staticRest);
1287
- if (!isWhatsAppPluginEnabled(db)) {
1288
- context.logger.warn("WhatsApp plugin skipped: disabled in settings");
1289
- return null;
1290
- }
1291
- if (!whatsAppConfigured(merged)) {
1292
- context.logger.warn("WhatsApp plugin skipped: set WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID (Meta Cloud API)");
1293
- return null;
1294
- }
1295
- const svc = new WhatsAppService(env, staticRest, getWhatsAppSettings, getMessageTemplateRow);
1296
- return {
1297
- send: /* @__PURE__ */ __name((opts) => svc.send(opts), "send")
1298
- };
1299
- }
1300
- };
1301
- }
1302
- __name(whatsappPlugin, "whatsappPlugin");
1303
-
1304
1004
  // src/plugins/payment/index.ts
1305
1005
  function paymentPlugin(config) {
1306
1006
  return {
@@ -2148,103 +1848,6 @@ function cachePlugin(config) {
2148
1848
  };
2149
1849
  }
2150
1850
  __name(cachePlugin, "cachePlugin");
2151
- function connectionFromUrl(redisUrl) {
2152
- const u = new URL(redisUrl);
2153
- return {
2154
- host: u.hostname,
2155
- port: u.port ? parseInt(u.port, 10) : 6379,
2156
- ...u.username && {
2157
- username: u.username
2158
- },
2159
- ...u.password && {
2160
- password: u.password
2161
- },
2162
- maxRetriesPerRequest: null
2163
- };
2164
- }
2165
- __name(connectionFromUrl, "connectionFromUrl");
2166
- var BullMQQueue = class {
2167
- static {
2168
- __name(this, "BullMQQueue");
2169
- }
2170
- connectionOptions;
2171
- queues = /* @__PURE__ */ new Map();
2172
- workers = /* @__PURE__ */ new Map();
2173
- constructor(redisUrl) {
2174
- this.connectionOptions = connectionFromUrl(redisUrl);
2175
- }
2176
- async add(queueName, data) {
2177
- const queue = this.getOrCreateQueue(queueName);
2178
- await queue.add(queueName, data);
2179
- }
2180
- registerProcessor(queueName, processor) {
2181
- if (this.workers.has(queueName)) return;
2182
- const worker = new Worker(queueName, async (job) => {
2183
- await processor(job.data);
2184
- }, {
2185
- connection: this.connectionOptions
2186
- });
2187
- this.workers.set(queueName, worker);
2188
- }
2189
- getOrCreateQueue(queueName) {
2190
- let queue = this.queues.get(queueName);
2191
- if (!queue) {
2192
- queue = new Queue(queueName, {
2193
- connection: this.connectionOptions
2194
- });
2195
- this.queues.set(queueName, queue);
2196
- }
2197
- return queue;
2198
- }
2199
- async destroy() {
2200
- for (const w of this.workers.values()) await w.close();
2201
- this.workers.clear();
2202
- for (const q of this.queues.values()) await q.close();
2203
- this.queues.clear();
2204
- }
2205
- };
2206
- var MemoryQueue = class {
2207
- static {
2208
- __name(this, "MemoryQueue");
2209
- }
2210
- queues = /* @__PURE__ */ new Map();
2211
- async add(queueName, data) {
2212
- const q = this.queues.get(queueName);
2213
- if (!q) throw new Error(`No processor registered for queue: ${queueName}`);
2214
- void Promise.resolve(q.push(data)).catch(() => void 0);
2215
- }
2216
- registerProcessor(queueName, processor) {
2217
- if (this.queues.has(queueName)) return;
2218
- const worker = /* @__PURE__ */ __name(async (task) => processor(task), "worker");
2219
- const q = fastq.promise(worker, 1);
2220
- this.queues.set(queueName, q);
2221
- }
2222
- };
2223
-
2224
- // src/plugins/queue/index.ts
2225
- function queuePlugin(config) {
2226
- let instance;
2227
- return {
2228
- name: "queue",
2229
- version: "1.0.0",
2230
- async init(context) {
2231
- const redisUrl = config?.redisUrl ?? context.config.REDIS_URL ?? "";
2232
- const url = typeof redisUrl === "string" ? redisUrl.trim() : "";
2233
- if (url) {
2234
- instance = new BullMQQueue(url);
2235
- return instance;
2236
- }
2237
- instance = new MemoryQueue();
2238
- return instance;
2239
- },
2240
- async destroy() {
2241
- if (instance && "destroy" in instance && typeof instance.destroy === "function") {
2242
- await instance.destroy();
2243
- }
2244
- }
2245
- };
2246
- }
2247
- __name(queuePlugin, "queuePlugin");
2248
1851
 
2249
1852
  // src/plugins/pg-boss/index.ts
2250
1853
  function pgBossPlugin(config = {}) {
@@ -2271,310 +1874,46 @@ function pgBossPlugin(config = {}) {
2271
1874
  };
2272
1875
  }
2273
1876
  __name(pgBossPlugin, "pgBossPlugin");
2274
-
2275
- // src/plugins/captcha/captcha-service.ts
2276
- function resolveKeys(config) {
2277
- const turnstile = config.TURNSTILE_SITE_KEY?.trim() && config.TURNSTILE_SECRET_KEY?.trim() ? {
2278
- site: config.TURNSTILE_SITE_KEY.trim(),
2279
- secret: config.TURNSTILE_SECRET_KEY.trim()
2280
- } : null;
2281
- const recaptcha = config.RECAPTCHA_SITE_KEY?.trim() && config.RECAPTCHA_SECRET_KEY?.trim() ? {
2282
- site: config.RECAPTCHA_SITE_KEY.trim(),
2283
- secret: config.RECAPTCHA_SECRET_KEY.trim()
2284
- } : null;
2285
- const raw = config.CAPTCHA_PROVIDER?.trim().toLowerCase();
2286
- let envDefaultProvider = null;
2287
- if (raw === "turnstile") envDefaultProvider = "turnstile";
2288
- else if (raw === "recaptcha_v3" || raw === "recaptcha") envDefaultProvider = "recaptcha_v3";
2289
- const minScore = parseFloat(config.RECAPTCHA_MIN_SCORE ?? "0.5");
2290
- return {
2291
- turnstile,
2292
- recaptcha,
2293
- envDefaultProvider,
2294
- minScore: Number.isFinite(minScore) ? minScore : 0.5
2295
- };
1877
+ function cn(...inputs) {
1878
+ return twMerge(clsx(inputs));
2296
1879
  }
2297
- __name(resolveKeys, "resolveKeys");
2298
- function buildCaptchaPublicConfig(config) {
2299
- const r = resolveKeys(config);
2300
- const available = [];
2301
- if (r.turnstile) available.push({
2302
- id: "turnstile",
2303
- siteKey: r.turnstile.site
2304
- });
2305
- if (r.recaptcha) available.push({
2306
- id: "recaptcha_v3",
2307
- siteKey: r.recaptcha.site
1880
+ __name(cn, "cn");
1881
+ function generateSlug(title) {
1882
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
1883
+ }
1884
+ __name(generateSlug, "generateSlug");
1885
+ function validateSlug(slug) {
1886
+ const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1887
+ return slugRegex.test(slug) && slug.length >= 3 && slug.length <= 50;
1888
+ }
1889
+ __name(validateSlug, "validateSlug");
1890
+ function formatDate(date) {
1891
+ const d = new Date(date);
1892
+ return d.toLocaleDateString("en-US", {
1893
+ year: "numeric",
1894
+ month: "long",
1895
+ day: "numeric"
2308
1896
  });
2309
- const enabled = available.length > 0;
2310
- const multipleProviders = available.length > 1;
2311
- let activeProvider = null;
2312
- if (r.turnstile && !r.recaptcha) activeProvider = "turnstile";
2313
- else if (r.recaptcha && !r.turnstile) activeProvider = "recaptcha_v3";
2314
- else if (multipleProviders) {
2315
- activeProvider = r.envDefaultProvider ?? available[0].id;
2316
- }
2317
- let siteKey = null;
2318
- if (activeProvider === "turnstile" && r.turnstile) siteKey = r.turnstile.site;
2319
- else if (activeProvider === "recaptcha_v3" && r.recaptcha) siteKey = r.recaptcha.site;
2320
- if (!siteKey && available.length === 1) siteKey = available[0].siteKey;
2321
- return {
2322
- enabled,
2323
- activeProvider,
2324
- siteKey,
2325
- availableProviders: available,
2326
- multipleProviders
2327
- };
2328
1897
  }
2329
- __name(buildCaptchaPublicConfig, "buildCaptchaPublicConfig");
2330
- function clientIp(req) {
2331
- const cf = req.headers.get("cf-connecting-ip")?.trim();
2332
- if (cf) return cf;
2333
- const xff = req.headers.get("x-forwarded-for")?.trim();
2334
- if (xff) return xff.split(",")[0].trim();
2335
- const real = req.headers.get("x-real-ip")?.trim();
2336
- if (real) return real;
2337
- return void 0;
1898
+ __name(formatDate, "formatDate");
1899
+ function formatDateTime(date) {
1900
+ const d = new Date(date);
1901
+ return d.toLocaleDateString("en-US", {
1902
+ year: "numeric",
1903
+ month: "short",
1904
+ day: "numeric",
1905
+ hour: "2-digit",
1906
+ minute: "2-digit"
1907
+ });
2338
1908
  }
2339
- __name(clientIp, "clientIp");
2340
- function normalizeBodyProvider(v) {
2341
- if (v === "turnstile") return "turnstile";
2342
- if (v === "recaptcha_v3" || v === "recaptcha") return "recaptcha_v3";
2343
- return null;
2344
- }
2345
- __name(normalizeBodyProvider, "normalizeBodyProvider");
2346
- function pickVerifyProvider(body, r) {
2347
- const bodyP = normalizeBodyProvider(body.captchaProvider);
2348
- const hasT = !!r.turnstile;
2349
- const hasR = !!r.recaptcha;
2350
- if (hasT && !hasR) {
2351
- if (bodyP && bodyP !== "turnstile") return {
2352
- error: "Invalid captchaProvider"
2353
- };
2354
- return {
2355
- provider: "turnstile",
2356
- secret: r.turnstile.secret
2357
- };
2358
- }
2359
- if (hasR && !hasT) {
2360
- if (bodyP && bodyP !== "recaptcha_v3") return {
2361
- error: "Invalid captchaProvider"
2362
- };
2363
- return {
2364
- provider: "recaptcha_v3",
2365
- secret: r.recaptcha.secret
2366
- };
2367
- }
2368
- if (!hasT && !hasR) return {
2369
- error: "Captcha not configured"
2370
- };
2371
- if (bodyP === "turnstile" && hasT) return {
2372
- provider: "turnstile",
2373
- secret: r.turnstile.secret
2374
- };
2375
- if (bodyP === "recaptcha_v3" && hasR) return {
2376
- provider: "recaptcha_v3",
2377
- secret: r.recaptcha.secret
2378
- };
2379
- if (r.envDefaultProvider === "turnstile" && hasT) return {
2380
- provider: "turnstile",
2381
- secret: r.turnstile.secret
2382
- };
2383
- if (r.envDefaultProvider === "recaptcha_v3" && hasR) return {
2384
- provider: "recaptcha_v3",
2385
- secret: r.recaptcha.secret
2386
- };
2387
- return {
2388
- error: "captchaProvider required (turnstile or recaptcha_v3)"
2389
- };
2390
- }
2391
- __name(pickVerifyProvider, "pickVerifyProvider");
2392
- async function verifyTurnstile(secret, token, remoteip) {
2393
- const body = new URLSearchParams({
2394
- secret,
2395
- response: token
2396
- });
2397
- if (remoteip) body.set("remoteip", remoteip);
2398
- const res = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
2399
- method: "POST",
2400
- headers: {
2401
- "Content-Type": "application/x-www-form-urlencoded"
2402
- },
2403
- body: body.toString()
2404
- });
2405
- if (!res.ok) return false;
2406
- const data = await res.json();
2407
- return data.success === true;
2408
- }
2409
- __name(verifyTurnstile, "verifyTurnstile");
2410
- async function verifyRecaptcha(secret, token, remoteip, minScore) {
2411
- const body = new URLSearchParams({
2412
- secret,
2413
- response: token
2414
- });
2415
- if (remoteip) body.set("remoteip", remoteip);
2416
- const res = await fetch("https://www.google.com/recaptcha/api/siteverify", {
2417
- method: "POST",
2418
- headers: {
2419
- "Content-Type": "application/x-www-form-urlencoded"
2420
- },
2421
- body: body.toString()
2422
- });
2423
- if (!res.ok) return {
2424
- ok: false
2425
- };
2426
- const data = await res.json();
2427
- if (data.success !== true) return {
2428
- ok: false
2429
- };
2430
- const score = typeof data.score === "number" ? data.score : 0;
2431
- return {
2432
- ok: score >= minScore,
2433
- score
2434
- };
2435
- }
2436
- __name(verifyRecaptcha, "verifyRecaptcha");
2437
- var CaptchaService = class {
2438
- static {
2439
- __name(this, "CaptchaService");
2440
- }
2441
- env;
2442
- constructor(env) {
2443
- this.env = env;
2444
- }
2445
- isEnforced() {
2446
- return buildCaptchaPublicConfig(this.env).enabled;
2447
- }
2448
- getPublicConfig() {
2449
- return buildCaptchaPublicConfig(this.env);
2450
- }
2451
- async verify(body, req) {
2452
- if (!this.isEnforced()) return {
2453
- ok: true
2454
- };
2455
- const token = typeof body.captchaToken === "string" ? body.captchaToken.trim() : "";
2456
- if (!token) return {
2457
- ok: false,
2458
- status: 400,
2459
- message: "captchaToken required"
2460
- };
2461
- const r = resolveKeys(this.env);
2462
- const picked = pickVerifyProvider(body, r);
2463
- if ("error" in picked) return {
2464
- ok: false,
2465
- status: 400,
2466
- message: picked.error
2467
- };
2468
- const ip = clientIp(req);
2469
- if (picked.provider === "turnstile") {
2470
- const ok = await verifyTurnstile(picked.secret, token, ip);
2471
- if (!ok) return {
2472
- ok: false,
2473
- status: 400,
2474
- message: "Captcha verification failed"
2475
- };
2476
- return {
2477
- ok: true
2478
- };
2479
- }
2480
- const vr = await verifyRecaptcha(picked.secret, token, ip, r.minScore);
2481
- if (!vr.ok) return {
2482
- ok: false,
2483
- status: 400,
2484
- message: "Captcha verification failed or score too low"
2485
- };
2486
- return {
2487
- ok: true
2488
- };
2489
- }
2490
- };
2491
-
2492
- // src/plugins/captcha/captcha-plugin.ts
2493
- function captchaPlugin() {
2494
- return {
2495
- name: "captcha",
2496
- version: "1.0.0",
2497
- async init(ctx) {
2498
- const s = new CaptchaService(ctx.config);
2499
- return s.isEnforced() ? s : void 0;
2500
- }
2501
- };
2502
- }
2503
- __name(captchaPlugin, "captchaPlugin");
2504
-
2505
- // src/plugins/blog-generator/index.ts
2506
- function blogGeneratorPlugin(config = {}) {
2507
- return {
2508
- name: "blog_generator",
2509
- version: "1.0.0",
2510
- async init(context) {
2511
- if (config.enabled === false) {
2512
- context.logger.warn("Blog Generator plugin skipped: disabled in config");
2513
- return void 0;
2514
- }
2515
- return new BlogGeneratorService();
2516
- }
2517
- };
2518
- }
2519
- __name(blogGeneratorPlugin, "blogGeneratorPlugin");
2520
-
2521
- // src/plugins/social-media/index.ts
2522
- function socialMediaPlugin(config = {}) {
2523
- return {
2524
- name: "social_media",
2525
- version: "1.0.0",
2526
- async init(context) {
2527
- if (config.enabled === false) {
2528
- context.logger.warn("Social media plugin skipped: disabled in config");
2529
- return void 0;
2530
- }
2531
- return {
2532
- name: "social_media"
2533
- };
2534
- }
2535
- };
2536
- }
2537
- __name(socialMediaPlugin, "socialMediaPlugin");
2538
- function cn(...inputs) {
2539
- return twMerge(clsx(inputs));
2540
- }
2541
- __name(cn, "cn");
2542
- function generateSlug(title) {
2543
- return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
2544
- }
2545
- __name(generateSlug, "generateSlug");
2546
- function validateSlug(slug) {
2547
- const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2548
- return slugRegex.test(slug) && slug.length >= 3 && slug.length <= 50;
2549
- }
2550
- __name(validateSlug, "validateSlug");
2551
- function formatDate(date) {
2552
- const d = new Date(date);
2553
- return d.toLocaleDateString("en-US", {
2554
- year: "numeric",
2555
- month: "long",
2556
- day: "numeric"
2557
- });
2558
- }
2559
- __name(formatDate, "formatDate");
2560
- function formatDateTime(date) {
2561
- const d = new Date(date);
2562
- return d.toLocaleDateString("en-US", {
2563
- year: "numeric",
2564
- month: "short",
2565
- day: "numeric",
2566
- hour: "2-digit",
2567
- minute: "2-digit"
2568
- });
2569
- }
2570
- __name(formatDateTime, "formatDateTime");
2571
- function formatDateOnly(date) {
2572
- const d = new Date(date);
2573
- return d.toLocaleDateString("en-US", {
2574
- year: "numeric",
2575
- month: "long",
2576
- day: "numeric"
2577
- });
1909
+ __name(formatDateTime, "formatDateTime");
1910
+ function formatDateOnly(date) {
1911
+ const d = new Date(date);
1912
+ return d.toLocaleDateString("en-US", {
1913
+ year: "numeric",
1914
+ month: "long",
1915
+ day: "numeric"
1916
+ });
2578
1917
  }
2579
1918
  __name(formatDateOnly, "formatDateOnly");
2580
1919
  function truncateText(text, maxLength) {
@@ -2713,14 +2052,14 @@ async function resolvePublicMetadata(args) {
2713
2052
  return out;
2714
2053
  }
2715
2054
  __name(resolvePublicMetadata, "resolvePublicMetadata");
2716
- function clientIp2(req) {
2055
+ function clientIp(req) {
2717
2056
  const cf = req.headers.get("cf-connecting-ip")?.trim();
2718
2057
  if (cf) return cf;
2719
2058
  const xff = req.headers.get("x-forwarded-for")?.trim();
2720
2059
  if (xff) return xff.split(",")[0].trim();
2721
2060
  return req.headers.get("x-real-ip")?.trim() || "unknown";
2722
2061
  }
2723
- __name(clientIp2, "clientIp");
2062
+ __name(clientIp, "clientIp");
2724
2063
  var WINDOW_MS = Math.max(1e3, parseInt(process.env.RATE_LIMIT_WINDOW_MS ?? "60000", 10) || 6e4);
2725
2064
  var MAX_REQ = Math.max(1, parseInt(process.env.RATE_LIMIT_MAX ?? "120", 10) || 120);
2726
2065
  var redisClient;
@@ -2808,7 +2147,7 @@ function shouldRateLimitPublicWrite(method, segments, extraRule) {
2808
2147
  }
2809
2148
  __name(shouldRateLimitPublicWrite, "shouldRateLimitPublicWrite");
2810
2149
  function rateLimitKeyForApiRequest(req, segments) {
2811
- return `${clientIp2(req)}:${segments.join("/")}`;
2150
+ return `${clientIp(req)}:${segments.join("/")}`;
2812
2151
  }
2813
2152
  __name(rateLimitKeyForApiRequest, "rateLimitKeyForApiRequest");
2814
2153
  async function rateLimitPublicApiIfNeeded(req, method, segments, options) {
@@ -2825,7 +2164,7 @@ async function rateLimitPublicApiIfNeeded(req, method, segments, options) {
2825
2164
  __name(rateLimitPublicApiIfNeeded, "rateLimitPublicApiIfNeeded");
2826
2165
  var CHECKOUT_PREFIX = "checkout:";
2827
2166
  async function rateLimitCheckoutPost(req) {
2828
- const key = `${CHECKOUT_PREFIX}${clientIp2(req)}`;
2167
+ const key = `${CHECKOUT_PREFIX}${clientIp(req)}`;
2829
2168
  const ok = await allowRateLimit(key);
2830
2169
  if (ok) return null;
2831
2170
  return NextResponse.json({
@@ -2935,6 +2274,36 @@ async function sendOrderPlacedEmailsAfterConfirmation(orderId, deps) {
2935
2274
  }
2936
2275
  }
2937
2276
  __name(sendOrderPlacedEmailsAfterConfirmation, "sendOrderPlacedEmailsAfterConfirmation");
2277
+
2278
+ // src/lib/order-notification-trigger-seeds.ts
2279
+ var DEFAULT_ORDER_NOTIFICATION_TRIGGER_SEEDS = [
2280
+ {
2281
+ triggerKey: "order_placed",
2282
+ label: "Order placed",
2283
+ description: "Fires when a new order is created (storefront checkout or admin).",
2284
+ category: "order",
2285
+ sortOrder: 1
2286
+ },
2287
+ {
2288
+ triggerKey: "order_cancelled",
2289
+ label: "Order cancelled",
2290
+ description: "Fires when an order is cancelled (includes refund amount when applicable).",
2291
+ category: "order",
2292
+ sortOrder: 2
2293
+ }
2294
+ ];
2295
+
2296
+ // src/lib/order-notification-triggers.ts
2297
+ var ORDER_NOTIFICATION_TRIGGERS = [
2298
+ {
2299
+ key: "order_placed",
2300
+ label: "Order placed"
2301
+ },
2302
+ {
2303
+ key: "order_cancelled",
2304
+ label: "Order cancelled"
2305
+ }
2306
+ ];
2938
2307
  function createDataSourceManager(entityMap, options) {
2939
2308
  const entities = Object.values(entityMap);
2940
2309
  const databaseUrl = options?.databaseUrl ?? process.env.DATABASE_URL;
@@ -3043,16 +2412,44 @@ __name(seedDefaultAdmin, "seedDefaultAdmin");
3043
2412
  // src/lib/enrich-user-vendor-context.ts
3044
2413
  async function enrichUserWithVendorContext(dataSource, user) {
3045
2414
  if (!user) return null;
2415
+ const multiVendorEnabled = await checkMultiVendorEnabled(dataSource);
3046
2416
  const ctx = await loadUserVendorContext(dataSource, user.id);
2417
+ const isRBACAdmin = isSuperAdmin({
2418
+ groupId: user.groupId ?? void 0,
2419
+ groupName: user.group?.name,
2420
+ isRBACAdmin: false
2421
+ });
2422
+ const isVendorOnlyUser = !isRBACAdmin && (ctx.vendorIds.length > 0 || isVendorGroupName(user.group?.name));
2423
+ if (!multiVendorEnabled && isVendorOnlyUser) {
2424
+ console.log("[enrich-vendor]", {
2425
+ email: user.email,
2426
+ multiVendorEnabled,
2427
+ vendorIdsFromCtx: ctx.vendorIds,
2428
+ groupName: user.group?.name,
2429
+ isRBACAdmin,
2430
+ adminAccess: user.adminAccess,
2431
+ isVendorOnlyUser
2432
+ });
2433
+ return null;
2434
+ }
2435
+ const effectiveCtx = multiVendorEnabled ? ctx : {
2436
+ vendorIds: [],
2437
+ activeVendorId: null,
2438
+ vendorRole: null,
2439
+ vendorRoleId: null,
2440
+ vendorRoleName: null,
2441
+ isVendorRoleOwner: false,
2442
+ vendorEntityPerms: {}
2443
+ };
3047
2444
  return {
3048
2445
  ...user,
3049
- vendorIds: ctx.vendorIds,
3050
- activeVendorId: ctx.activeVendorId,
3051
- vendorRole: ctx.vendorRole,
3052
- vendorRoleId: ctx.vendorRoleId,
3053
- vendorRoleName: ctx.vendorRoleName,
3054
- isVendorRoleOwner: ctx.isVendorRoleOwner,
3055
- vendorEntityPerms: ctx.vendorEntityPerms
2446
+ vendorIds: effectiveCtx.vendorIds,
2447
+ activeVendorId: effectiveCtx.activeVendorId,
2448
+ vendorRole: effectiveCtx.vendorRole,
2449
+ vendorRoleId: effectiveCtx.vendorRoleId,
2450
+ vendorRoleName: effectiveCtx.vendorRoleName,
2451
+ isVendorRoleOwner: effectiveCtx.isVendorRoleOwner,
2452
+ vendorEntityPerms: effectiveCtx.vendorEntityPerms
3056
2453
  };
3057
2454
  }
3058
2455
  __name(enrichUserWithVendorContext, "enrichUserWithVendorContext");
@@ -3131,6 +2528,270 @@ async function withAdminRlsContext(dataSource, fn) {
3131
2528
  }
3132
2529
  __name(withAdminRlsContext, "withAdminRlsContext");
3133
2530
 
2531
+ // src/plugins/captcha/captcha-service.ts
2532
+ function resolveKeys(config) {
2533
+ const turnstile = config.TURNSTILE_SITE_KEY?.trim() && config.TURNSTILE_SECRET_KEY?.trim() ? {
2534
+ site: config.TURNSTILE_SITE_KEY.trim(),
2535
+ secret: config.TURNSTILE_SECRET_KEY.trim()
2536
+ } : null;
2537
+ const recaptcha = config.RECAPTCHA_SITE_KEY?.trim() && config.RECAPTCHA_SECRET_KEY?.trim() ? {
2538
+ site: config.RECAPTCHA_SITE_KEY.trim(),
2539
+ secret: config.RECAPTCHA_SECRET_KEY.trim()
2540
+ } : null;
2541
+ const raw = config.CAPTCHA_PROVIDER?.trim().toLowerCase();
2542
+ let envDefaultProvider = null;
2543
+ if (raw === "turnstile") envDefaultProvider = "turnstile";
2544
+ else if (raw === "recaptcha_v3" || raw === "recaptcha") envDefaultProvider = "recaptcha_v3";
2545
+ const minScore = parseFloat(config.RECAPTCHA_MIN_SCORE ?? "0.5");
2546
+ return {
2547
+ turnstile,
2548
+ recaptcha,
2549
+ envDefaultProvider,
2550
+ minScore: Number.isFinite(minScore) ? minScore : 0.5
2551
+ };
2552
+ }
2553
+ __name(resolveKeys, "resolveKeys");
2554
+ function buildCaptchaPublicConfig(config) {
2555
+ const r = resolveKeys(config);
2556
+ const available = [];
2557
+ if (r.turnstile) available.push({
2558
+ id: "turnstile",
2559
+ siteKey: r.turnstile.site
2560
+ });
2561
+ if (r.recaptcha) available.push({
2562
+ id: "recaptcha_v3",
2563
+ siteKey: r.recaptcha.site
2564
+ });
2565
+ const enabled = available.length > 0;
2566
+ const multipleProviders = available.length > 1;
2567
+ let activeProvider = null;
2568
+ if (r.turnstile && !r.recaptcha) activeProvider = "turnstile";
2569
+ else if (r.recaptcha && !r.turnstile) activeProvider = "recaptcha_v3";
2570
+ else if (multipleProviders) {
2571
+ activeProvider = r.envDefaultProvider ?? available[0].id;
2572
+ }
2573
+ let siteKey = null;
2574
+ if (activeProvider === "turnstile" && r.turnstile) siteKey = r.turnstile.site;
2575
+ else if (activeProvider === "recaptcha_v3" && r.recaptcha) siteKey = r.recaptcha.site;
2576
+ if (!siteKey && available.length === 1) siteKey = available[0].siteKey;
2577
+ return {
2578
+ enabled,
2579
+ activeProvider,
2580
+ siteKey,
2581
+ availableProviders: available,
2582
+ multipleProviders
2583
+ };
2584
+ }
2585
+ __name(buildCaptchaPublicConfig, "buildCaptchaPublicConfig");
2586
+ function clientIp2(req) {
2587
+ const cf = req.headers.get("cf-connecting-ip")?.trim();
2588
+ if (cf) return cf;
2589
+ const xff = req.headers.get("x-forwarded-for")?.trim();
2590
+ if (xff) return xff.split(",")[0].trim();
2591
+ const real = req.headers.get("x-real-ip")?.trim();
2592
+ if (real) return real;
2593
+ return void 0;
2594
+ }
2595
+ __name(clientIp2, "clientIp");
2596
+ function normalizeBodyProvider(v) {
2597
+ if (v === "turnstile") return "turnstile";
2598
+ if (v === "recaptcha_v3" || v === "recaptcha") return "recaptcha_v3";
2599
+ return null;
2600
+ }
2601
+ __name(normalizeBodyProvider, "normalizeBodyProvider");
2602
+ function pickVerifyProvider(body, r) {
2603
+ const bodyP = normalizeBodyProvider(body.captchaProvider);
2604
+ const hasT = !!r.turnstile;
2605
+ const hasR = !!r.recaptcha;
2606
+ if (hasT && !hasR) {
2607
+ if (bodyP && bodyP !== "turnstile") return {
2608
+ error: "Invalid captchaProvider"
2609
+ };
2610
+ return {
2611
+ provider: "turnstile",
2612
+ secret: r.turnstile.secret
2613
+ };
2614
+ }
2615
+ if (hasR && !hasT) {
2616
+ if (bodyP && bodyP !== "recaptcha_v3") return {
2617
+ error: "Invalid captchaProvider"
2618
+ };
2619
+ return {
2620
+ provider: "recaptcha_v3",
2621
+ secret: r.recaptcha.secret
2622
+ };
2623
+ }
2624
+ if (!hasT && !hasR) return {
2625
+ error: "Captcha not configured"
2626
+ };
2627
+ if (bodyP === "turnstile" && hasT) return {
2628
+ provider: "turnstile",
2629
+ secret: r.turnstile.secret
2630
+ };
2631
+ if (bodyP === "recaptcha_v3" && hasR) return {
2632
+ provider: "recaptcha_v3",
2633
+ secret: r.recaptcha.secret
2634
+ };
2635
+ if (r.envDefaultProvider === "turnstile" && hasT) return {
2636
+ provider: "turnstile",
2637
+ secret: r.turnstile.secret
2638
+ };
2639
+ if (r.envDefaultProvider === "recaptcha_v3" && hasR) return {
2640
+ provider: "recaptcha_v3",
2641
+ secret: r.recaptcha.secret
2642
+ };
2643
+ return {
2644
+ error: "captchaProvider required (turnstile or recaptcha_v3)"
2645
+ };
2646
+ }
2647
+ __name(pickVerifyProvider, "pickVerifyProvider");
2648
+ async function verifyTurnstile(secret, token, remoteip) {
2649
+ const body = new URLSearchParams({
2650
+ secret,
2651
+ response: token
2652
+ });
2653
+ if (remoteip) body.set("remoteip", remoteip);
2654
+ const res = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
2655
+ method: "POST",
2656
+ headers: {
2657
+ "Content-Type": "application/x-www-form-urlencoded"
2658
+ },
2659
+ body: body.toString()
2660
+ });
2661
+ if (!res.ok) return false;
2662
+ const data = await res.json();
2663
+ return data.success === true;
2664
+ }
2665
+ __name(verifyTurnstile, "verifyTurnstile");
2666
+ async function verifyRecaptcha(secret, token, remoteip, minScore) {
2667
+ const body = new URLSearchParams({
2668
+ secret,
2669
+ response: token
2670
+ });
2671
+ if (remoteip) body.set("remoteip", remoteip);
2672
+ const res = await fetch("https://www.google.com/recaptcha/api/siteverify", {
2673
+ method: "POST",
2674
+ headers: {
2675
+ "Content-Type": "application/x-www-form-urlencoded"
2676
+ },
2677
+ body: body.toString()
2678
+ });
2679
+ if (!res.ok) return {
2680
+ ok: false
2681
+ };
2682
+ const data = await res.json();
2683
+ if (data.success !== true) return {
2684
+ ok: false
2685
+ };
2686
+ const score = typeof data.score === "number" ? data.score : 0;
2687
+ return {
2688
+ ok: score >= minScore,
2689
+ score
2690
+ };
2691
+ }
2692
+ __name(verifyRecaptcha, "verifyRecaptcha");
2693
+ var CaptchaService = class {
2694
+ static {
2695
+ __name(this, "CaptchaService");
2696
+ }
2697
+ env;
2698
+ constructor(env) {
2699
+ this.env = env;
2700
+ }
2701
+ isEnforced() {
2702
+ return buildCaptchaPublicConfig(this.env).enabled;
2703
+ }
2704
+ getPublicConfig() {
2705
+ return buildCaptchaPublicConfig(this.env);
2706
+ }
2707
+ async verify(body, req) {
2708
+ if (!this.isEnforced()) return {
2709
+ ok: true
2710
+ };
2711
+ const token = typeof body.captchaToken === "string" ? body.captchaToken.trim() : "";
2712
+ if (!token) return {
2713
+ ok: false,
2714
+ status: 400,
2715
+ message: "captchaToken required"
2716
+ };
2717
+ const r = resolveKeys(this.env);
2718
+ const picked = pickVerifyProvider(body, r);
2719
+ if ("error" in picked) return {
2720
+ ok: false,
2721
+ status: 400,
2722
+ message: picked.error
2723
+ };
2724
+ const ip = clientIp2(req);
2725
+ if (picked.provider === "turnstile") {
2726
+ const ok = await verifyTurnstile(picked.secret, token, ip);
2727
+ if (!ok) return {
2728
+ ok: false,
2729
+ status: 400,
2730
+ message: "Captcha verification failed"
2731
+ };
2732
+ return {
2733
+ ok: true
2734
+ };
2735
+ }
2736
+ const vr = await verifyRecaptcha(picked.secret, token, ip, r.minScore);
2737
+ if (!vr.ok) return {
2738
+ ok: false,
2739
+ status: 400,
2740
+ message: "Captcha verification failed or score too low"
2741
+ };
2742
+ return {
2743
+ ok: true
2744
+ };
2745
+ }
2746
+ };
2747
+
2748
+ // src/plugins/captcha/captcha-plugin.ts
2749
+ function captchaPlugin() {
2750
+ return {
2751
+ name: "captcha",
2752
+ version: "1.0.0",
2753
+ async init(ctx) {
2754
+ const s = new CaptchaService(ctx.config);
2755
+ return s.isEnforced() ? s : void 0;
2756
+ }
2757
+ };
2758
+ }
2759
+ __name(captchaPlugin, "captchaPlugin");
2760
+
2761
+ // src/plugins/blog-generator/index.ts
2762
+ function blogGeneratorPlugin(config = {}) {
2763
+ return {
2764
+ name: "blog_generator",
2765
+ version: "1.0.0",
2766
+ async init(context) {
2767
+ if (config.enabled === false) {
2768
+ context.logger.warn("Blog Generator plugin skipped: disabled in config");
2769
+ return void 0;
2770
+ }
2771
+ return new BlogGeneratorService();
2772
+ }
2773
+ };
2774
+ }
2775
+ __name(blogGeneratorPlugin, "blogGeneratorPlugin");
2776
+
2777
+ // src/plugins/social-media/index.ts
2778
+ function socialMediaPlugin(config = {}) {
2779
+ return {
2780
+ name: "social_media",
2781
+ version: "1.0.0",
2782
+ async init(context) {
2783
+ if (config.enabled === false) {
2784
+ context.logger.warn("Social media plugin skipped: disabled in config");
2785
+ return void 0;
2786
+ }
2787
+ return {
2788
+ name: "social_media"
2789
+ };
2790
+ }
2791
+ };
2792
+ }
2793
+ __name(socialMediaPlugin, "socialMediaPlugin");
2794
+
3134
2795
  // src/admin/config.ts
3135
2796
  var DEFAULT_ADMIN_NAV = [
3136
2797
  {
@@ -3172,6 +2833,6 @@ var DEFAULT_ADMIN_NAV = [
3172
2833
  ];
3173
2834
 
3174
2835
  // src/index.ts
3175
- console.log("\u{1F525} USING\u{1F525}");
2836
+ console.log("\u{1F525}USING LOCAL CMS CORE \u{1F525}");
3176
2837
 
3177
- export { CaptchaService, DEFAULT_ADMIN_NAV, ERPSubmissionService, LlmService, allowRateLimit, analyticsPlugin, blogGeneratorPlugin, buildCaptchaPublicConfig, cachePlugin, captchaPlugin, cn, createCmsApp, createDataSourceManager, enrichUserWithVendorContext, erpPlugin, fetchSeoBySlug, formatDate, formatDateOnly, formatDateTime, generateSlug, llmPlugin, loadPublicThemeSettings, localStoragePlugin, mergeSeoBySlug, parseHfInferenceEmbeddingBody, paymentPlugin, pgBossPlugin, queuePlugin, rateLimitCheckoutPost, rateLimitKeyForApiRequest, rateLimitPublicApiIfNeeded, resolvePublicMetadata, s3StoragePlugin, seedDefaultAdmin, sendOrderPlacedEmailsAfterConfirmation, shouldRateLimitPublicWrite, smsPlugin, socialMediaPlugin, truncateText, validateSlug, whatsappPlugin, withAdminRlsContext, withVendorRlsContext };
2838
+ export { CaptchaService, DEFAULT_ADMIN_NAV, DEFAULT_ORDER_NOTIFICATION_TRIGGER_SEEDS, ERPSubmissionService, LlmService, ORDER_NOTIFICATION_TRIGGERS, allowRateLimit, analyticsPlugin, blogGeneratorPlugin, buildCaptchaPublicConfig, cachePlugin, captchaPlugin, cn, createDataSourceManager, enrichUserWithVendorContext, erpPlugin, fetchSeoBySlug, formatDate, formatDateOnly, formatDateTime, generateSlug, llmPlugin, loadPublicThemeSettings, localStoragePlugin, mergeSeoBySlug, parseHfInferenceEmbeddingBody, paymentPlugin, pgBossPlugin, rateLimitCheckoutPost, rateLimitKeyForApiRequest, rateLimitPublicApiIfNeeded, resolvePublicMetadata, s3StoragePlugin, seedDefaultAdmin, sendOrderPlacedEmailsAfterConfirmation, shouldRateLimitPublicWrite, smsPlugin, socialMediaPlugin, truncateText, validateSlug, withAdminRlsContext, withVendorRlsContext };