@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
@@ -6,6 +6,7 @@ import { __name } from './chunk-SHUYVCID.js';
6
6
  import { SESClient, SendRawEmailCommand, SendEmailCommand } from '@aws-sdk/client-ses';
7
7
  import { createRequire } from 'module';
8
8
  import nodemailer from 'nodemailer';
9
+ import * as crypto from 'crypto';
9
10
 
10
11
  // src/lib/event-order-template-context.ts
11
12
  function resolvePublicSiteUrl() {
@@ -26,12 +27,6 @@ function buildOrderTrackUrl(qrToken, siteUrl, orderNumber) {
26
27
  return `${base}/track/${token}`;
27
28
  }
28
29
  __name(buildOrderTrackUrl, "buildOrderTrackUrl");
29
- function buildOrderQrImageUrl(trackUrl, size = 200, fallbackData) {
30
- const data = trackUrl.trim() || fallbackData?.trim() || "";
31
- if (!data) return "";
32
- return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(data)}`;
33
- }
34
- __name(buildOrderQrImageUrl, "buildOrderQrImageUrl");
35
30
  function formatOrderDetailsText(input) {
36
31
  const { orderNumber, total, currency, lines } = input;
37
32
  const header = `Order: ${orderNumber}
@@ -53,8 +48,6 @@ __name(formatOrderDetailsHtml, "formatOrderDetailsHtml");
53
48
  function buildEventOrderTemplateVariables(input) {
54
49
  const { event, order, contact, productNames, vendorName = "", orderLines = [], siteUrl } = input;
55
50
  const trackUrl = buildOrderTrackUrl(order.qrToken, siteUrl, order.orderNumber);
56
- const qrImageUrl = buildOrderQrImageUrl(trackUrl, 200, order.orderNumber);
57
- 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;" />` : "";
58
51
  const baseSite = (siteUrl ?? resolvePublicSiteUrl()).replace(/\/+$/, "");
59
52
  const invoiceNumber = `INV-${order.orderNumber ?? ""}`;
60
53
  const invoiceUrl = baseSite ? `${baseSite}/api/orders/${order.orderNumber ?? ""}/invoice` : `/api/orders/${order.orderNumber ?? ""}/invoice`;
@@ -78,8 +71,6 @@ function buildEventOrderTemplateVariables(input) {
78
71
  customerPhone: contact.phone?.trim() || "",
79
72
  productNames,
80
73
  trackUrl,
81
- qrImageUrl,
82
- ticketQr,
83
74
  invoiceNumber,
84
75
  invoiceUrl,
85
76
  orderDetails: formatOrderDetailsText(orderDetailsPayload),
@@ -497,7 +488,7 @@ var WhatsAppService = class {
497
488
  let body = opts.body?.trim() ?? "";
498
489
  const variables = opts.variables ?? {};
499
490
  if (opts.templateKey?.trim() && this.getMessageTemplateRow) {
500
- const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-XQ3IDPD7.js');
491
+ const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-H4RT7NMR.js');
501
492
  if (opts.templateKey.trim() === EVENT_ORDER_TEMPLATE_KEY) {
502
493
  const resolved = await resolveEventOrderTemplate("whatsapp", async (ch, key) => {
503
494
  const row = await this.getMessageTemplateRow(ch, key);
@@ -2061,6 +2052,162 @@ function emailPlugin(config) {
2061
2052
  };
2062
2053
  }
2063
2054
  __name(emailPlugin, "emailPlugin");
2055
+ function base64UrlEncode(str) {
2056
+ const buf = typeof str === "string" ? Buffer.from(str, "utf8") : str;
2057
+ return buf.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2058
+ }
2059
+ __name(base64UrlEncode, "base64UrlEncode");
2060
+ async function getGoogleAccessToken(clientEmail, privateKey) {
2061
+ const now = Math.floor(Date.now() / 1e3);
2062
+ const header = {
2063
+ alg: "RS256",
2064
+ typ: "JWT"
2065
+ };
2066
+ const claimSet = {
2067
+ iss: clientEmail,
2068
+ scope: "https://www.googleapis.com/auth/firebase.messaging",
2069
+ aud: "https://oauth2.googleapis.com/token",
2070
+ exp: now + 3600,
2071
+ iat: now
2072
+ };
2073
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
2074
+ const encodedClaimSet = base64UrlEncode(JSON.stringify(claimSet));
2075
+ const signatureInput = `${encodedHeader}.${encodedClaimSet}`;
2076
+ const formattedKey = privateKey.replace(/\\n/g, "\n");
2077
+ const signer = crypto.createSign("RSA-SHA256");
2078
+ signer.update(signatureInput);
2079
+ const signature = signer.sign(formattedKey, "base64");
2080
+ const jwt = `${signatureInput}.${signature.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_")}`;
2081
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
2082
+ method: "POST",
2083
+ headers: {
2084
+ "Content-Type": "application/x-www-form-urlencoded"
2085
+ },
2086
+ body: new URLSearchParams({
2087
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
2088
+ assertion: jwt
2089
+ }).toString()
2090
+ });
2091
+ if (!tokenRes.ok) {
2092
+ const errText = await tokenRes.text();
2093
+ throw new Error(`Failed to obtain Google access token: ${errText}`);
2094
+ }
2095
+ const tokenData = await tokenRes.json();
2096
+ if (!tokenData.access_token) {
2097
+ throw new Error("Google OAuth token response missing access_token");
2098
+ }
2099
+ return tokenData.access_token;
2100
+ }
2101
+ __name(getGoogleAccessToken, "getGoogleAccessToken");
2102
+ async function resolveFcmServiceAccount(dataSource, entityMap) {
2103
+ const pushSettings = await getSettingsGroup(dataSource, entityMap, "push");
2104
+ let projectId = pushSettings.firebase_project_id?.trim();
2105
+ let clientEmail = pushSettings.firebase_client_email?.trim();
2106
+ let privateKey = pushSettings.firebase_private_key?.trim();
2107
+ if (pushSettings.firebase_service_account_json?.trim()) {
2108
+ try {
2109
+ const parsed = JSON.parse(pushSettings.firebase_service_account_json.trim());
2110
+ if (parsed.project_id) projectId = parsed.project_id;
2111
+ if (parsed.client_email) clientEmail = parsed.client_email;
2112
+ if (parsed.private_key) privateKey = parsed.private_key;
2113
+ } catch {
2114
+ }
2115
+ }
2116
+ if (!projectId || !clientEmail || !privateKey) {
2117
+ return {
2118
+ ok: false,
2119
+ error: "Firebase Service Account JSON / credentials are not configured"
2120
+ };
2121
+ }
2122
+ return {
2123
+ ok: true,
2124
+ account: {
2125
+ projectId,
2126
+ clientEmail,
2127
+ privateKey
2128
+ }
2129
+ };
2130
+ }
2131
+ __name(resolveFcmServiceAccount, "resolveFcmServiceAccount");
2132
+ async function validateFcmCredentials(dataSource, entityMap) {
2133
+ try {
2134
+ const resolved = await resolveFcmServiceAccount(dataSource, entityMap);
2135
+ if (!resolved.ok) {
2136
+ return {
2137
+ success: false,
2138
+ error: resolved.error
2139
+ };
2140
+ }
2141
+ const { projectId, clientEmail, privateKey } = resolved.account;
2142
+ await getGoogleAccessToken(clientEmail, privateKey);
2143
+ return {
2144
+ success: true,
2145
+ projectId,
2146
+ clientEmail
2147
+ };
2148
+ } catch (err) {
2149
+ const errorMsg = err instanceof Error ? err.message : "Unknown credential validation error";
2150
+ console.error("[fcm-push] Exception in validateFcmCredentials", errorMsg);
2151
+ return {
2152
+ success: false,
2153
+ error: errorMsg
2154
+ };
2155
+ }
2156
+ }
2157
+ __name(validateFcmCredentials, "validateFcmCredentials");
2158
+ async function sendFcmPushNotification(dataSource, entityMap, msg) {
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
+ const accessToken = await getGoogleAccessToken(clientEmail, privateKey);
2169
+ const fcmEndpoint = `https://fcm.googleapis.com/v1/projects/${projectId}/messages:send`;
2170
+ const fcmPayload = {
2171
+ message: {
2172
+ token: msg.token,
2173
+ notification: {
2174
+ title: msg.title,
2175
+ body: msg.body
2176
+ },
2177
+ data: msg.data || {}
2178
+ }
2179
+ };
2180
+ const response = await fetch(fcmEndpoint, {
2181
+ method: "POST",
2182
+ headers: {
2183
+ "Authorization": `Bearer ${accessToken}`,
2184
+ "Content-Type": "application/json"
2185
+ },
2186
+ body: JSON.stringify(fcmPayload)
2187
+ });
2188
+ const resData = await response.json();
2189
+ if (!response.ok) {
2190
+ const errorMsg = resData.error?.message || "FCM request failed";
2191
+ console.error("[fcm-push] Error sending push notification", errorMsg);
2192
+ return {
2193
+ success: false,
2194
+ error: errorMsg
2195
+ };
2196
+ }
2197
+ return {
2198
+ success: true,
2199
+ messageId: resData.name
2200
+ };
2201
+ } catch (err) {
2202
+ const errorMsg = err instanceof Error ? err.message : "Unknown push error";
2203
+ console.error("[fcm-push] Exception in sendFcmPushNotification", errorMsg);
2204
+ return {
2205
+ success: false,
2206
+ error: errorMsg
2207
+ };
2208
+ }
2209
+ }
2210
+ __name(sendFcmPushNotification, "sendFcmPushNotification");
2064
2211
 
2065
2212
  // src/lib/order-notification-dispatcher.ts
2066
2213
  var initialized = false;
@@ -2211,6 +2358,82 @@ async function isAudienceEmailEnabled(dataSource, entityMap, audience) {
2211
2358
  return settings[key] !== "false" && settings[key] !== "0";
2212
2359
  }
2213
2360
  __name(isAudienceEmailEnabled, "isAudienceEmailEnabled");
2361
+ async function isPushNotificationsEnabled(dataSource, entityMap) {
2362
+ const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2363
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2364
+ return settings.push_enabled !== "false" && settings.push_enabled !== "0";
2365
+ }
2366
+ __name(isPushNotificationsEnabled, "isPushNotificationsEnabled");
2367
+ async function isAudiencePushEnabled(dataSource, entityMap, audience) {
2368
+ const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2369
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2370
+ if (settings.push_enabled === "false" || settings.push_enabled === "0") return false;
2371
+ const key = `push_${audience}_enabled`;
2372
+ return settings[key] !== "false" && settings[key] !== "0";
2373
+ }
2374
+ __name(isAudiencePushEnabled, "isAudiencePushEnabled");
2375
+ async function sendPushNotificationForTrigger(triggerKey, orderId, audienceType, targetId, variables, dataSource, entityMap) {
2376
+ let template = await loadBoundTemplate(triggerKey, "mobile", audienceType, dataSource, entityMap);
2377
+ if (!template) {
2378
+ template = await loadBoundTemplate(triggerKey, "push", audienceType, dataSource, entityMap);
2379
+ }
2380
+ if (!template || !template.body) return;
2381
+ const rawTitle = template.subject?.trim() || "Order Notification";
2382
+ const title = applyTemplateVars(rawTitle, variables);
2383
+ const body = applyTemplateVars(template.body, variables);
2384
+ if (!entityMap.user_device_tokens) return;
2385
+ const tokenRepo = dataSource.getRepository(entityMap.user_device_tokens);
2386
+ let targetTokens = [];
2387
+ try {
2388
+ if (audienceType === "customers") {
2389
+ targetTokens = await tokenRepo.find({
2390
+ where: {
2391
+ userId: targetId,
2392
+ isActive: true
2393
+ },
2394
+ select: [
2395
+ "token"
2396
+ ]
2397
+ });
2398
+ } else if (audienceType === "vendors") {
2399
+ targetTokens = await tokenRepo.find({
2400
+ where: {
2401
+ vendorId: targetId,
2402
+ isActive: true
2403
+ },
2404
+ select: [
2405
+ "token"
2406
+ ]
2407
+ });
2408
+ } else if (audienceType === "admin") {
2409
+ targetTokens = await tokenRepo.find({
2410
+ where: {
2411
+ isActive: true
2412
+ },
2413
+ select: [
2414
+ "token"
2415
+ ]
2416
+ });
2417
+ }
2418
+ } catch (err) {
2419
+ console.error("[order-notifications] Error querying device tokens for push", err);
2420
+ return;
2421
+ }
2422
+ for (const t of targetTokens) {
2423
+ if (t.token?.trim()) {
2424
+ await sendFcmPushNotification(dataSource, entityMap, {
2425
+ token: t.token.trim(),
2426
+ title,
2427
+ body,
2428
+ data: {
2429
+ orderId: String(orderId),
2430
+ triggerKey
2431
+ }
2432
+ });
2433
+ }
2434
+ }
2435
+ }
2436
+ __name(sendPushNotificationForTrigger, "sendPushNotificationForTrigger");
2214
2437
  async function getOrderVendorEmails(orderId, orderVendorId, dataSource, entityMap) {
2215
2438
  const emails = /* @__PURE__ */ new Set();
2216
2439
  if (!entityMap.vendors) return [];
@@ -2473,12 +2696,13 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
2473
2696
  return;
2474
2697
  }
2475
2698
  }
2476
- const [emailChannelEnabled, whatsappChannelEnabled] = await Promise.all([
2699
+ const [emailChannelEnabled, whatsappChannelEnabled, pushChannelEnabled] = await Promise.all([
2477
2700
  isEmailNotificationsEnabled(dataSource, entityMap),
2478
- isWhatsappNotificationsEnabled(dataSource, entityMap)
2701
+ isWhatsappNotificationsEnabled(dataSource, entityMap),
2702
+ isPushNotificationsEnabled(dataSource, entityMap)
2479
2703
  ]);
2480
- if (!emailChannelEnabled && !whatsappChannelEnabled) {
2481
- console.warn("[order-notifications] skip: Both Email and WhatsApp notifications are disabled");
2704
+ if (!emailChannelEnabled && !whatsappChannelEnabled && !pushChannelEnabled) {
2705
+ console.warn("[order-notifications] skip: Email, WhatsApp, and Mobile Push notifications are disabled");
2482
2706
  return;
2483
2707
  }
2484
2708
  const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, dataSource, entityMap);
@@ -2517,6 +2741,23 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
2517
2741
  } else {
2518
2742
  console.log("[order-notifications] skip email: Email notifications channel is disabled");
2519
2743
  }
2744
+ if (pushChannelEnabled) {
2745
+ const [pushCustomerEnabled, pushVendorEnabled, pushAdminEnabled] = await Promise.all([
2746
+ isAudiencePushEnabled(dataSource, entityMap, "customer"),
2747
+ isAudiencePushEnabled(dataSource, entityMap, "vendor"),
2748
+ isAudiencePushEnabled(dataSource, entityMap, "admin")
2749
+ ]);
2750
+ const targetCustomerId = context?.order?.customerContactId || context?.order?.contactId || context?.order?.userId || context?.order?.id;
2751
+ if (pushCustomerEnabled && targetCustomerId) {
2752
+ await sendPushNotificationForTrigger(triggerKey, payload.orderId, "customers", String(targetCustomerId), variables, dataSource, entityMap);
2753
+ }
2754
+ if (pushVendorEnabled && payload.vendorId) {
2755
+ await sendPushNotificationForTrigger(triggerKey, payload.orderId, "vendors", String(payload.vendorId), variables, dataSource, entityMap);
2756
+ }
2757
+ if (pushAdminEnabled) {
2758
+ await sendPushNotificationForTrigger(triggerKey, payload.orderId, "admin", "admin", variables, dataSource, entityMap);
2759
+ }
2760
+ }
2520
2761
  if (whatsappChannelEnabled) {
2521
2762
  if (contactPhone) {
2522
2763
  await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
@@ -2594,4 +2835,4 @@ async function resendOrderNotification(triggerKey, orderId, deps) {
2594
2835
  }
2595
2836
  __name(resendOrderNotification, "resendOrderNotification");
2596
2837
 
2597
- export { CHAT_EMAIL_LOG, EmailService, WhatsAppService, buildChatbotSystemPromptWithEmailTool, buildEventOrderTemplateVariables, buildTranscriptForLeadEmail, detectChatLeadIntent, emailPlugin, emailTemplates, initWhatsappTriggerDispatcher, isWhatsAppPluginEnabled, joinRecipientsForSend, mergeWhatsAppConfigLayers, parseEmailRecipientsFromConfig, parseIntentRecipientEmails, queueWhatsApp, registerWhatsAppQueueProcessor, renderEmail, renderLayout, resendOrderNotification, resolveChatEmailToolSettings, sendChatLeadEmail, serializeEmailRecipients, whatsAppConfigured };
2838
+ export { CHAT_EMAIL_LOG, EmailService, WhatsAppService, buildChatbotSystemPromptWithEmailTool, buildEventOrderTemplateVariables, buildTranscriptForLeadEmail, detectChatLeadIntent, emailPlugin, emailTemplates, getSettingsGroup, initWhatsappTriggerDispatcher, isWhatsAppPluginEnabled, joinRecipientsForSend, mergeWhatsAppConfigLayers, parseEmailRecipientsFromConfig, parseIntentRecipientEmails, queueWhatsApp, registerWhatsAppQueueProcessor, renderEmail, renderLayout, resendOrderNotification, resolveChatEmailToolSettings, sendChatLeadEmail, sendFcmPushNotification, serializeEmailRecipients, validateFcmCredentials, whatsAppConfigured };