mailery 0.16.0 → 0.16.2
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/index.cjs +61 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +61 -13
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +53 -9
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +53 -9
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/testing.cjs
CHANGED
|
@@ -314,15 +314,49 @@ var init_webhook_tolerance = __esm({
|
|
|
314
314
|
// src/server/providers/sendgrid.ts
|
|
315
315
|
var sendgrid_exports = {};
|
|
316
316
|
__export(sendgrid_exports, {
|
|
317
|
-
SendGridProvider: () => SendGridProvider
|
|
317
|
+
SendGridProvider: () => SendGridProvider,
|
|
318
|
+
normalizeSendGridMessageId: () => normalizeSendGridMessageId,
|
|
319
|
+
normalizeWebhookVerificationKey: () => normalizeWebhookVerificationKey
|
|
318
320
|
});
|
|
321
|
+
function normalizeWebhookVerificationKey(input) {
|
|
322
|
+
let key = String(input ?? "").trim().replace(/^["']|["']$/g, "");
|
|
323
|
+
if (key.includes("\\n")) key = key.replace(/\\n/g, "\n");
|
|
324
|
+
let pem;
|
|
325
|
+
if (key.includes(PEM_HEADER)) {
|
|
326
|
+
pem = key;
|
|
327
|
+
} else {
|
|
328
|
+
const b64 = key.replace(/\s+/g, "");
|
|
329
|
+
if (!b64 || !/^[A-Za-z0-9+/=]+$/.test(b64)) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
"SendGridProvider: webhookVerificationKey is neither a PEM public key nor a base64 SPKI key. Copy the Verification Key from SendGrid \u2192 Settings \u2192 Mail Settings \u2192 Signed Event Webhook."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
pem = `${PEM_HEADER}
|
|
335
|
+
${b64.match(/.{1,64}/g).join("\n")}
|
|
336
|
+
${PEM_FOOTER}
|
|
337
|
+
`;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
crypto2__default.default.createPublicKey(pem);
|
|
341
|
+
} catch (err) {
|
|
342
|
+
throw new Error(
|
|
343
|
+
`SendGridProvider: webhookVerificationKey did not parse as a public key (${String(err?.message ?? err)}). Copy the Verification Key from SendGrid \u2192 Settings \u2192 Mail Settings \u2192 Signed Event Webhook.`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
return pem;
|
|
347
|
+
}
|
|
348
|
+
function normalizeSendGridMessageId(raw) {
|
|
349
|
+
const id = String(raw ?? "");
|
|
350
|
+
const marker = /\.(?:filter|recvd)\S*$/i.exec(id);
|
|
351
|
+
return marker ? id.slice(0, marker.index) : id;
|
|
352
|
+
}
|
|
319
353
|
function normalizeSendGridEvent(e) {
|
|
320
354
|
const type = mapEventType(e.event);
|
|
321
355
|
if (!type) return null;
|
|
322
356
|
return {
|
|
323
357
|
type,
|
|
324
358
|
providerEventId: String(e.sg_event_id ?? e["smtp-id"] ?? `${e.event}-${e.timestamp}-${e.email}`),
|
|
325
|
-
providerMessageId:
|
|
359
|
+
providerMessageId: e.sg_message_id != null ? normalizeSendGridMessageId(e.sg_message_id) : String(e["smtp-id"] ?? ""),
|
|
326
360
|
email: String(e.email ?? "").toLowerCase(),
|
|
327
361
|
occurredAt: new Date(Number(e.timestamp) * 1e3),
|
|
328
362
|
details: {
|
|
@@ -354,24 +388,29 @@ function mapEventType(sgEvent) {
|
|
|
354
388
|
return null;
|
|
355
389
|
}
|
|
356
390
|
}
|
|
357
|
-
var SG_SIG_HEADER, SG_TS_HEADER, SendGridProvider;
|
|
391
|
+
var SG_SIG_HEADER, SG_TS_HEADER, PEM_HEADER, PEM_FOOTER, SendGridProvider;
|
|
358
392
|
var init_sendgrid = __esm({
|
|
359
393
|
"src/server/providers/sendgrid.ts"() {
|
|
360
394
|
init_webhook_tolerance();
|
|
361
395
|
SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
|
|
362
396
|
SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
|
|
397
|
+
PEM_HEADER = "-----BEGIN PUBLIC KEY-----";
|
|
398
|
+
PEM_FOOTER = "-----END PUBLIC KEY-----";
|
|
363
399
|
SendGridProvider = class {
|
|
364
400
|
constructor(opts) {
|
|
365
401
|
this.opts = opts;
|
|
366
402
|
sgMail__default.default.setApiKey(opts.apiKey);
|
|
367
403
|
this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
|
|
368
404
|
this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
|
|
405
|
+
this.verificationKeyPem = opts.webhookVerificationKey ? normalizeWebhookVerificationKey(opts.webhookVerificationKey) : null;
|
|
369
406
|
}
|
|
370
407
|
opts;
|
|
371
408
|
name = "sendgrid";
|
|
372
409
|
sendRatePerSecond;
|
|
373
410
|
/** Resolved replay window in seconds; `0` means the check is disabled. */
|
|
374
411
|
webhookToleranceSeconds;
|
|
412
|
+
/** The verification key as PEM, whatever shape it was configured in. */
|
|
413
|
+
verificationKeyPem;
|
|
375
414
|
async send(args) {
|
|
376
415
|
const msg = {
|
|
377
416
|
to: args.to,
|
|
@@ -403,7 +442,7 @@ var init_sendgrid = __esm({
|
|
|
403
442
|
};
|
|
404
443
|
}
|
|
405
444
|
async verifyWebhook(rawBody, headers) {
|
|
406
|
-
if (!this.
|
|
445
|
+
if (!this.verificationKeyPem) return false;
|
|
407
446
|
const sig = headers[SG_SIG_HEADER];
|
|
408
447
|
const ts = headers[SG_TS_HEADER];
|
|
409
448
|
if (!sig || !ts) return false;
|
|
@@ -411,7 +450,7 @@ var init_sendgrid = __esm({
|
|
|
411
450
|
try {
|
|
412
451
|
const verifier = crypto2__default.default.createVerify("sha256");
|
|
413
452
|
verifier.update(payload);
|
|
414
|
-
if (!verifier.verify(this.
|
|
453
|
+
if (!verifier.verify(this.verificationKeyPem, sig, "base64")) return false;
|
|
415
454
|
} catch {
|
|
416
455
|
return false;
|
|
417
456
|
}
|
|
@@ -15444,11 +15483,16 @@ async function processWebhookBacklog(ctx, opts = {}) {
|
|
|
15444
15483
|
}
|
|
15445
15484
|
}
|
|
15446
15485
|
}
|
|
15486
|
+
async function findSendForEvent(event, ctx) {
|
|
15487
|
+
if (event.providerMessageId) {
|
|
15488
|
+
const byId = await ctx.collections.sends.findOne({ providerMessageId: event.providerMessageId });
|
|
15489
|
+
if (byId) return byId;
|
|
15490
|
+
}
|
|
15491
|
+
if (!event.email) return null;
|
|
15492
|
+
return ctx.collections.sends.findOne({ emailAtSend: event.email, sentAt: { $ne: null } }, { sort: { sentAt: -1 } });
|
|
15493
|
+
}
|
|
15447
15494
|
async function applyWebhookEvent(event, ctx) {
|
|
15448
|
-
const send = await ctx
|
|
15449
|
-
event.providerMessageId ? { $or: [{ providerMessageId: event.providerMessageId }, { emailAtSend: event.email }] } : { emailAtSend: event.email },
|
|
15450
|
-
{ sort: { queuedAt: -1 } }
|
|
15451
|
-
);
|
|
15495
|
+
const send = await findSendForEvent(event, ctx);
|
|
15452
15496
|
switch (event.type) {
|
|
15453
15497
|
case "delivered":
|
|
15454
15498
|
if (send) {
|