@infuro/cms-core 1.0.47 → 1.0.50

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 (34) hide show
  1. package/README.md +30 -16
  2. package/dist/admin.cjs +1172 -38
  3. package/dist/admin.js +1173 -39
  4. package/dist/api.cjs +37 -37
  5. package/dist/api.js +5 -5
  6. package/dist/auth.cjs +52 -23
  7. package/dist/auth.d.cts +101 -2
  8. package/dist/auth.d.ts +101 -2
  9. package/dist/auth.js +3 -2
  10. package/dist/chunk-3EOM4V2M.cjs +452 -0
  11. package/dist/{chunk-2HU5R2JE.js → chunk-AYERBA7I.js} +1 -7
  12. package/dist/{chunk-YXH2UUEZ.js → chunk-IPDHT2UV.js} +257 -16
  13. package/dist/{chunk-XUCKZPML.cjs → chunk-JCMOOPNX.cjs} +2696 -2601
  14. package/dist/{chunk-W42UZLQO.js → chunk-LT2WOPKA.js} +2642 -2540
  15. package/dist/chunk-RK5ETF2I.js +434 -0
  16. package/dist/{chunk-4PMK3RNA.cjs → chunk-TJ2MIQUT.cjs} +1 -7
  17. package/dist/{chunk-LMJ7RKPF.cjs → chunk-UUWAUHUW.cjs} +279 -16
  18. package/dist/{chunk-YC4NUZCS.js → chunk-WRKV6MHB.js} +414 -2
  19. package/dist/{chunk-Q3HQEM4R.cjs → chunk-YV5PK4JW.cjs} +421 -1
  20. package/dist/cli.cjs +13 -22
  21. package/dist/cli.js +13 -22
  22. package/dist/{event-order-defaults-7Z3UZOEH.cjs → event-order-defaults-DU7V3YND.cjs} +9 -9
  23. package/dist/{event-order-defaults-XQ3IDPD7.js → event-order-defaults-H4RT7NMR.js} +1 -1
  24. package/dist/index.cjs +327 -291
  25. package/dist/index.d.cts +132 -5
  26. package/dist/index.d.ts +132 -5
  27. package/dist/index.js +8 -8
  28. package/dist/migrations/1782400000000-CreateUserDeviceTokens.ts +36 -0
  29. package/dist/migrations/1782500000000-AddPushToOrderNotificationBindingsChannelEnum.ts +15 -0
  30. package/dist/{order-notification-dispatcher-6WNG24NY.js → order-notification-dispatcher-3TA4ETYJ.js} +1 -1
  31. package/dist/{order-notification-dispatcher-6XSU3AR7.cjs → order-notification-dispatcher-ZEAZ5SHV.cjs} +7 -3
  32. package/package.json +1 -1
  33. package/dist/chunk-VUQFARRT.cjs +0 -182
  34. package/dist/chunk-ZF2RQWXB.js +0 -171
@@ -8,11 +8,31 @@ var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
8
8
  var clientSes = require('@aws-sdk/client-ses');
9
9
  var module$1 = require('module');
10
10
  var nodemailer = require('nodemailer');
11
+ var crypto = require('crypto');
11
12
 
12
13
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
15
 
16
+ function _interopNamespace(e) {
17
+ if (e && e.__esModule) return e;
18
+ var n = Object.create(null);
19
+ if (e) {
20
+ Object.keys(e).forEach(function (k) {
21
+ if (k !== 'default') {
22
+ var d = Object.getOwnPropertyDescriptor(e, k);
23
+ Object.defineProperty(n, k, d.get ? d : {
24
+ enumerable: true,
25
+ get: function () { return e[k]; }
26
+ });
27
+ }
28
+ });
29
+ }
30
+ n.default = e;
31
+ return Object.freeze(n);
32
+ }
33
+
15
34
  var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
35
+ var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
16
36
 
17
37
  // src/lib/event-order-template-context.ts
18
38
  function resolvePublicSiteUrl() {
@@ -33,12 +53,6 @@ function buildOrderTrackUrl(qrToken, siteUrl, orderNumber) {
33
53
  return `${base}/track/${token}`;
34
54
  }
35
55
  chunkUSNT2KNT_cjs.__name(buildOrderTrackUrl, "buildOrderTrackUrl");
36
- function buildOrderQrImageUrl(trackUrl, size = 200, fallbackData) {
37
- const data = trackUrl.trim() || fallbackData?.trim() || "";
38
- if (!data) return "";
39
- return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(data)}`;
40
- }
41
- chunkUSNT2KNT_cjs.__name(buildOrderQrImageUrl, "buildOrderQrImageUrl");
42
56
  function formatOrderDetailsText(input) {
43
57
  const { orderNumber, total, currency, lines } = input;
44
58
  const header = `Order: ${orderNumber}
@@ -60,8 +74,6 @@ chunkUSNT2KNT_cjs.__name(formatOrderDetailsHtml, "formatOrderDetailsHtml");
60
74
  function buildEventOrderTemplateVariables(input) {
61
75
  const { event, order, contact, productNames, vendorName = "", orderLines = [], siteUrl } = input;
62
76
  const trackUrl = buildOrderTrackUrl(order.qrToken, siteUrl, order.orderNumber);
63
- const qrImageUrl = buildOrderQrImageUrl(trackUrl, 200, order.orderNumber);
64
- const ticketQr = qrImageUrl ? `<img src="${qrImageUrl}" alt="Ticket QR Code" width="200" height="200" style="display:block;margin:12px 0;border:1px solid #e5e7eb;border-radius:8px;" />` : "";
65
77
  const baseSite = (siteUrl ?? resolvePublicSiteUrl()).replace(/\/+$/, "");
66
78
  const invoiceNumber = `INV-${order.orderNumber ?? ""}`;
67
79
  const invoiceUrl = baseSite ? `${baseSite}/api/orders/${order.orderNumber ?? ""}/invoice` : `/api/orders/${order.orderNumber ?? ""}/invoice`;
@@ -85,8 +97,6 @@ function buildEventOrderTemplateVariables(input) {
85
97
  customerPhone: contact.phone?.trim() || "",
86
98
  productNames,
87
99
  trackUrl,
88
- qrImageUrl,
89
- ticketQr,
90
100
  invoiceNumber,
91
101
  invoiceUrl,
92
102
  orderDetails: formatOrderDetailsText(orderDetailsPayload),
@@ -504,7 +514,7 @@ var WhatsAppService = class {
504
514
  let body = opts.body?.trim() ?? "";
505
515
  const variables = opts.variables ?? {};
506
516
  if (opts.templateKey?.trim() && this.getMessageTemplateRow) {
507
- const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-7Z3UZOEH.cjs');
517
+ const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-DU7V3YND.cjs');
508
518
  if (opts.templateKey.trim() === EVENT_ORDER_TEMPLATE_KEY) {
509
519
  const resolved = await resolveEventOrderTemplate("whatsapp", async (ch, key) => {
510
520
  const row = await this.getMessageTemplateRow(ch, key);
@@ -1316,7 +1326,7 @@ function renderEmail(templateName, ctx, options) {
1316
1326
  chunkUSNT2KNT_cjs.__name(renderEmail, "renderEmail");
1317
1327
 
1318
1328
  // src/plugins/email/email-service.ts
1319
- var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-LMJ7RKPF.cjs', document.baseURI).href)));
1329
+ var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-UUWAUHUW.cjs', document.baseURI).href)));
1320
1330
  var MailComposer = require2("nodemailer/lib/mail-composer");
1321
1331
  var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
1322
1332
  "af-south-1",
@@ -2068,6 +2078,162 @@ function emailPlugin(config) {
2068
2078
  };
2069
2079
  }
2070
2080
  chunkUSNT2KNT_cjs.__name(emailPlugin, "emailPlugin");
2081
+ function base64UrlEncode(str) {
2082
+ const buf = typeof str === "string" ? Buffer.from(str, "utf8") : str;
2083
+ return buf.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2084
+ }
2085
+ chunkUSNT2KNT_cjs.__name(base64UrlEncode, "base64UrlEncode");
2086
+ async function getGoogleAccessToken(clientEmail, privateKey) {
2087
+ const now = Math.floor(Date.now() / 1e3);
2088
+ const header = {
2089
+ alg: "RS256",
2090
+ typ: "JWT"
2091
+ };
2092
+ const claimSet = {
2093
+ iss: clientEmail,
2094
+ scope: "https://www.googleapis.com/auth/firebase.messaging",
2095
+ aud: "https://oauth2.googleapis.com/token",
2096
+ exp: now + 3600,
2097
+ iat: now
2098
+ };
2099
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
2100
+ const encodedClaimSet = base64UrlEncode(JSON.stringify(claimSet));
2101
+ const signatureInput = `${encodedHeader}.${encodedClaimSet}`;
2102
+ const formattedKey = privateKey.replace(/\\n/g, "\n");
2103
+ const signer = crypto__namespace.createSign("RSA-SHA256");
2104
+ signer.update(signatureInput);
2105
+ const signature = signer.sign(formattedKey, "base64");
2106
+ const jwt = `${signatureInput}.${signature.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_")}`;
2107
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
2108
+ method: "POST",
2109
+ headers: {
2110
+ "Content-Type": "application/x-www-form-urlencoded"
2111
+ },
2112
+ body: new URLSearchParams({
2113
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
2114
+ assertion: jwt
2115
+ }).toString()
2116
+ });
2117
+ if (!tokenRes.ok) {
2118
+ const errText = await tokenRes.text();
2119
+ throw new Error(`Failed to obtain Google access token: ${errText}`);
2120
+ }
2121
+ const tokenData = await tokenRes.json();
2122
+ if (!tokenData.access_token) {
2123
+ throw new Error("Google OAuth token response missing access_token");
2124
+ }
2125
+ return tokenData.access_token;
2126
+ }
2127
+ chunkUSNT2KNT_cjs.__name(getGoogleAccessToken, "getGoogleAccessToken");
2128
+ async function resolveFcmServiceAccount(dataSource, entityMap) {
2129
+ const pushSettings = await getSettingsGroup(dataSource, entityMap, "push");
2130
+ let projectId = pushSettings.firebase_project_id?.trim();
2131
+ let clientEmail = pushSettings.firebase_client_email?.trim();
2132
+ let privateKey = pushSettings.firebase_private_key?.trim();
2133
+ if (pushSettings.firebase_service_account_json?.trim()) {
2134
+ try {
2135
+ const parsed = JSON.parse(pushSettings.firebase_service_account_json.trim());
2136
+ if (parsed.project_id) projectId = parsed.project_id;
2137
+ if (parsed.client_email) clientEmail = parsed.client_email;
2138
+ if (parsed.private_key) privateKey = parsed.private_key;
2139
+ } catch {
2140
+ }
2141
+ }
2142
+ if (!projectId || !clientEmail || !privateKey) {
2143
+ return {
2144
+ ok: false,
2145
+ error: "Firebase Service Account JSON / credentials are not configured"
2146
+ };
2147
+ }
2148
+ return {
2149
+ ok: true,
2150
+ account: {
2151
+ projectId,
2152
+ clientEmail,
2153
+ privateKey
2154
+ }
2155
+ };
2156
+ }
2157
+ chunkUSNT2KNT_cjs.__name(resolveFcmServiceAccount, "resolveFcmServiceAccount");
2158
+ async function validateFcmCredentials(dataSource, entityMap) {
2159
+ try {
2160
+ const resolved = await resolveFcmServiceAccount(dataSource, entityMap);
2161
+ if (!resolved.ok) {
2162
+ return {
2163
+ success: false,
2164
+ error: resolved.error
2165
+ };
2166
+ }
2167
+ const { projectId, clientEmail, privateKey } = resolved.account;
2168
+ await getGoogleAccessToken(clientEmail, privateKey);
2169
+ return {
2170
+ success: true,
2171
+ projectId,
2172
+ clientEmail
2173
+ };
2174
+ } catch (err) {
2175
+ const errorMsg = err instanceof Error ? err.message : "Unknown credential validation error";
2176
+ console.error("[fcm-push] Exception in validateFcmCredentials", errorMsg);
2177
+ return {
2178
+ success: false,
2179
+ error: errorMsg
2180
+ };
2181
+ }
2182
+ }
2183
+ chunkUSNT2KNT_cjs.__name(validateFcmCredentials, "validateFcmCredentials");
2184
+ async function sendFcmPushNotification(dataSource, entityMap, msg) {
2185
+ try {
2186
+ const resolved = await resolveFcmServiceAccount(dataSource, entityMap);
2187
+ if (!resolved.ok) {
2188
+ return {
2189
+ success: false,
2190
+ error: resolved.error
2191
+ };
2192
+ }
2193
+ const { projectId, clientEmail, privateKey } = resolved.account;
2194
+ const accessToken = await getGoogleAccessToken(clientEmail, privateKey);
2195
+ const fcmEndpoint = `https://fcm.googleapis.com/v1/projects/${projectId}/messages:send`;
2196
+ const fcmPayload = {
2197
+ message: {
2198
+ token: msg.token,
2199
+ notification: {
2200
+ title: msg.title,
2201
+ body: msg.body
2202
+ },
2203
+ data: msg.data || {}
2204
+ }
2205
+ };
2206
+ const response = await fetch(fcmEndpoint, {
2207
+ method: "POST",
2208
+ headers: {
2209
+ "Authorization": `Bearer ${accessToken}`,
2210
+ "Content-Type": "application/json"
2211
+ },
2212
+ body: JSON.stringify(fcmPayload)
2213
+ });
2214
+ const resData = await response.json();
2215
+ if (!response.ok) {
2216
+ const errorMsg = resData.error?.message || "FCM request failed";
2217
+ console.error("[fcm-push] Error sending push notification", errorMsg);
2218
+ return {
2219
+ success: false,
2220
+ error: errorMsg
2221
+ };
2222
+ }
2223
+ return {
2224
+ success: true,
2225
+ messageId: resData.name
2226
+ };
2227
+ } catch (err) {
2228
+ const errorMsg = err instanceof Error ? err.message : "Unknown push error";
2229
+ console.error("[fcm-push] Exception in sendFcmPushNotification", errorMsg);
2230
+ return {
2231
+ success: false,
2232
+ error: errorMsg
2233
+ };
2234
+ }
2235
+ }
2236
+ chunkUSNT2KNT_cjs.__name(sendFcmPushNotification, "sendFcmPushNotification");
2071
2237
 
2072
2238
  // src/lib/order-notification-dispatcher.ts
2073
2239
  var initialized = false;
@@ -2218,6 +2384,82 @@ async function isAudienceEmailEnabled(dataSource, entityMap, audience) {
2218
2384
  return settings[key] !== "false" && settings[key] !== "0";
2219
2385
  }
2220
2386
  chunkUSNT2KNT_cjs.__name(isAudienceEmailEnabled, "isAudienceEmailEnabled");
2387
+ async function isPushNotificationsEnabled(dataSource, entityMap) {
2388
+ const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2389
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2390
+ return settings.push_enabled !== "false" && settings.push_enabled !== "0";
2391
+ }
2392
+ chunkUSNT2KNT_cjs.__name(isPushNotificationsEnabled, "isPushNotificationsEnabled");
2393
+ async function isAudiencePushEnabled(dataSource, entityMap, audience) {
2394
+ const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2395
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2396
+ if (settings.push_enabled === "false" || settings.push_enabled === "0") return false;
2397
+ const key = `push_${audience}_enabled`;
2398
+ return settings[key] !== "false" && settings[key] !== "0";
2399
+ }
2400
+ chunkUSNT2KNT_cjs.__name(isAudiencePushEnabled, "isAudiencePushEnabled");
2401
+ async function sendPushNotificationForTrigger(triggerKey, orderId, audienceType, targetId, variables, dataSource, entityMap) {
2402
+ let template = await loadBoundTemplate(triggerKey, "mobile", audienceType, dataSource, entityMap);
2403
+ if (!template) {
2404
+ template = await loadBoundTemplate(triggerKey, "push", audienceType, dataSource, entityMap);
2405
+ }
2406
+ if (!template || !template.body) return;
2407
+ const rawTitle = template.subject?.trim() || "Order Notification";
2408
+ const title = chunkOOTLNH63_cjs.applyTemplateVars(rawTitle, variables);
2409
+ const body = chunkOOTLNH63_cjs.applyTemplateVars(template.body, variables);
2410
+ if (!entityMap.user_device_tokens) return;
2411
+ const tokenRepo = dataSource.getRepository(entityMap.user_device_tokens);
2412
+ let targetTokens = [];
2413
+ try {
2414
+ if (audienceType === "customers") {
2415
+ targetTokens = await tokenRepo.find({
2416
+ where: {
2417
+ userId: targetId,
2418
+ isActive: true
2419
+ },
2420
+ select: [
2421
+ "token"
2422
+ ]
2423
+ });
2424
+ } else if (audienceType === "vendors") {
2425
+ targetTokens = await tokenRepo.find({
2426
+ where: {
2427
+ vendorId: targetId,
2428
+ isActive: true
2429
+ },
2430
+ select: [
2431
+ "token"
2432
+ ]
2433
+ });
2434
+ } else if (audienceType === "admin") {
2435
+ targetTokens = await tokenRepo.find({
2436
+ where: {
2437
+ isActive: true
2438
+ },
2439
+ select: [
2440
+ "token"
2441
+ ]
2442
+ });
2443
+ }
2444
+ } catch (err) {
2445
+ console.error("[order-notifications] Error querying device tokens for push", err);
2446
+ return;
2447
+ }
2448
+ for (const t of targetTokens) {
2449
+ if (t.token?.trim()) {
2450
+ await sendFcmPushNotification(dataSource, entityMap, {
2451
+ token: t.token.trim(),
2452
+ title,
2453
+ body,
2454
+ data: {
2455
+ orderId: String(orderId),
2456
+ triggerKey
2457
+ }
2458
+ });
2459
+ }
2460
+ }
2461
+ }
2462
+ chunkUSNT2KNT_cjs.__name(sendPushNotificationForTrigger, "sendPushNotificationForTrigger");
2221
2463
  async function getOrderVendorEmails(orderId, orderVendorId, dataSource, entityMap) {
2222
2464
  const emails = /* @__PURE__ */ new Set();
2223
2465
  if (!entityMap.vendors) return [];
@@ -2480,12 +2722,13 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
2480
2722
  return;
2481
2723
  }
2482
2724
  }
2483
- const [emailChannelEnabled, whatsappChannelEnabled] = await Promise.all([
2725
+ const [emailChannelEnabled, whatsappChannelEnabled, pushChannelEnabled] = await Promise.all([
2484
2726
  isEmailNotificationsEnabled(dataSource, entityMap),
2485
- isWhatsappNotificationsEnabled(dataSource, entityMap)
2727
+ isWhatsappNotificationsEnabled(dataSource, entityMap),
2728
+ isPushNotificationsEnabled(dataSource, entityMap)
2486
2729
  ]);
2487
- if (!emailChannelEnabled && !whatsappChannelEnabled) {
2488
- console.warn("[order-notifications] skip: Both Email and WhatsApp notifications are disabled");
2730
+ if (!emailChannelEnabled && !whatsappChannelEnabled && !pushChannelEnabled) {
2731
+ console.warn("[order-notifications] skip: Email, WhatsApp, and Mobile Push notifications are disabled");
2489
2732
  return;
2490
2733
  }
2491
2734
  const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, dataSource, entityMap);
@@ -2524,6 +2767,23 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
2524
2767
  } else {
2525
2768
  console.log("[order-notifications] skip email: Email notifications channel is disabled");
2526
2769
  }
2770
+ if (pushChannelEnabled) {
2771
+ const [pushCustomerEnabled, pushVendorEnabled, pushAdminEnabled] = await Promise.all([
2772
+ isAudiencePushEnabled(dataSource, entityMap, "customer"),
2773
+ isAudiencePushEnabled(dataSource, entityMap, "vendor"),
2774
+ isAudiencePushEnabled(dataSource, entityMap, "admin")
2775
+ ]);
2776
+ const targetCustomerId = context?.order?.customerContactId || context?.order?.contactId || context?.order?.userId || context?.order?.id;
2777
+ if (pushCustomerEnabled && targetCustomerId) {
2778
+ await sendPushNotificationForTrigger(triggerKey, payload.orderId, "customers", String(targetCustomerId), variables, dataSource, entityMap);
2779
+ }
2780
+ if (pushVendorEnabled && payload.vendorId) {
2781
+ await sendPushNotificationForTrigger(triggerKey, payload.orderId, "vendors", String(payload.vendorId), variables, dataSource, entityMap);
2782
+ }
2783
+ if (pushAdminEnabled) {
2784
+ await sendPushNotificationForTrigger(triggerKey, payload.orderId, "admin", "admin", variables, dataSource, entityMap);
2785
+ }
2786
+ }
2527
2787
  if (whatsappChannelEnabled) {
2528
2788
  if (contactPhone) {
2529
2789
  await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
@@ -2610,6 +2870,7 @@ exports.buildTranscriptForLeadEmail = buildTranscriptForLeadEmail;
2610
2870
  exports.detectChatLeadIntent = detectChatLeadIntent;
2611
2871
  exports.emailPlugin = emailPlugin;
2612
2872
  exports.emailTemplates = emailTemplates;
2873
+ exports.getSettingsGroup = getSettingsGroup;
2613
2874
  exports.initWhatsappTriggerDispatcher = initWhatsappTriggerDispatcher;
2614
2875
  exports.isWhatsAppPluginEnabled = isWhatsAppPluginEnabled;
2615
2876
  exports.joinRecipientsForSend = joinRecipientsForSend;
@@ -2623,5 +2884,7 @@ exports.renderLayout = renderLayout;
2623
2884
  exports.resendOrderNotification = resendOrderNotification;
2624
2885
  exports.resolveChatEmailToolSettings = resolveChatEmailToolSettings;
2625
2886
  exports.sendChatLeadEmail = sendChatLeadEmail;
2887
+ exports.sendFcmPushNotification = sendFcmPushNotification;
2626
2888
  exports.serializeEmailRecipients = serializeEmailRecipients;
2889
+ exports.validateFcmCredentials = validateFcmCredentials;
2627
2890
  exports.whatsAppConfigured = whatsAppConfigured;