@infuro/cms-core 1.0.60 → 1.0.62
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.
- package/dist/admin.cjs +4179 -2665
- package/dist/admin.js +2990 -1476
- package/dist/api.cjs +41 -37
- package/dist/api.js +9 -5
- package/dist/{chunk-URN7GS23.js → chunk-6LV36PCL.js} +199 -1059
- package/dist/{chunk-O2642WVS.js → chunk-6XAVZE4I.js} +17 -3
- package/dist/{chunk-BXYZDMTZ.cjs → chunk-AXMP2U43.cjs} +3 -94
- package/dist/{chunk-T3HPSF7K.js → chunk-AYWDQFJZ.js} +3 -0
- package/dist/chunk-CTIEAHQL.cjs +96 -0
- package/dist/chunk-GWDI752Q.js +83 -0
- package/dist/chunk-GZGFNVTK.js +886 -0
- package/dist/chunk-H3IVLIPR.cjs +86 -0
- package/dist/{chunk-2KUCQVAQ.js → chunk-HMLVPIJR.js} +2 -91
- package/dist/{chunk-V25RDUOU.cjs → chunk-LRD4CGOG.cjs} +38 -4
- package/dist/{chunk-73WVHINE.cjs → chunk-LVQSG25Y.cjs} +18 -4
- package/dist/chunk-NMYGQWHV.js +93 -0
- package/dist/chunk-OVTOP42W.js +199 -0
- package/dist/{chunk-BSLNXKV2.cjs → chunk-QA5C477U.cjs} +927 -351
- package/dist/{chunk-JAEUYYGA.js → chunk-R6Q67HAS.js} +876 -291
- package/dist/{chunk-YSYX4GUD.cjs → chunk-TWTISTZB.cjs} +210 -1080
- package/dist/{chunk-77I3KS7V.cjs → chunk-UEE4PTTB.cjs} +3 -0
- package/dist/chunk-WFMVD567.cjs +209 -0
- package/dist/{chunk-L3525VXI.js → chunk-YW7EF7K7.js} +36 -2
- package/dist/chunk-ZPAPCGCR.cjs +899 -0
- package/dist/{email-queue-OI2HSCGE.js → email-queue-65PQAG4C.js} +3 -2
- package/dist/{email-queue-6NJY6HNM.cjs → email-queue-J7DXYNCE.cjs} +7 -6
- package/dist/email-service-F7HA4X63.js +3 -0
- package/dist/email-service-RDOTY3CO.cjs +28 -0
- package/dist/{emit-order-notification-trigger-TNEZOYEE.cjs → emit-order-notification-trigger-K7URAZHJ.cjs} +4 -4
- package/dist/{emit-order-notification-trigger-CZU3V2WE.js → emit-order-notification-trigger-MD7EBODC.js} +2 -2
- package/dist/{erp-order-invoice-EYYNR526.cjs → erp-order-invoice-C5GZCOBM.cjs} +6 -5
- package/dist/{erp-order-invoice-3GXDE443.js → erp-order-invoice-MYFCXOCY.js} +2 -1
- package/dist/index.cjs +638 -346
- package/dist/index.d.cts +53 -19
- package/dist/index.d.ts +53 -19
- package/dist/index.js +350 -58
- package/dist/migrations/1783000000000-AddOrderPendingProcessingCompletedTriggers.ts +56 -0
- package/dist/migrations/1783100000000-AddSmsToOrderNotificationBindingsChannelEnum.ts +17 -0
- package/dist/migrations/1783200000000-AddOrderAssigneeColumns.ts +30 -0
- package/dist/order-assignees-handlers-LKMAGLG6.js +150 -0
- package/dist/order-assignees-handlers-NPKD64P6.cjs +152 -0
- package/dist/order-completion-otp-handlers-3NTOGEA5.js +361 -0
- package/dist/order-completion-otp-handlers-4EOLYLAP.cjs +363 -0
- package/dist/order-notification-dispatcher-IYMOS4VY.js +10 -0
- package/dist/order-notification-dispatcher-X2XM5VUY.cjs +35 -0
- package/dist/sms-notification-templates-handlers-SJLO7JGZ.js +47 -0
- package/dist/sms-notification-templates-handlers-YSB3L2PK.cjs +49 -0
- package/package.json +1 -1
- package/dist/order-notification-dispatcher-KJVGLK5E.cjs +0 -24
- package/dist/order-notification-dispatcher-ZSFKEYMP.js +0 -7
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
|
|
4
|
+
|
|
5
|
+
// src/plugins/whatsapp/whatsapp-queue.ts
|
|
6
|
+
var WHATSAPP_QUEUE_NAME = "whatsapp";
|
|
7
|
+
function maskPhone(raw) {
|
|
8
|
+
const digits = raw.replace(/\D/g, "");
|
|
9
|
+
if (digits.length <= 4) return "****";
|
|
10
|
+
return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
|
|
11
|
+
}
|
|
12
|
+
chunkUSNT2KNT_cjs.__name(maskPhone, "maskPhone");
|
|
13
|
+
function registerWhatsAppQueueProcessor(cms) {
|
|
14
|
+
const queue = cms.getPlugin("queue");
|
|
15
|
+
const whatsapp = cms.getPlugin("whatsapp");
|
|
16
|
+
if (!queue) {
|
|
17
|
+
console.warn("[whatsapp] queue processor not registered: queue plugin missing");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!whatsapp || typeof whatsapp.send !== "function") {
|
|
21
|
+
console.warn("[whatsapp] queue processor not registered: whatsapp plugin missing or not configured");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
queue.registerProcessor(WHATSAPP_QUEUE_NAME, async (data) => {
|
|
25
|
+
const payload = data;
|
|
26
|
+
if (!payload.to) {
|
|
27
|
+
console.warn("[whatsapp] queue job skipped: missing recipient");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log("[whatsapp] queue job processing", {
|
|
31
|
+
to: maskPhone(payload.to),
|
|
32
|
+
templateKey: payload.templateKey ?? null,
|
|
33
|
+
externalTemplateRef: payload.externalTemplateRef ?? null
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
const ok = await whatsapp.send(payload);
|
|
37
|
+
if (ok) {
|
|
38
|
+
console.log("[whatsapp] queue job completed", {
|
|
39
|
+
to: maskPhone(payload.to),
|
|
40
|
+
sent: true
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
console.warn("[whatsapp] queue job completed without delivery", {
|
|
44
|
+
to: maskPhone(payload.to),
|
|
45
|
+
sent: false
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error("[whatsapp] queue job failed", {
|
|
50
|
+
to: maskPhone(payload.to),
|
|
51
|
+
error: err instanceof Error ? err.message : String(err)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
console.log("[whatsapp] queue processor registered", {
|
|
56
|
+
queue: WHATSAPP_QUEUE_NAME
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
chunkUSNT2KNT_cjs.__name(registerWhatsAppQueueProcessor, "registerWhatsAppQueueProcessor");
|
|
60
|
+
async function queueWhatsApp(cms, payload) {
|
|
61
|
+
const queue = cms.getPlugin("queue");
|
|
62
|
+
if (queue) {
|
|
63
|
+
console.log("[whatsapp] enqueue", {
|
|
64
|
+
to: maskPhone(payload.to),
|
|
65
|
+
templateKey: payload.templateKey ?? null,
|
|
66
|
+
externalTemplateRef: payload.externalTemplateRef ?? null
|
|
67
|
+
});
|
|
68
|
+
await queue.add(WHATSAPP_QUEUE_NAME, payload);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const whatsapp = cms.getPlugin("whatsapp");
|
|
72
|
+
if (whatsapp && typeof whatsapp.send === "function") {
|
|
73
|
+
console.log("[whatsapp] no queue plugin \u2014 sending immediately", {
|
|
74
|
+
to: maskPhone(payload.to)
|
|
75
|
+
});
|
|
76
|
+
return whatsapp.send(payload);
|
|
77
|
+
}
|
|
78
|
+
console.warn("[whatsapp] send skipped: neither queue nor whatsapp plugin available", {
|
|
79
|
+
to: maskPhone(payload.to)
|
|
80
|
+
});
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
chunkUSNT2KNT_cjs.__name(queueWhatsApp, "queueWhatsApp");
|
|
84
|
+
|
|
85
|
+
exports.queueWhatsApp = queueWhatsApp;
|
|
86
|
+
exports.registerWhatsAppQueueProcessor = registerWhatsAppQueueProcessor;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
|
|
2
|
+
import { mergeEmailLayoutCompanyDetails, getCompanyDetailsFromSettings } from './chunk-NMYGQWHV.js';
|
|
2
3
|
import { __name } from './chunk-SHUYVCID.js';
|
|
3
4
|
import { rgb, PDFDocument, StandardFonts } from 'pdf-lib';
|
|
4
5
|
|
|
@@ -583,96 +584,6 @@ function resolveInvoiceAssetUrl(raw) {
|
|
|
583
584
|
}
|
|
584
585
|
__name(resolveInvoiceAssetUrl, "resolveInvoiceAssetUrl");
|
|
585
586
|
|
|
586
|
-
// src/plugins/email/templates/types.ts
|
|
587
|
-
function normalizeSocialLinkItem(o) {
|
|
588
|
-
const url = String(o.url ?? "").trim();
|
|
589
|
-
if (!url) return null;
|
|
590
|
-
let iconUrl = String(o.iconUrl ?? o.icon_image ?? "").trim();
|
|
591
|
-
let icon = String(o.icon ?? "").trim();
|
|
592
|
-
if (!iconUrl && /^https?:\/\//i.test(icon)) {
|
|
593
|
-
iconUrl = icon;
|
|
594
|
-
icon = "";
|
|
595
|
-
}
|
|
596
|
-
const item = {
|
|
597
|
-
url
|
|
598
|
-
};
|
|
599
|
-
if (iconUrl) item.iconUrl = iconUrl;
|
|
600
|
-
if (icon) item.icon = icon;
|
|
601
|
-
return item;
|
|
602
|
-
}
|
|
603
|
-
__name(normalizeSocialLinkItem, "normalizeSocialLinkItem");
|
|
604
|
-
function parseSocialLinksJson(raw) {
|
|
605
|
-
if (raw == null || raw.trim() === "") return void 0;
|
|
606
|
-
try {
|
|
607
|
-
const parsed = JSON.parse(raw);
|
|
608
|
-
if (!Array.isArray(parsed)) return void 0;
|
|
609
|
-
const out = [];
|
|
610
|
-
for (const item of parsed) {
|
|
611
|
-
if (item && typeof item === "object" && "url" in item) {
|
|
612
|
-
const n = normalizeSocialLinkItem(item);
|
|
613
|
-
if (n) out.push(n);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
return out.length ? out : void 0;
|
|
617
|
-
} catch {
|
|
618
|
-
return void 0;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
__name(parseSocialLinksJson, "parseSocialLinksJson");
|
|
622
|
-
function mergeEmailLayoutCompanyDetails(branding, emailSettings) {
|
|
623
|
-
const fromBranding = getCompanyDetailsFromSettings(branding);
|
|
624
|
-
const pick = /* @__PURE__ */ __name((emailVal, fallback) => {
|
|
625
|
-
const t = emailVal?.trim();
|
|
626
|
-
return t || fallback?.trim() || void 0;
|
|
627
|
-
}, "pick");
|
|
628
|
-
const logoUrl = pick(emailSettings.logoUrl ?? emailSettings.emailLogoUrl, fromBranding.logoUrl);
|
|
629
|
-
const companyName = pick(emailSettings.companyName ?? emailSettings.emailCompanyName, fromBranding.companyName);
|
|
630
|
-
const supportEmail = pick(emailSettings.supportEmail ?? emailSettings.emailSupportEmail, fromBranding.supportEmail);
|
|
631
|
-
const supportPhone = pick(emailSettings.supportPhone, void 0);
|
|
632
|
-
const footerDisclaimer = pick(emailSettings.footerDisclaimer, void 0);
|
|
633
|
-
const followUsTitle = pick(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
|
|
634
|
-
const socialFromEmail = parseSocialLinksJson(emailSettings.socialLinks);
|
|
635
|
-
const socialLinks = socialFromEmail?.length ? socialFromEmail : fromBranding.socialLinks;
|
|
636
|
-
return {
|
|
637
|
-
logoUrl,
|
|
638
|
-
companyName,
|
|
639
|
-
supportEmail,
|
|
640
|
-
supportPhone,
|
|
641
|
-
socialLinks,
|
|
642
|
-
footerDisclaimer,
|
|
643
|
-
followUsTitle
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
__name(mergeEmailLayoutCompanyDetails, "mergeEmailLayoutCompanyDetails");
|
|
647
|
-
function getCompanyDetailsFromSettings(settingsGroup) {
|
|
648
|
-
const logoUrl = settingsGroup.logo ?? settingsGroup.logoUrl ?? "";
|
|
649
|
-
const companyName = settingsGroup.companyName ?? settingsGroup.company_name ?? "";
|
|
650
|
-
const supportEmail = settingsGroup.supportEmail ?? settingsGroup.support_email ?? "";
|
|
651
|
-
let socialLinks = [];
|
|
652
|
-
const raw = settingsGroup.socialLinks ?? settingsGroup.social_links;
|
|
653
|
-
if (typeof raw === "string") {
|
|
654
|
-
try {
|
|
655
|
-
const arr = JSON.parse(raw);
|
|
656
|
-
if (Array.isArray(arr)) {
|
|
657
|
-
for (const item of arr) {
|
|
658
|
-
if (item && typeof item === "object") {
|
|
659
|
-
const n = normalizeSocialLinkItem(item);
|
|
660
|
-
if (n) socialLinks.push(n);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
} catch {
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
return {
|
|
668
|
-
logoUrl: logoUrl || void 0,
|
|
669
|
-
companyName: companyName || void 0,
|
|
670
|
-
supportEmail: supportEmail || void 0,
|
|
671
|
-
socialLinks: socialLinks.length ? socialLinks : void 0
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
__name(getCompanyDetailsFromSettings, "getCompanyDetailsFromSettings");
|
|
675
|
-
|
|
676
587
|
// src/lib/invoice/build-local-invoice-for-order.ts
|
|
677
588
|
async function loadSettingsGroup(ds, entityMap, group) {
|
|
678
589
|
if (!entityMap.configs) return {};
|
|
@@ -933,4 +844,4 @@ async function streamOrderInvoicePdf(cms, dataSource, entityMap, orderId, option
|
|
|
933
844
|
}
|
|
934
845
|
__name(streamOrderInvoicePdf, "streamOrderInvoicePdf");
|
|
935
846
|
|
|
936
|
-
export { INVOICE_TEMPLATE_IDS, INVOICE_TEMPLATE_OPTIONS, ORDER_INVOICE_EMAIL_ELIGIBLE_STATUSES, buildLocalInvoicePdfForOrder, extractChildOrderRefsFromSalePayload, generateLocalInvoicePdf,
|
|
847
|
+
export { INVOICE_TEMPLATE_IDS, INVOICE_TEMPLATE_OPTIONS, ORDER_INVOICE_EMAIL_ELIGIBLE_STATUSES, buildLocalInvoicePdfForOrder, extractChildOrderRefsFromSalePayload, generateLocalInvoicePdf, isOrderEligibleForInvoiceEmail, mapErpPayloadToFulfillment, mapErpPayloadToInvoiceNumber, normalizeInvoiceTemplateId, resolveInvoiceAssetUrl, resolveOrderInvoicePdfBytes, streamOrderInvoicePdf, unwrapErpReadData };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkAXMP2U43_cjs = require('./chunk-AXMP2U43.cjs');
|
|
4
4
|
var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
|
|
5
5
|
|
|
6
6
|
// src/plugins/email/email-queue.ts
|
|
@@ -50,13 +50,13 @@ async function resolveInvoiceAttachmentForJob(cms, payload, options) {
|
|
|
50
50
|
invoiceAttached: false
|
|
51
51
|
};
|
|
52
52
|
const o = order;
|
|
53
|
-
if (!
|
|
53
|
+
if (!chunkAXMP2U43_cjs.isOrderEligibleForInvoiceEmail(o.status, o.orderKind)) {
|
|
54
54
|
return {
|
|
55
55
|
attachments: payload.attachments,
|
|
56
56
|
invoiceAttached: false
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
const resolved = await
|
|
59
|
+
const resolved = await chunkAXMP2U43_cjs.resolveOrderInvoicePdfBytes(cms, dataSource, entityMap, payload.orderId);
|
|
60
60
|
if ("error" in resolved) {
|
|
61
61
|
console.warn("[email] order invoice attach failed", {
|
|
62
62
|
orderId: payload.orderId,
|
|
@@ -130,7 +130,41 @@ async function queueEmail(cms, payload) {
|
|
|
130
130
|
await queue.add(EMAIL_QUEUE_NAME, payload);
|
|
131
131
|
return;
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
let email = cms && typeof cms.getPlugin === "function" ? cms.getPlugin("email") : void 0;
|
|
134
|
+
if (!email) {
|
|
135
|
+
try {
|
|
136
|
+
const { EmailService } = await import('./email-service-RDOTY3CO.cjs');
|
|
137
|
+
const env = typeof process !== "undefined" && process.env ? process.env : {};
|
|
138
|
+
const type = String(env.SMTP_TYPE || "SMTP").toUpperCase();
|
|
139
|
+
const port = env.SMTP_PORT ? parseInt(env.SMTP_PORT, 10) : void 0;
|
|
140
|
+
const fallbackService = new EmailService({
|
|
141
|
+
type: type === "AWS" || type === "GMAIL" || type === "SENDGRID" ? type : "SMTP",
|
|
142
|
+
from: env.SMTP_FROM || "no-reply@example.com",
|
|
143
|
+
to: env.SMTP_TO || "info@example.com",
|
|
144
|
+
user: env.SMTP_USER,
|
|
145
|
+
password: env.SMTP_PASSWORD || env.SMTP_PASS,
|
|
146
|
+
host: env.SMTP_HOST,
|
|
147
|
+
port,
|
|
148
|
+
secure: env.SMTP_SECURE === "true",
|
|
149
|
+
region: env.AWS_SES_REGION || env.AWS_REGION,
|
|
150
|
+
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
|
151
|
+
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
|
|
152
|
+
});
|
|
153
|
+
if (payload.subject != null && payload.html != null) {
|
|
154
|
+
await fallbackService.send({
|
|
155
|
+
to: payload.to,
|
|
156
|
+
subject: payload.subject,
|
|
157
|
+
html: payload.html,
|
|
158
|
+
text: payload.text,
|
|
159
|
+
attachments: payload.attachments
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error("[email-queue] fallback email send error", e);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
134
168
|
if (!email) return;
|
|
135
169
|
const attachmentsResult = await resolveInvoiceAttachmentForJob(cms, payload, registeredInvoiceOptions);
|
|
136
170
|
if (payload.templateName && payload.ctx) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkUEE4PTTB_cjs = require('./chunk-UEE4PTTB.cjs');
|
|
4
4
|
var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
|
|
5
5
|
|
|
6
6
|
// src/lib/emit-order-notification-trigger.ts
|
|
@@ -18,7 +18,7 @@ async function emitOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
|
|
|
18
18
|
});
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
const listenerCount =
|
|
21
|
+
const listenerCount = chunkUEE4PTTB_cjs.notificationTriggerEmitter.listenerCount("__any__");
|
|
22
22
|
if (listenerCount === 0) {
|
|
23
23
|
console.warn("[notification-triggers] no WhatsApp dispatcher listener \u2014 ensure createCmsApiHandler passes getCms (initWhatsappTriggerDispatcher)");
|
|
24
24
|
}
|
|
@@ -79,7 +79,7 @@ async function emitOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
const triggerPayload = {
|
|
83
83
|
orderId,
|
|
84
84
|
orderNumber: String(fo.orderNumber ?? ""),
|
|
85
85
|
customerName: fo.contact?.name ?? "",
|
|
@@ -89,7 +89,21 @@ async function emitOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
|
|
|
89
89
|
vendorId: fo.vendorId ?? null,
|
|
90
90
|
extraVariables: extra?.extraVariables,
|
|
91
91
|
refundAmount: resolvedRefundAmount
|
|
92
|
-
}
|
|
92
|
+
};
|
|
93
|
+
if (listenerCount === 0) {
|
|
94
|
+
try {
|
|
95
|
+
const { dispatchOrderNotificationDirectly } = await import('./order-notification-dispatcher-X2XM5VUY.cjs');
|
|
96
|
+
await dispatchOrderNotificationDirectly(triggerKey, triggerPayload, deps.dataSource, deps.entityMap);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("[notification-triggers] direct dispatch failed", {
|
|
99
|
+
triggerKey,
|
|
100
|
+
orderId,
|
|
101
|
+
err
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
chunkUEE4PTTB_cjs.notificationTriggerEmitter.emitTrigger(triggerKey, triggerPayload);
|
|
106
|
+
}
|
|
93
107
|
}
|
|
94
108
|
chunkUSNT2KNT_cjs.__name(emitOrderNotificationTrigger, "emitOrderNotificationTrigger");
|
|
95
109
|
function fireOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { __name } from './chunk-SHUYVCID.js';
|
|
2
|
+
|
|
3
|
+
// src/plugins/email/templates/types.ts
|
|
4
|
+
function normalizeSocialLinkItem(o) {
|
|
5
|
+
const url = String(o.url ?? "").trim();
|
|
6
|
+
if (!url) return null;
|
|
7
|
+
let iconUrl = String(o.iconUrl ?? o.icon_image ?? "").trim();
|
|
8
|
+
let icon = String(o.icon ?? "").trim();
|
|
9
|
+
if (!iconUrl && /^https?:\/\//i.test(icon)) {
|
|
10
|
+
iconUrl = icon;
|
|
11
|
+
icon = "";
|
|
12
|
+
}
|
|
13
|
+
const item = {
|
|
14
|
+
url
|
|
15
|
+
};
|
|
16
|
+
if (iconUrl) item.iconUrl = iconUrl;
|
|
17
|
+
if (icon) item.icon = icon;
|
|
18
|
+
return item;
|
|
19
|
+
}
|
|
20
|
+
__name(normalizeSocialLinkItem, "normalizeSocialLinkItem");
|
|
21
|
+
function parseSocialLinksJson(raw) {
|
|
22
|
+
if (raw == null || raw.trim() === "") return void 0;
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const item of parsed) {
|
|
28
|
+
if (item && typeof item === "object" && "url" in item) {
|
|
29
|
+
const n = normalizeSocialLinkItem(item);
|
|
30
|
+
if (n) out.push(n);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return out.length ? out : void 0;
|
|
34
|
+
} catch {
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
__name(parseSocialLinksJson, "parseSocialLinksJson");
|
|
39
|
+
function mergeEmailLayoutCompanyDetails(branding, emailSettings) {
|
|
40
|
+
const fromBranding = getCompanyDetailsFromSettings(branding);
|
|
41
|
+
const pick = /* @__PURE__ */ __name((emailVal, fallback) => {
|
|
42
|
+
const t = emailVal?.trim();
|
|
43
|
+
return t || fallback?.trim() || void 0;
|
|
44
|
+
}, "pick");
|
|
45
|
+
const logoUrl = pick(emailSettings.logoUrl ?? emailSettings.emailLogoUrl, fromBranding.logoUrl);
|
|
46
|
+
const companyName = pick(emailSettings.companyName ?? emailSettings.emailCompanyName, fromBranding.companyName);
|
|
47
|
+
const supportEmail = pick(emailSettings.supportEmail ?? emailSettings.emailSupportEmail, fromBranding.supportEmail);
|
|
48
|
+
const supportPhone = pick(emailSettings.supportPhone, void 0);
|
|
49
|
+
const footerDisclaimer = pick(emailSettings.footerDisclaimer, void 0);
|
|
50
|
+
const followUsTitle = pick(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
|
|
51
|
+
const socialFromEmail = parseSocialLinksJson(emailSettings.socialLinks);
|
|
52
|
+
const socialLinks = socialFromEmail?.length ? socialFromEmail : fromBranding.socialLinks;
|
|
53
|
+
return {
|
|
54
|
+
logoUrl,
|
|
55
|
+
companyName,
|
|
56
|
+
supportEmail,
|
|
57
|
+
supportPhone,
|
|
58
|
+
socialLinks,
|
|
59
|
+
footerDisclaimer,
|
|
60
|
+
followUsTitle
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
__name(mergeEmailLayoutCompanyDetails, "mergeEmailLayoutCompanyDetails");
|
|
64
|
+
function getCompanyDetailsFromSettings(settingsGroup) {
|
|
65
|
+
const logoUrl = settingsGroup.logo ?? settingsGroup.logoUrl ?? "";
|
|
66
|
+
const companyName = settingsGroup.companyName ?? settingsGroup.company_name ?? "";
|
|
67
|
+
const supportEmail = settingsGroup.supportEmail ?? settingsGroup.support_email ?? "";
|
|
68
|
+
let socialLinks = [];
|
|
69
|
+
const raw = settingsGroup.socialLinks ?? settingsGroup.social_links;
|
|
70
|
+
if (typeof raw === "string") {
|
|
71
|
+
try {
|
|
72
|
+
const arr = JSON.parse(raw);
|
|
73
|
+
if (Array.isArray(arr)) {
|
|
74
|
+
for (const item of arr) {
|
|
75
|
+
if (item && typeof item === "object") {
|
|
76
|
+
const n = normalizeSocialLinkItem(item);
|
|
77
|
+
if (n) socialLinks.push(n);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
logoUrl: logoUrl || void 0,
|
|
86
|
+
companyName: companyName || void 0,
|
|
87
|
+
supportEmail: supportEmail || void 0,
|
|
88
|
+
socialLinks: socialLinks.length ? socialLinks : void 0
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
__name(getCompanyDetailsFromSettings, "getCompanyDetailsFromSettings");
|
|
92
|
+
|
|
93
|
+
export { getCompanyDetailsFromSettings, mergeEmailLayoutCompanyDetails };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { __name } from './chunk-SHUYVCID.js';
|
|
2
|
+
import { createHmac, timingSafeEqual, randomInt } from 'crypto';
|
|
3
|
+
import { MoreThan, IsNull } from 'typeorm';
|
|
4
|
+
|
|
5
|
+
// src/plugins/sms/sms-queue.ts
|
|
6
|
+
var SMS_QUEUE_NAME = "sms";
|
|
7
|
+
function registerSmsQueueProcessor(cms) {
|
|
8
|
+
const queue = cms.getPlugin("queue");
|
|
9
|
+
const sms = cms.getPlugin("sms");
|
|
10
|
+
if (!queue || !sms || typeof sms.send !== "function") return;
|
|
11
|
+
queue.registerProcessor(SMS_QUEUE_NAME, async (data) => {
|
|
12
|
+
const payload = data;
|
|
13
|
+
if (!payload.to) return;
|
|
14
|
+
if (payload.templateKey?.trim()) {
|
|
15
|
+
await sms.send({
|
|
16
|
+
to: payload.to,
|
|
17
|
+
templateKey: payload.templateKey.trim(),
|
|
18
|
+
variables: payload.variables,
|
|
19
|
+
otpCode: payload.otpCode
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!payload.body?.trim()) return;
|
|
24
|
+
await sms.send({
|
|
25
|
+
to: payload.to,
|
|
26
|
+
body: payload.body,
|
|
27
|
+
otpCode: payload.otpCode,
|
|
28
|
+
variables: payload.variables
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
__name(registerSmsQueueProcessor, "registerSmsQueueProcessor");
|
|
33
|
+
async function queueSms(cms, payload) {
|
|
34
|
+
const queue = cms.getPlugin("queue");
|
|
35
|
+
const sms = cms.getPlugin("sms");
|
|
36
|
+
if (queue) {
|
|
37
|
+
await queue.add(SMS_QUEUE_NAME, payload);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (sms && typeof sms.send === "function") {
|
|
41
|
+
if (payload.templateKey?.trim()) {
|
|
42
|
+
await sms.send({
|
|
43
|
+
to: payload.to,
|
|
44
|
+
templateKey: payload.templateKey.trim(),
|
|
45
|
+
variables: payload.variables,
|
|
46
|
+
otpCode: payload.otpCode
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (payload.body?.trim()) {
|
|
51
|
+
await sms.send({
|
|
52
|
+
to: payload.to,
|
|
53
|
+
body: payload.body,
|
|
54
|
+
otpCode: payload.otpCode,
|
|
55
|
+
variables: payload.variables
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
__name(queueSms, "queueSms");
|
|
61
|
+
var OTP_TTL_MS = 10 * 60 * 1e3;
|
|
62
|
+
var MAX_SENDS_PER_HOUR = 5;
|
|
63
|
+
var MAX_VERIFY_ATTEMPTS = 8;
|
|
64
|
+
function getPepper(explicit) {
|
|
65
|
+
return (explicit || process.env.OTP_PEPPER || process.env.NEXTAUTH_SECRET || "dev-otp-pepper").trim();
|
|
66
|
+
}
|
|
67
|
+
__name(getPepper, "getPepper");
|
|
68
|
+
function hashOtpCode(code, purpose, identifier, pepper) {
|
|
69
|
+
return createHmac("sha256", getPepper(pepper)).update(`${purpose}|${identifier}|${code}`).digest("hex");
|
|
70
|
+
}
|
|
71
|
+
__name(hashOtpCode, "hashOtpCode");
|
|
72
|
+
function verifyOtpCodeHash(code, storedHash, purpose, identifier, pepper) {
|
|
73
|
+
const h = hashOtpCode(code, purpose, identifier, pepper);
|
|
74
|
+
try {
|
|
75
|
+
return timingSafeEqual(Buffer.from(h, "utf8"), Buffer.from(storedHash, "utf8"));
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
__name(verifyOtpCodeHash, "verifyOtpCodeHash");
|
|
81
|
+
function generateNumericOtp(length = 6) {
|
|
82
|
+
const max = 10 ** length;
|
|
83
|
+
return randomInt(0, max).toString().padStart(length, "0");
|
|
84
|
+
}
|
|
85
|
+
__name(generateNumericOtp, "generateNumericOtp");
|
|
86
|
+
function normalizePhoneE164(raw, defaultCountryCode) {
|
|
87
|
+
const t = raw.trim();
|
|
88
|
+
const digitsOnly = t.replace(/\D/g, "");
|
|
89
|
+
if (digitsOnly.length < 10) return null;
|
|
90
|
+
if (t.startsWith("+")) return `+${digitsOnly}`;
|
|
91
|
+
const cc = (defaultCountryCode || process.env.DEFAULT_PHONE_COUNTRY_CODE || "91").replace(/\D/g, "");
|
|
92
|
+
if (digitsOnly.length > 10) return `+${digitsOnly}`;
|
|
93
|
+
return `+${cc}${digitsOnly}`;
|
|
94
|
+
}
|
|
95
|
+
__name(normalizePhoneE164, "normalizePhoneE164");
|
|
96
|
+
async function countRecentOtpSends(dataSource, entityMap, purpose, identifier, since) {
|
|
97
|
+
const repo = dataSource.getRepository(entityMap.otp_challenges);
|
|
98
|
+
return repo.count({
|
|
99
|
+
where: {
|
|
100
|
+
purpose,
|
|
101
|
+
identifier,
|
|
102
|
+
createdAt: MoreThan(since)
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
__name(countRecentOtpSends, "countRecentOtpSends");
|
|
107
|
+
async function createOtpChallenge(dataSource, entityMap, input) {
|
|
108
|
+
const { purpose, channel, identifier, code, pepper } = input;
|
|
109
|
+
const since = new Date(Date.now() - 60 * 60 * 1e3);
|
|
110
|
+
const recent = await countRecentOtpSends(dataSource, entityMap, purpose, identifier, since);
|
|
111
|
+
if (recent >= MAX_SENDS_PER_HOUR) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: "Too many codes sent. Try again later.",
|
|
115
|
+
status: 429
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const repo = dataSource.getRepository(entityMap.otp_challenges);
|
|
119
|
+
await repo.delete({
|
|
120
|
+
purpose,
|
|
121
|
+
identifier,
|
|
122
|
+
consumedAt: IsNull()
|
|
123
|
+
});
|
|
124
|
+
const expiresAt = new Date(Date.now() + OTP_TTL_MS);
|
|
125
|
+
const codeHash = hashOtpCode(code, purpose, identifier, pepper);
|
|
126
|
+
await repo.save(repo.create({
|
|
127
|
+
purpose,
|
|
128
|
+
channel,
|
|
129
|
+
identifier,
|
|
130
|
+
codeHash,
|
|
131
|
+
expiresAt,
|
|
132
|
+
attempts: 0,
|
|
133
|
+
consumedAt: null
|
|
134
|
+
}));
|
|
135
|
+
return {
|
|
136
|
+
ok: true
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
__name(createOtpChallenge, "createOtpChallenge");
|
|
140
|
+
async function verifyAndConsumeOtpChallenge(dataSource, entityMap, input) {
|
|
141
|
+
const { purpose, identifier, code, pepper } = input;
|
|
142
|
+
const repo = dataSource.getRepository(entityMap.otp_challenges);
|
|
143
|
+
const row = await repo.findOne({
|
|
144
|
+
where: {
|
|
145
|
+
purpose,
|
|
146
|
+
identifier,
|
|
147
|
+
consumedAt: IsNull()
|
|
148
|
+
},
|
|
149
|
+
order: {
|
|
150
|
+
id: "DESC"
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (!row) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
error: "Invalid or expired code",
|
|
157
|
+
status: 400
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const r = row;
|
|
161
|
+
if (new Date(r.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
162
|
+
await repo.delete(row.id);
|
|
163
|
+
return {
|
|
164
|
+
ok: false,
|
|
165
|
+
error: "Invalid or expired code",
|
|
166
|
+
status: 400
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const attempts = r.attempts || 0;
|
|
170
|
+
if (attempts >= MAX_VERIFY_ATTEMPTS) {
|
|
171
|
+
await repo.delete(row.id);
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
error: "Too many attempts",
|
|
175
|
+
status: 400
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const valid = verifyOtpCodeHash(code, r.codeHash, purpose, identifier, pepper);
|
|
179
|
+
if (!valid) {
|
|
180
|
+
await repo.update(row.id, {
|
|
181
|
+
attempts: attempts + 1
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
error: "Invalid or expired code",
|
|
186
|
+
status: 400
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
await repo.update(row.id, {
|
|
190
|
+
consumedAt: /* @__PURE__ */ new Date(),
|
|
191
|
+
attempts: attempts + 1
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
ok: true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
__name(verifyAndConsumeOtpChallenge, "verifyAndConsumeOtpChallenge");
|
|
198
|
+
|
|
199
|
+
export { countRecentOtpSends, createOtpChallenge, generateNumericOtp, hashOtpCode, normalizePhoneE164, queueSms, registerSmsQueueProcessor, verifyAndConsumeOtpChallenge, verifyOtpCodeHash };
|