@infuro/cms-core 1.0.44 → 1.0.46

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 (66) hide show
  1. package/README.md +19 -2
  2. package/dist/admin.cjs +1413 -620
  3. package/dist/admin.d.cts +8 -2
  4. package/dist/admin.d.ts +8 -2
  5. package/dist/admin.js +1414 -621
  6. package/dist/api.cjs +40 -37
  7. package/dist/api.d.cts +3 -1
  8. package/dist/api.d.ts +3 -1
  9. package/dist/api.js +8 -5
  10. package/dist/auth.cjs +23 -23
  11. package/dist/auth.d.cts +1 -0
  12. package/dist/auth.d.ts +1 -0
  13. package/dist/auth.js +2 -2
  14. package/dist/chunk-2KUCQVAQ.js +936 -0
  15. package/dist/chunk-3K5AIEIZ.js +256 -0
  16. package/dist/{chunk-I6NH72MO.js → chunk-57O3HZPG.js} +9 -1
  17. package/dist/chunk-7PMHZRF3.cjs +872 -0
  18. package/dist/chunk-BXYZDMTZ.cjs +953 -0
  19. package/dist/chunk-CQHXW4TR.js +107 -0
  20. package/dist/{chunk-NNQBWDCI.js → chunk-CRYKVJTO.js} +55 -959
  21. package/dist/chunk-DBPSJYLZ.js +47 -0
  22. package/dist/{chunk-TAHX46EI.cjs → chunk-FBKDMRQL.cjs} +9 -1
  23. package/dist/chunk-GUSHM5HN.js +862 -0
  24. package/dist/chunk-HY3AXLOI.cjs +265 -0
  25. package/dist/chunk-L3525VXI.js +243 -0
  26. package/dist/{chunk-ZMRQ72F6.cjs → chunk-NBY4NVTY.cjs} +3 -3
  27. package/dist/chunk-OY2YTBMP.cjs +50 -0
  28. package/dist/{chunk-BLR6H5GL.js → chunk-SF2XKMCI.js} +3 -3
  29. package/dist/{chunk-22FFHLTO.cjs → chunk-TCSGFAWB.cjs} +56 -970
  30. package/dist/{chunk-P2IWWBJV.cjs → chunk-UDVLFVEC.cjs} +1314 -601
  31. package/dist/{chunk-SQGBHYJF.js → chunk-UKC3HYXI.js} +1245 -548
  32. package/dist/chunk-V25RDUOU.cjs +248 -0
  33. package/dist/chunk-XMRMZ6NQ.cjs +109 -0
  34. package/dist/cli.cjs +9 -4
  35. package/dist/cli.js +9 -4
  36. package/dist/{email-queue-TPZWLTIV.cjs → email-queue-6NJY6HNM.cjs} +7 -5
  37. package/dist/email-queue-OI2HSCGE.js +4 -0
  38. package/dist/erp-order-invoice-3GXDE443.js +3 -0
  39. package/dist/erp-order-invoice-EYYNR526.cjs +24 -0
  40. package/dist/index.cjs +426 -359
  41. package/dist/index.d.cts +308 -20
  42. package/dist/index.d.ts +308 -20
  43. package/dist/index.js +31 -110
  44. package/dist/migrations/1781800000000-CustomerPhoneNullable.ts +53 -0
  45. package/dist/migrations/1781810000000-OrderItemsVariantId.ts +44 -0
  46. package/dist/migrations/1781900000000-UserInviteStatusToken.ts +46 -0
  47. package/dist/migrations/1782000000000-ComboSlug.ts +58 -0
  48. package/dist/migrations/1782100000000-DiscountDeviceType.ts +20 -0
  49. package/dist/order-inventory-2MYRKEPC.cjs +39 -0
  50. package/dist/order-inventory-76V4XIGW.js +2 -0
  51. package/dist/order-notification-dispatcher-SBQAMM5V.cjs +21 -0
  52. package/dist/{order-notification-dispatcher-XWIJYDGA.js → order-notification-dispatcher-ZJZB2HUN.js} +5 -2
  53. package/dist/retire-soft-deleted-unique-5VXBA5XX.cjs +15 -0
  54. package/dist/retire-soft-deleted-unique-NXQIH4BC.js +2 -0
  55. package/dist/send-order-placed-emails-B5ZVJT7T.cjs +15 -0
  56. package/dist/send-order-placed-emails-WPI37KRZ.js +6 -0
  57. package/package.json +5 -4
  58. package/src/admin/admin.css +27 -24
  59. package/dist/chunk-5ELSW2UP.js +0 -150
  60. package/dist/chunk-PNOKNSG2.cjs +0 -222
  61. package/dist/chunk-WEMDMVHQ.js +0 -216
  62. package/dist/chunk-XW5ATPRW.cjs +0 -155
  63. package/dist/email-queue-CGZBVVAS.js +0 -2
  64. package/dist/erp-order-invoice-3YISSOWW.js +0 -3
  65. package/dist/erp-order-invoice-ZR5F2KTA.cjs +0 -12
  66. package/dist/order-notification-dispatcher-RU7BTGGX.cjs +0 -18
@@ -1,575 +1,10 @@
1
- import { notificationTriggerEmitter } from './chunk-HCIRL37O.js';
2
- import { queueEmail } from './chunk-5ELSW2UP.js';
3
- import { applyTemplateVars } from './chunk-CRFV5WJK.js';
1
+ import { queueEmail } from './chunk-L3525VXI.js';
2
+ import { mergeEmailLayoutCompanyDetails } from './chunk-2KUCQVAQ.js';
4
3
  import { __name } from './chunk-SHUYVCID.js';
5
- import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
4
+ import { SESClient, SendRawEmailCommand, SendEmailCommand } from '@aws-sdk/client-ses';
5
+ import { createRequire } from 'module';
6
6
  import nodemailer from 'nodemailer';
7
7
 
8
- // src/lib/event-order-template-context.ts
9
- function resolvePublicSiteUrl() {
10
- if (typeof process !== "undefined") {
11
- const u = (process.env.CMS_PUBLIC_URL || process.env.NEXT_PUBLIC_SITE_URL || "").trim();
12
- if (u) return u.replace(/\/+$/, "");
13
- const v = (process.env.VERCEL_URL || "").trim();
14
- if (v) return v.startsWith("http") ? v.replace(/\/+$/, "") : `https://${v.replace(/\/+$/, "")}`;
15
- }
16
- return "";
17
- }
18
- __name(resolvePublicSiteUrl, "resolvePublicSiteUrl");
19
- function buildOrderTrackUrl(qrToken, siteUrl) {
20
- const token = qrToken?.trim();
21
- if (!token) return "";
22
- const base = (siteUrl ?? resolvePublicSiteUrl()).replace(/\/+$/, "");
23
- if (!base) return `/track/${token}`;
24
- return `${base}/track/${token}`;
25
- }
26
- __name(buildOrderTrackUrl, "buildOrderTrackUrl");
27
- function buildOrderQrImageUrl(trackUrl, size = 200) {
28
- if (!trackUrl.trim()) return "";
29
- return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(trackUrl)}`;
30
- }
31
- __name(buildOrderQrImageUrl, "buildOrderQrImageUrl");
32
- function formatOrderDetailsText(input) {
33
- const { orderNumber, total, currency, lines } = input;
34
- const header = `Order: ${orderNumber}
35
- Total: ${total} ${currency}`;
36
- if (!lines.length) return header;
37
- const items = lines.map((line) => `- ${line.name} \xD7 ${line.quantity} (${line.total} ${line.currency})`).join("\n");
38
- return `${header}
39
-
40
- Items:
41
- ${items}`;
42
- }
43
- __name(formatOrderDetailsText, "formatOrderDetailsText");
44
- function formatOrderDetailsHtml(input) {
45
- const text = formatOrderDetailsText(input);
46
- const escaped = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
47
- return `<pre style="font-family:inherit;white-space:pre-wrap;margin:0">${escaped}</pre>`;
48
- }
49
- __name(formatOrderDetailsHtml, "formatOrderDetailsHtml");
50
- function buildEventOrderTemplateVariables(input) {
51
- const { event, order, contact, productNames, orderLines = [], siteUrl } = input;
52
- const trackUrl = buildOrderTrackUrl(order.qrToken, siteUrl);
53
- const qrImageUrl = buildOrderQrImageUrl(trackUrl);
54
- const orderDetailsPayload = {
55
- orderNumber: String(order.orderNumber ?? ""),
56
- total: String(order.total ?? ""),
57
- currency: String(order.currency ?? "INR"),
58
- lines: orderLines
59
- };
60
- return {
61
- eventName: event.name ?? "",
62
- eventSlug: event.slug ?? "",
63
- venue: event.venue ?? "",
64
- startDate: formatEventDate(event.startDate),
65
- orderNumber: String(order.orderNumber ?? ""),
66
- orderTotal: String(order.total ?? ""),
67
- currency: String(order.currency ?? "INR"),
68
- customerName: contact.name?.trim() || contact.email?.split("@")[0] || "Guest",
69
- customerEmail: contact.email?.trim() || "",
70
- customerPhone: contact.phone?.trim() || "",
71
- productNames,
72
- trackUrl,
73
- qrImageUrl,
74
- orderDetails: formatOrderDetailsText(orderDetailsPayload),
75
- orderDetailsHtml: formatOrderDetailsHtml(orderDetailsPayload)
76
- };
77
- }
78
- __name(buildEventOrderTemplateVariables, "buildEventOrderTemplateVariables");
79
- function formatEventDate(d) {
80
- if (!d) return "";
81
- try {
82
- return new Date(d).toLocaleString(void 0, {
83
- dateStyle: "medium",
84
- timeStyle: "short"
85
- });
86
- } catch {
87
- return String(d);
88
- }
89
- }
90
- __name(formatEventDate, "formatEventDate");
91
-
92
- // src/lib/order-notification-context.ts
93
- async function loadOrderLines(dataSource, entityMap, orderId, currency) {
94
- if (!entityMap.order_items) return [];
95
- const itemRows = await dataSource.getRepository(entityMap.order_items).find({
96
- where: {
97
- orderId
98
- },
99
- relations: [
100
- "product"
101
- ],
102
- order: {
103
- id: "ASC"
104
- }
105
- });
106
- return itemRows.map((item) => {
107
- const row = item;
108
- const name = row.product?.name || row.product?.title || `Product #${row.productId}`;
109
- return {
110
- name: String(name),
111
- quantity: Number(row.quantity) || 1,
112
- total: row.total,
113
- currency
114
- };
115
- });
116
- }
117
- __name(loadOrderLines, "loadOrderLines");
118
- async function loadPrimaryEvent(dataSource, entityMap, itemRows) {
119
- if (!entityMap.event_products || !entityMap.events) {
120
- return {
121
- productNames: []
122
- };
123
- }
124
- const epRepo = dataSource.getRepository(entityMap.event_products);
125
- const eventRepo = dataSource.getRepository(entityMap.events);
126
- const eventMap = /* @__PURE__ */ new Map();
127
- for (const item of itemRows) {
128
- const productId = item.productId;
129
- const productName = item.product?.name || item.product?.title || `Product #${productId}`;
130
- const eps = await epRepo.find({
131
- where: {
132
- productId,
133
- isActive: true
134
- }
135
- });
136
- for (const ep of eps) {
137
- const eventId = ep.eventId;
138
- const existing = eventMap.get(eventId);
139
- if (existing) {
140
- if (!existing.productNames.includes(String(productName))) {
141
- existing.productNames.push(String(productName));
142
- }
143
- } else {
144
- const event = await eventRepo.findOne({
145
- where: {
146
- id: eventId,
147
- deleted: false
148
- }
149
- });
150
- if (event) {
151
- eventMap.set(eventId, {
152
- event,
153
- productNames: [
154
- String(productName)
155
- ]
156
- });
157
- }
158
- }
159
- }
160
- }
161
- const first = eventMap.values().next().value;
162
- if (!first) return {
163
- productNames: []
164
- };
165
- return {
166
- event: first.event,
167
- productNames: first.productNames
168
- };
169
- }
170
- __name(loadPrimaryEvent, "loadPrimaryEvent");
171
- async function loadOrderNotificationContexts(orderId, deps) {
172
- if (!deps.entityMap.orders) return null;
173
- const order = await deps.dataSource.getRepository(deps.entityMap.orders).findOne({
174
- where: {
175
- id: orderId,
176
- deleted: false
177
- },
178
- relations: [
179
- "contact"
180
- ]
181
- });
182
- if (!order) return null;
183
- const o = order;
184
- const contact = o.contact ?? {};
185
- const currency = String(o.currency ?? "INR");
186
- const siteUrl = resolvePublicSiteUrl();
187
- const itemRows = await deps.dataSource.getRepository(deps.entityMap.order_items).find({
188
- where: {
189
- orderId
190
- },
191
- relations: [
192
- "product"
193
- ],
194
- order: {
195
- id: "ASC"
196
- }
197
- });
198
- const orderLines = await loadOrderLines(deps.dataSource, deps.entityMap, orderId, currency);
199
- const { event, productNames: eventProductNames } = await loadPrimaryEvent(deps.dataSource, deps.entityMap, itemRows);
200
- const allProductNames = orderLines.map((line) => line.name);
201
- const productNames = eventProductNames.length > 0 ? eventProductNames.join(", ") : allProductNames.join(", ");
202
- const variables = event ? buildEventOrderTemplateVariables({
203
- event,
204
- order: {
205
- orderNumber: o.orderNumber,
206
- total: o.total,
207
- currency,
208
- qrToken: o.qrToken
209
- },
210
- contact,
211
- productNames,
212
- orderLines,
213
- siteUrl
214
- }) : buildEventOrderTemplateVariables({
215
- event: {
216
- name: productNames || "Your order",
217
- slug: "",
218
- venue: "",
219
- startDate: null
220
- },
221
- order: {
222
- orderNumber: o.orderNumber,
223
- total: o.total,
224
- currency,
225
- qrToken: o.qrToken
226
- },
227
- contact,
228
- productNames,
229
- orderLines,
230
- siteUrl
231
- });
232
- return {
233
- order: {
234
- id: o.id,
235
- orderNumber: String(o.orderNumber ?? ""),
236
- total: o.total,
237
- currency,
238
- qrToken: o.qrToken
239
- },
240
- contact,
241
- orderLines,
242
- productNames,
243
- event,
244
- variables
245
- };
246
- }
247
- __name(loadOrderNotificationContexts, "loadOrderNotificationContexts");
248
-
249
- // src/plugins/whatsapp/whatsapp-queue.ts
250
- var WHATSAPP_QUEUE_NAME = "whatsapp";
251
- function maskPhone(raw) {
252
- const digits = raw.replace(/\D/g, "");
253
- if (digits.length <= 4) return "****";
254
- return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
255
- }
256
- __name(maskPhone, "maskPhone");
257
- function registerWhatsAppQueueProcessor(cms) {
258
- const queue = cms.getPlugin("queue");
259
- const whatsapp = cms.getPlugin("whatsapp");
260
- if (!queue) {
261
- console.warn("[whatsapp] queue processor not registered: queue plugin missing");
262
- return;
263
- }
264
- if (!whatsapp || typeof whatsapp.send !== "function") {
265
- console.warn("[whatsapp] queue processor not registered: whatsapp plugin missing or not configured");
266
- return;
267
- }
268
- queue.registerProcessor(WHATSAPP_QUEUE_NAME, async (data) => {
269
- const payload = data;
270
- if (!payload.to) {
271
- console.warn("[whatsapp] queue job skipped: missing recipient");
272
- return;
273
- }
274
- console.log("[whatsapp] queue job processing", {
275
- to: maskPhone(payload.to),
276
- templateKey: payload.templateKey ?? null,
277
- externalTemplateRef: payload.externalTemplateRef ?? null
278
- });
279
- try {
280
- const ok = await whatsapp.send(payload);
281
- if (ok) {
282
- console.log("[whatsapp] queue job completed", {
283
- to: maskPhone(payload.to),
284
- sent: true
285
- });
286
- } else {
287
- console.warn("[whatsapp] queue job completed without delivery", {
288
- to: maskPhone(payload.to),
289
- sent: false
290
- });
291
- }
292
- } catch (err) {
293
- console.error("[whatsapp] queue job failed", {
294
- to: maskPhone(payload.to),
295
- error: err instanceof Error ? err.message : String(err)
296
- });
297
- }
298
- });
299
- console.log("[whatsapp] queue processor registered", {
300
- queue: WHATSAPP_QUEUE_NAME
301
- });
302
- }
303
- __name(registerWhatsAppQueueProcessor, "registerWhatsAppQueueProcessor");
304
- async function queueWhatsApp(cms, payload) {
305
- const queue = cms.getPlugin("queue");
306
- if (queue) {
307
- console.log("[whatsapp] enqueue", {
308
- to: maskPhone(payload.to),
309
- templateKey: payload.templateKey ?? null,
310
- externalTemplateRef: payload.externalTemplateRef ?? null
311
- });
312
- await queue.add(WHATSAPP_QUEUE_NAME, payload);
313
- return true;
314
- }
315
- const whatsapp = cms.getPlugin("whatsapp");
316
- if (whatsapp && typeof whatsapp.send === "function") {
317
- console.log("[whatsapp] no queue plugin \u2014 sending immediately", {
318
- to: maskPhone(payload.to)
319
- });
320
- return whatsapp.send(payload);
321
- }
322
- console.warn("[whatsapp] send skipped: neither queue nor whatsapp plugin available", {
323
- to: maskPhone(payload.to)
324
- });
325
- return false;
326
- }
327
- __name(queueWhatsApp, "queueWhatsApp");
328
-
329
- // src/plugins/whatsapp/whatsapp-service.ts
330
- function norm(v) {
331
- const t = v?.trim();
332
- return t || void 0;
333
- }
334
- __name(norm, "norm");
335
- function pick(plugin, db, env, ...keys) {
336
- for (const key of keys) {
337
- const pk = key;
338
- if (pk in plugin && plugin[pk] !== void 0 && String(plugin[pk]).trim() !== "") {
339
- const v = norm(String(plugin[pk]));
340
- if (v) return v;
341
- }
342
- if (norm(db[key])) return norm(db[key]);
343
- if (norm(env[key])) return norm(env[key]);
344
- }
345
- return void 0;
346
- }
347
- __name(pick, "pick");
348
- function mergeWhatsAppConfigLayers(env, db, plugin) {
349
- return {
350
- accessToken: pick(plugin, db, env, "accessToken", "WHATSAPP_ACCESS_TOKEN"),
351
- phoneNumberId: pick(plugin, db, env, "phoneNumberId", "WHATSAPP_PHONE_NUMBER_ID"),
352
- apiVersion: pick(plugin, db, env, "apiVersion", "WHATSAPP_API_VERSION") ?? "v21.0"
353
- };
354
- }
355
- __name(mergeWhatsAppConfigLayers, "mergeWhatsAppConfigLayers");
356
- function isWhatsAppPluginEnabled(db) {
357
- const v = (db.enabled ?? "true").toLowerCase();
358
- return v !== "false" && v !== "0";
359
- }
360
- __name(isWhatsAppPluginEnabled, "isWhatsAppPluginEnabled");
361
- function whatsAppConfigured(config) {
362
- return Boolean(config.accessToken?.trim() && config.phoneNumberId?.trim());
363
- }
364
- __name(whatsAppConfigured, "whatsAppConfigured");
365
- function normalizeWhatsAppRecipient(to) {
366
- const digits = to.replace(/\D/g, "");
367
- return digits;
368
- }
369
- __name(normalizeWhatsAppRecipient, "normalizeWhatsAppRecipient");
370
- function resolveMetaWhatsAppTemplateName(opts) {
371
- const ref = opts.externalTemplateRef?.trim();
372
- const rowName = opts.name?.trim();
373
- if (ref && !/^\d+$/.test(ref)) return ref;
374
- if (rowName) return rowName;
375
- return ref || void 0;
376
- }
377
- __name(resolveMetaWhatsAppTemplateName, "resolveMetaWhatsAppTemplateName");
378
- function maskPhone2(digits) {
379
- if (digits.length <= 4) return "****";
380
- return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
381
- }
382
- __name(maskPhone2, "maskPhone");
383
- function logWhatsApp(level, message, extra) {
384
- const line = extra ? `${message} ${JSON.stringify(extra)}` : message;
385
- if (level === "info") console.log(`[whatsapp] ${line}`);
386
- else if (level === "warn") console.warn(`[whatsapp] ${line}`);
387
- else console.error(`[whatsapp] ${line}`);
388
- }
389
- __name(logWhatsApp, "logWhatsApp");
390
- var WhatsAppService = class {
391
- static {
392
- __name(this, "WhatsAppService");
393
- }
394
- getWhatsAppSettings;
395
- config;
396
- getMessageTemplateRow;
397
- constructor(env, plugin = {}, getWhatsAppSettings, getMessageTemplateRow) {
398
- this.getWhatsAppSettings = getWhatsAppSettings;
399
- this.getMessageTemplateRow = getMessageTemplateRow;
400
- this.config = mergeWhatsAppConfigLayers(env, {}, plugin);
401
- }
402
- async mergedConfig() {
403
- let db = {};
404
- try {
405
- db = await this.getWhatsAppSettings?.() ?? {};
406
- } catch {
407
- }
408
- return mergeWhatsAppConfigLayers((typeof process !== "undefined" ? process.env : {}) ?? {}, db, this.config);
409
- }
410
- async send(opts) {
411
- const cfg = await this.mergedConfig();
412
- if (!whatsAppConfigured(cfg)) {
413
- logWhatsApp("warn", "send skipped: not configured (Plugins \u2192 WhatsApp or WHATSAPP_ACCESS_TOKEN + WHATSAPP_PHONE_NUMBER_ID)");
414
- return false;
415
- }
416
- const to = normalizeWhatsAppRecipient(opts.to);
417
- if (!to) {
418
- logWhatsApp("warn", "send skipped: invalid recipient", {
419
- rawTo: opts.to
420
- });
421
- return false;
422
- }
423
- logWhatsApp("info", "send started", {
424
- to: maskPhone2(to),
425
- templateKey: opts.templateKey ?? null,
426
- externalTemplateRef: opts.externalTemplateRef ?? null,
427
- phoneNumberId: cfg.phoneNumberId
428
- });
429
- let templateName = resolveMetaWhatsAppTemplateName({
430
- externalTemplateRef: opts.externalTemplateRef
431
- });
432
- let templateLanguage = opts.templateLanguage?.trim();
433
- let body = opts.body?.trim() ?? "";
434
- const variables = opts.variables ?? {};
435
- if (opts.templateKey?.trim() && this.getMessageTemplateRow) {
436
- const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-SDRNAZHC.js');
437
- if (opts.templateKey.trim() === EVENT_ORDER_TEMPLATE_KEY) {
438
- const resolved = await resolveEventOrderTemplate("whatsapp", async (ch, key) => {
439
- const row = await this.getMessageTemplateRow(ch, key);
440
- if (!row) return null;
441
- return {
442
- subject: null,
443
- body: row.body,
444
- externalTemplateRef: row.externalTemplateRef,
445
- enabled: row.enabled
446
- };
447
- });
448
- if (resolved && !resolved.enabled) {
449
- logWhatsApp("warn", "send skipped: template disabled in message_templates", {
450
- templateKey: opts.templateKey
451
- });
452
- return false;
453
- }
454
- if (resolved) {
455
- body = resolved.body;
456
- if (resolved.externalTemplateRef) templateName = resolved.externalTemplateRef;
457
- }
458
- }
459
- }
460
- const { applyTemplateVars: applyTemplateVars2 } = await import('./interpolate-WDUWDF3I.js');
461
- if (body) body = applyTemplateVars2(body, variables);
462
- if (templateName) {
463
- const paramValues = opts.templateParamOrder?.length ? opts.templateParamOrder.map((key) => String(variables[key] ?? "").slice(0, 1024)) : Object.keys(variables).length ? Object.values(variables) : body ? [
464
- body
465
- ] : [];
466
- logWhatsApp("info", "sending template message", {
467
- to: maskPhone2(to),
468
- templateName,
469
- paramCount: paramValues.length,
470
- paramOrder: opts.templateParamOrder ?? null
471
- });
472
- return this.sendTemplateMessage(cfg, to, templateName, paramValues, templateLanguage);
473
- }
474
- if (!body) {
475
- logWhatsApp("warn", "send skipped: no template name and empty body", {
476
- to: maskPhone2(to)
477
- });
478
- return false;
479
- }
480
- logWhatsApp("info", "sending text message", {
481
- to: maskPhone2(to),
482
- bodyLength: body.length
483
- });
484
- return this.sendTextMessage(cfg, to, body);
485
- }
486
- async sendTextMessage(cfg, to, body) {
487
- const url = `https://graph.facebook.com/${cfg.apiVersion}/${cfg.phoneNumberId}/messages`;
488
- const res = await fetch(url, {
489
- method: "POST",
490
- headers: {
491
- Authorization: `Bearer ${cfg.accessToken}`,
492
- "Content-Type": "application/json"
493
- },
494
- body: JSON.stringify({
495
- messaging_product: "whatsapp",
496
- to,
497
- type: "text",
498
- text: {
499
- body
500
- }
501
- })
502
- });
503
- if (!res.ok) {
504
- const errText = await res.text().catch(() => "");
505
- logWhatsApp("error", "text send failed", {
506
- status: res.status,
507
- to: maskPhone2(to),
508
- error: errText
509
- });
510
- return false;
511
- }
512
- const data = await res.json().catch(() => ({}));
513
- logWhatsApp("info", "text send succeeded", {
514
- to: maskPhone2(to),
515
- messageId: data.messages?.[0]?.id ?? null
516
- });
517
- return true;
518
- }
519
- async sendTemplateMessage(cfg, to, templateName, paramValues, templateLanguage) {
520
- const components = paramValues.length > 0 ? [
521
- {
522
- type: "body",
523
- parameters: paramValues.map((text) => ({
524
- type: "text",
525
- text: String(text).slice(0, 1024)
526
- }))
527
- }
528
- ] : void 0;
529
- const url = `https://graph.facebook.com/${cfg.apiVersion}/${cfg.phoneNumberId}/messages`;
530
- const payload = {
531
- messaging_product: "whatsapp",
532
- to,
533
- type: "template",
534
- template: {
535
- name: templateName,
536
- language: {
537
- code: templateLanguage?.trim() || "en"
538
- },
539
- ...components ? {
540
- components
541
- } : {}
542
- }
543
- };
544
- const res = await fetch(url, {
545
- method: "POST",
546
- headers: {
547
- Authorization: `Bearer ${cfg.accessToken}`,
548
- "Content-Type": "application/json"
549
- },
550
- body: JSON.stringify(payload)
551
- });
552
- if (!res.ok) {
553
- const errText = await res.text().catch(() => "");
554
- logWhatsApp("error", "template send failed", {
555
- status: res.status,
556
- to: maskPhone2(to),
557
- templateName,
558
- paramCount: paramValues.length,
559
- error: errText
560
- });
561
- return false;
562
- }
563
- const data = await res.json().catch(() => ({}));
564
- logWhatsApp("info", "template send succeeded", {
565
- to: maskPhone2(to),
566
- templateName,
567
- messageId: data.messages?.[0]?.id ?? null
568
- });
569
- return true;
570
- }
571
- };
572
-
573
8
  // src/plugins/email/templates/layout.ts
574
9
  function escapeHtml(s) {
575
10
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -649,96 +84,6 @@ function renderLayout(options) {
649
84
  }
650
85
  __name(renderLayout, "renderLayout");
651
86
 
652
- // src/plugins/email/templates/types.ts
653
- function normalizeSocialLinkItem(o) {
654
- const url = String(o.url ?? "").trim();
655
- if (!url) return null;
656
- let iconUrl = String(o.iconUrl ?? o.icon_image ?? "").trim();
657
- let icon = String(o.icon ?? "").trim();
658
- if (!iconUrl && /^https?:\/\//i.test(icon)) {
659
- iconUrl = icon;
660
- icon = "";
661
- }
662
- const item = {
663
- url
664
- };
665
- if (iconUrl) item.iconUrl = iconUrl;
666
- if (icon) item.icon = icon;
667
- return item;
668
- }
669
- __name(normalizeSocialLinkItem, "normalizeSocialLinkItem");
670
- function parseSocialLinksJson(raw) {
671
- if (raw == null || raw.trim() === "") return void 0;
672
- try {
673
- const parsed = JSON.parse(raw);
674
- if (!Array.isArray(parsed)) return void 0;
675
- const out = [];
676
- for (const item of parsed) {
677
- if (item && typeof item === "object" && "url" in item) {
678
- const n = normalizeSocialLinkItem(item);
679
- if (n) out.push(n);
680
- }
681
- }
682
- return out.length ? out : void 0;
683
- } catch {
684
- return void 0;
685
- }
686
- }
687
- __name(parseSocialLinksJson, "parseSocialLinksJson");
688
- function mergeEmailLayoutCompanyDetails(branding, emailSettings) {
689
- const fromBranding = getCompanyDetailsFromSettings(branding);
690
- const pick2 = /* @__PURE__ */ __name((emailVal, fallback) => {
691
- const t = emailVal?.trim();
692
- return t || fallback?.trim() || void 0;
693
- }, "pick");
694
- const logoUrl = pick2(emailSettings.logoUrl ?? emailSettings.emailLogoUrl, fromBranding.logoUrl);
695
- const companyName = pick2(emailSettings.companyName ?? emailSettings.emailCompanyName, fromBranding.companyName);
696
- const supportEmail = pick2(emailSettings.supportEmail ?? emailSettings.emailSupportEmail, fromBranding.supportEmail);
697
- const supportPhone = pick2(emailSettings.supportPhone, void 0);
698
- const footerDisclaimer = pick2(emailSettings.footerDisclaimer, void 0);
699
- const followUsTitle = pick2(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
700
- const socialFromEmail = parseSocialLinksJson(emailSettings.socialLinks);
701
- const socialLinks = socialFromEmail?.length ? socialFromEmail : fromBranding.socialLinks;
702
- return {
703
- logoUrl,
704
- companyName,
705
- supportEmail,
706
- supportPhone,
707
- socialLinks,
708
- footerDisclaimer,
709
- followUsTitle
710
- };
711
- }
712
- __name(mergeEmailLayoutCompanyDetails, "mergeEmailLayoutCompanyDetails");
713
- function getCompanyDetailsFromSettings(settingsGroup) {
714
- const logoUrl = settingsGroup.logo ?? settingsGroup.logoUrl ?? "";
715
- const companyName = settingsGroup.companyName ?? settingsGroup.company_name ?? "";
716
- const supportEmail = settingsGroup.supportEmail ?? settingsGroup.support_email ?? "";
717
- let socialLinks = [];
718
- const raw = settingsGroup.socialLinks ?? settingsGroup.social_links;
719
- if (typeof raw === "string") {
720
- try {
721
- const arr = JSON.parse(raw);
722
- if (Array.isArray(arr)) {
723
- for (const item of arr) {
724
- if (item && typeof item === "object") {
725
- const n = normalizeSocialLinkItem(item);
726
- if (n) socialLinks.push(n);
727
- }
728
- }
729
- }
730
- } catch {
731
- }
732
- }
733
- return {
734
- logoUrl: logoUrl || void 0,
735
- companyName: companyName || void 0,
736
- supportEmail: supportEmail || void 0,
737
- socialLinks: socialLinks.length ? socialLinks : void 0
738
- };
739
- }
740
- __name(getCompanyDetailsFromSettings, "getCompanyDetailsFromSettings");
741
-
742
87
  // src/plugins/email/templates/inline-cta.ts
743
88
  function escapeHtml2(s) {
744
89
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -943,7 +288,7 @@ function renderAddressesText(billing, shipping) {
943
288
  }
944
289
  __name(renderAddressesText, "renderAddressesText");
945
290
  function render4(ctx) {
946
- const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress } = ctx;
291
+ const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress, invoiceAttached = false } = ctx;
947
292
  const itemsHtml = renderLineItemsHtml(lineItems, currency);
948
293
  const itemsText = renderLineItemsText(lineItems, currency);
949
294
  const addrHtml = renderAddressesHtml(billingAddress, shippingAddress);
@@ -982,6 +327,7 @@ ${subtotalLine}${taxLine}${totalLine}`;
982
327
  ${addrHtml}
983
328
  ${itemsHtml}
984
329
  ${subtotalLine}${taxLine}${totalLine}
330
+ ${invoiceAttached ? `<p style="margin:16px 0 0 0;font-size:14px;line-height:1.5;color:#333;">Your order invoice is attached as a PDF.</p>` : ""}
985
331
  <p style="margin:16px 0 0 0;font-size:13px;line-height:1.5;color:#666;">If you have questions, reply to this email or contact us using the details below.</p>`;
986
332
  text = [
987
333
  `Order confirmed #${orderNumber}`,
@@ -992,6 +338,7 @@ ${subtotalLine}${taxLine}${totalLine}
992
338
  subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
993
339
  tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
994
340
  total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : "",
341
+ invoiceAttached ? "Your order invoice is attached as a PDF." : "",
995
342
  "",
996
343
  "We will process your order shortly."
997
344
  ].filter((line, i, arr) => !(line === "" && arr[i - 1] === "")).join("\n");
@@ -1318,8 +665,8 @@ function renderEmail(templateName, ctx, options) {
1318
665
  return render12(ctx);
1319
666
  }
1320
667
  __name(renderEmail, "renderEmail");
1321
-
1322
- // src/plugins/email/email-service.ts
668
+ var require2 = createRequire(import.meta.url);
669
+ var MailComposer = require2("nodemailer/lib/mail-composer");
1323
670
  var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
1324
671
  "af-south-1",
1325
672
  "ap-south-2",
@@ -1340,6 +687,10 @@ function sesRegionSupportsSmtp(region) {
1340
687
  return !SES_REGIONS_WITHOUT_SMTP.has(region.trim());
1341
688
  }
1342
689
  __name(sesRegionSupportsSmtp, "sesRegionSupportsSmtp");
690
+ function toBuffer(content) {
691
+ return Buffer.isBuffer(content) ? content : Buffer.from(content);
692
+ }
693
+ __name(toBuffer, "toBuffer");
1343
694
  var EmailService = class {
1344
695
  static {
1345
696
  __name(this, "EmailService");
@@ -1403,14 +754,47 @@ var EmailService = class {
1403
754
  throw new Error(`Unsupported email type: ${config.type}`);
1404
755
  }
1405
756
  }
757
+ nodemailerAttachments(attachments) {
758
+ if (!attachments?.length) return void 0;
759
+ return attachments.map((a) => ({
760
+ filename: a.filename,
761
+ content: toBuffer(a.content),
762
+ contentType: a.contentType || "application/octet-stream"
763
+ }));
764
+ }
1406
765
  async send(emailData) {
1407
766
  try {
767
+ const from = emailData.from || this.config.from;
768
+ const to = emailData.to || this.config.to;
769
+ const attachments = this.nodemailerAttachments(emailData.attachments);
1408
770
  if (this.config.type === "AWS" && this.sesClient) {
771
+ if (attachments?.length) {
772
+ const composer = new MailComposer({
773
+ from,
774
+ to,
775
+ subject: emailData.subject,
776
+ html: emailData.html,
777
+ text: emailData.text,
778
+ attachments
779
+ });
780
+ const raw = await new Promise((resolve, reject) => {
781
+ composer.compile().build((err, message) => {
782
+ if (err) reject(err);
783
+ else resolve(message);
784
+ });
785
+ });
786
+ await this.sesClient.send(new SendRawEmailCommand({
787
+ RawMessage: {
788
+ Data: raw
789
+ }
790
+ }));
791
+ return true;
792
+ }
1409
793
  await this.sesClient.send(new SendEmailCommand({
1410
- Source: emailData.from || this.config.from,
794
+ Source: from,
1411
795
  Destination: {
1412
796
  ToAddresses: [
1413
- emailData.to || this.config.to
797
+ to
1414
798
  ]
1415
799
  },
1416
800
  Message: {
@@ -1436,11 +820,12 @@ var EmailService = class {
1436
820
  }
1437
821
  if (this.transporter && (this.config.type === "SMTP" || this.config.type === "GMAIL" || this.config.type === "AWS" && this.awsViaSmtp)) {
1438
822
  await this.transporter.sendMail({
1439
- from: emailData.from || this.config.from,
1440
- to: emailData.to || this.config.to,
823
+ from,
824
+ to,
1441
825
  subject: emailData.subject,
1442
826
  html: emailData.html,
1443
- text: emailData.text
827
+ text: emailData.text,
828
+ attachments
1444
829
  });
1445
830
  return true;
1446
831
  }
@@ -1646,8 +1031,8 @@ function resolveChatEmailToolSettings(configMap, sources) {
1646
1031
  __name(resolveChatEmailToolSettings, "resolveChatEmailToolSettings");
1647
1032
  function intentByKey(intents, key) {
1648
1033
  if (!key) return null;
1649
- const norm2 = normalizeIntentKey(key);
1650
- return intents.find((i) => i.intent === norm2) ?? null;
1034
+ const norm = normalizeIntentKey(key);
1035
+ return intents.find((i) => i.intent === norm) ?? null;
1651
1036
  }
1652
1037
  __name(intentByKey, "intentByKey");
1653
1038
  function buildIntentTableForPrompt(intents) {
@@ -2033,293 +1418,4 @@ function emailPlugin(config) {
2033
1418
  }
2034
1419
  __name(emailPlugin, "emailPlugin");
2035
1420
 
2036
- // src/lib/order-notification-dispatcher.ts
2037
- var initialized = false;
2038
- function maskPhone3(raw) {
2039
- const digits = String(raw ?? "").replace(/\D/g, "");
2040
- if (digits.length <= 4) return "(none)";
2041
- return `***${digits.slice(-4)}`;
2042
- }
2043
- __name(maskPhone3, "maskPhone");
2044
- async function getSettingsGroup(dataSource, entityMap, group) {
2045
- if (!entityMap.configs) return {};
2046
- const rows = await dataSource.getRepository(entityMap.configs).find({
2047
- where: {
2048
- settings: group,
2049
- deleted: false
2050
- }
2051
- });
2052
- return Object.fromEntries(rows.map((r) => [
2053
- r.key,
2054
- r.value
2055
- ]));
2056
- }
2057
- __name(getSettingsGroup, "getSettingsGroup");
2058
- async function isOrderNotificationsEnabled(dataSource, entityMap) {
2059
- const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2060
- return settings.enabled !== "false" && settings.enabled !== "0";
2061
- }
2062
- __name(isOrderNotificationsEnabled, "isOrderNotificationsEnabled");
2063
- async function isTriggerCatalogEnabled(triggerKey, dataSource, entityMap) {
2064
- if (!entityMap.order_notification_triggers) {
2065
- console.warn("[order-notifications] order_notification_triggers missing \u2014 run migration 1781230000000-AddOrderNotificationTriggers");
2066
- return false;
2067
- }
2068
- try {
2069
- const row = await dataSource.getRepository(entityMap.order_notification_triggers).findOne({
2070
- where: {
2071
- triggerKey,
2072
- enabled: true
2073
- }
2074
- });
2075
- if (!row) {
2076
- console.warn("[order-notifications] trigger disabled or not in DB", {
2077
- triggerKey
2078
- });
2079
- }
2080
- return Boolean(row);
2081
- } catch (err) {
2082
- console.error("[order-notifications] failed to load trigger catalog", {
2083
- triggerKey,
2084
- err
2085
- });
2086
- return false;
2087
- }
2088
- }
2089
- __name(isTriggerCatalogEnabled, "isTriggerCatalogEnabled");
2090
- async function loadBoundTemplate(triggerKey, channel, dataSource, entityMap) {
2091
- if (!entityMap.order_notification_bindings || !entityMap.message_templates) {
2092
- console.warn("[order-notifications] order_notification_bindings or message_templates missing from entityMap");
2093
- return null;
2094
- }
2095
- const binding = await dataSource.getRepository(entityMap.order_notification_bindings).findOne({
2096
- where: {
2097
- triggerKey,
2098
- channel,
2099
- enabled: true
2100
- }
2101
- });
2102
- if (!binding) {
2103
- console.warn("[order-notifications] no order_notification_bindings row", {
2104
- triggerKey,
2105
- channel
2106
- });
2107
- return null;
2108
- }
2109
- const templateId = binding.messageTemplateId;
2110
- if (templateId == null) {
2111
- console.warn("[order-notifications] binding exists but no template selected", {
2112
- triggerKey,
2113
- channel
2114
- });
2115
- return null;
2116
- }
2117
- const template = await dataSource.getRepository(entityMap.message_templates).findOne({
2118
- where: {
2119
- id: templateId,
2120
- channel,
2121
- deleted: false,
2122
- enabled: true
2123
- }
2124
- });
2125
- if (!template) {
2126
- console.warn("[order-notifications] bound template not found or disabled", {
2127
- triggerKey,
2128
- channel,
2129
- templateId
2130
- });
2131
- return null;
2132
- }
2133
- return template;
2134
- }
2135
- __name(loadBoundTemplate, "loadBoundTemplate");
2136
- function mergeTriggerVariables(payload, rich) {
2137
- const basic = {
2138
- orderId: String(payload.orderId ?? ""),
2139
- orderNumber: payload.orderNumber,
2140
- customerName: payload.customerName,
2141
- customerPhone: payload.customerPhone ?? "",
2142
- total: String(payload.total),
2143
- orderTotal: String(payload.total),
2144
- currency: payload.currency,
2145
- vendorId: String(payload.vendorId ?? ""),
2146
- ...payload.refundAmount != null ? {
2147
- refundAmount: String(payload.refundAmount)
2148
- } : {}
2149
- };
2150
- return {
2151
- ...basic,
2152
- ...rich ?? {}
2153
- };
2154
- }
2155
- __name(mergeTriggerVariables, "mergeTriggerVariables");
2156
- async function sendEmailForTrigger(triggerKey, orderId, variables, contactEmail, dataSource, entityMap, getCms) {
2157
- const template = await loadBoundTemplate(triggerKey, "email", dataSource, entityMap);
2158
- if (!template) return;
2159
- const [branding, emailSettings] = await Promise.all([
2160
- getSettingsGroup(dataSource, entityMap, "branding"),
2161
- getSettingsGroup(dataSource, entityMap, "email")
2162
- ]);
2163
- const companyDetails = mergeEmailLayoutCompanyDetails(branding, emailSettings);
2164
- const subject = applyTemplateVars(template.subject ?? "Order update", variables);
2165
- const bodyHtml = applyTemplateVars(template.body, variables);
2166
- const html = renderLayout({
2167
- bodyHtml,
2168
- companyDetails
2169
- });
2170
- const cms = await getCms();
2171
- await queueEmail(cms, {
2172
- to: contactEmail,
2173
- subject,
2174
- html,
2175
- text: bodyHtml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
2176
- });
2177
- console.log("[order-notifications] email queued", {
2178
- triggerKey,
2179
- orderId,
2180
- to: contactEmail
2181
- });
2182
- }
2183
- __name(sendEmailForTrigger, "sendEmailForTrigger");
2184
- async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dataSource, entityMap, getCms) {
2185
- const template = await loadBoundTemplate(triggerKey, "whatsapp", dataSource, entityMap);
2186
- if (!template) return;
2187
- const paramOrder = template.providerMeta?.paramOrder ?? [];
2188
- if (paramOrder.length === 0) {
2189
- console.warn("[order-notifications] WhatsApp template has no paramOrder set \u2014 variables may send blank/wrong", {
2190
- triggerKey
2191
- });
2192
- }
2193
- const cms = await getCms();
2194
- const metaTemplateName = resolveMetaWhatsAppTemplateName({
2195
- externalTemplateRef: template.externalTemplateRef,
2196
- name: template.name
2197
- });
2198
- if (!metaTemplateName) {
2199
- console.warn("[order-notifications] bound WhatsApp template has no Meta template name", {
2200
- triggerKey,
2201
- templateId: template.id
2202
- });
2203
- return;
2204
- }
2205
- const sent = await queueWhatsApp(cms, {
2206
- to: phone.trim(),
2207
- templateKey: triggerKey,
2208
- externalTemplateRef: metaTemplateName,
2209
- templateLanguage: template.providerMeta?.language ?? void 0,
2210
- body: template.body,
2211
- variables,
2212
- templateParamOrder: paramOrder
2213
- });
2214
- if (!sent) {
2215
- console.warn("[order-notifications] WhatsApp send failed", {
2216
- triggerKey,
2217
- orderId,
2218
- hint: "Register whatsappPlugin() + queuePlugin() in getCms(), or set WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID"
2219
- });
2220
- } else {
2221
- console.log("[order-notifications] WhatsApp queued/sent", {
2222
- triggerKey,
2223
- orderId,
2224
- phone: maskPhone3(phone)
2225
- });
2226
- }
2227
- }
2228
- __name(sendWhatsAppForTrigger, "sendWhatsAppForTrigger");
2229
- async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms) {
2230
- console.log("[order-notifications] received", {
2231
- triggerKey,
2232
- orderId: payload.orderId,
2233
- phone: maskPhone3(payload.customerPhone)
2234
- });
2235
- const pluginEnabled = await isOrderNotificationsEnabled(dataSource, entityMap);
2236
- if (!pluginEnabled) {
2237
- console.warn("[order-notifications] skip: Plugins \u2192 Order notifications disabled");
2238
- return;
2239
- }
2240
- const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, dataSource, entityMap);
2241
- if (!triggerEnabled) return;
2242
- const context = await loadOrderNotificationContexts(payload.orderId, {
2243
- dataSource,
2244
- entityMap
2245
- });
2246
- const variables = mergeTriggerVariables(payload, context?.variables);
2247
- const contactEmail = context?.contact.email?.trim() || "";
2248
- const contactPhone = context?.contact.phone?.trim() || payload.customerPhone?.trim() || "";
2249
- if (contactEmail) {
2250
- await sendEmailForTrigger(triggerKey, payload.orderId, variables, contactEmail, dataSource, entityMap, getCms);
2251
- } else {
2252
- console.warn("[order-notifications] skip email: no customer email", {
2253
- triggerKey,
2254
- orderId: payload.orderId
2255
- });
2256
- }
2257
- if (contactPhone) {
2258
- await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
2259
- } else {
2260
- console.warn("[order-notifications] skip WhatsApp: no customer phone", {
2261
- triggerKey,
2262
- orderId: payload.orderId
2263
- });
2264
- }
2265
- }
2266
- __name(handleTrigger, "handleTrigger");
2267
- function initWhatsappTriggerDispatcher(deps) {
2268
- if (initialized) return;
2269
- initialized = true;
2270
- const { dataSource, entityMap, getCms } = deps;
2271
- notificationTriggerEmitter.onAnyTrigger((triggerKey, payload) => {
2272
- void handleTrigger(triggerKey, payload, dataSource, entityMap, getCms).catch((err) => {
2273
- console.error("[order-notifications]", triggerKey, err);
2274
- });
2275
- });
2276
- console.log("[order-notifications] dispatcher initialized (email + WhatsApp, unified via order_notification_bindings)");
2277
- }
2278
- __name(initWhatsappTriggerDispatcher, "initWhatsappTriggerDispatcher");
2279
- async function resendOrderNotification(triggerKey, orderId, deps) {
2280
- if (!deps.entityMap.orders) return {
2281
- ok: false,
2282
- error: "orders entity missing"
2283
- };
2284
- const pluginEnabled = await isOrderNotificationsEnabled(deps.dataSource, deps.entityMap);
2285
- if (!pluginEnabled) return {
2286
- ok: false,
2287
- error: "Order notifications plugin is disabled"
2288
- };
2289
- const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, deps.dataSource, deps.entityMap);
2290
- if (!triggerEnabled) {
2291
- return {
2292
- ok: false,
2293
- error: `Trigger "${triggerKey}" is disabled or migration 1781230000000 not applied`
2294
- };
2295
- }
2296
- const order = await deps.dataSource.getRepository(deps.entityMap.orders).findOne({
2297
- where: {
2298
- id: orderId,
2299
- deleted: false
2300
- },
2301
- relations: [
2302
- "contact"
2303
- ]
2304
- });
2305
- if (!order) return {
2306
- ok: false,
2307
- error: "Order not found"
2308
- };
2309
- const o = order;
2310
- await handleTrigger(triggerKey, {
2311
- orderId,
2312
- orderNumber: String(o.orderNumber ?? ""),
2313
- customerName: o.contact?.name ?? "",
2314
- customerPhone: o.contact?.phone ?? null,
2315
- total: o.total ?? 0,
2316
- currency: String(o.currency ?? "INR"),
2317
- vendorId: o.vendorId ?? null
2318
- }, deps.dataSource, deps.entityMap, deps.getCms);
2319
- return {
2320
- ok: true
2321
- };
2322
- }
2323
- __name(resendOrderNotification, "resendOrderNotification");
2324
-
2325
- export { CHAT_EMAIL_LOG, EmailService, WhatsAppService, buildChatbotSystemPromptWithEmailTool, buildEventOrderTemplateVariables, buildTranscriptForLeadEmail, detectChatLeadIntent, emailPlugin, emailTemplates, getCompanyDetailsFromSettings, initWhatsappTriggerDispatcher, isWhatsAppPluginEnabled, joinRecipientsForSend, mergeEmailLayoutCompanyDetails, mergeWhatsAppConfigLayers, parseEmailRecipientsFromConfig, parseIntentRecipientEmails, queueWhatsApp, registerWhatsAppQueueProcessor, renderEmail, renderLayout, resendOrderNotification, resolveChatEmailToolSettings, sendChatLeadEmail, serializeEmailRecipients, whatsAppConfigured };
1421
+ export { CHAT_EMAIL_LOG, EmailService, buildChatbotSystemPromptWithEmailTool, buildTranscriptForLeadEmail, detectChatLeadIntent, emailPlugin, emailTemplates, joinRecipientsForSend, parseEmailRecipientsFromConfig, parseIntentRecipientEmails, renderEmail, renderLayout, resolveChatEmailToolSettings, sendChatLeadEmail, serializeEmailRecipients };