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.js
CHANGED
|
@@ -299,15 +299,49 @@ var init_webhook_tolerance = __esm({
|
|
|
299
299
|
// src/server/providers/sendgrid.ts
|
|
300
300
|
var sendgrid_exports = {};
|
|
301
301
|
__export(sendgrid_exports, {
|
|
302
|
-
SendGridProvider: () => SendGridProvider
|
|
302
|
+
SendGridProvider: () => SendGridProvider,
|
|
303
|
+
normalizeSendGridMessageId: () => normalizeSendGridMessageId,
|
|
304
|
+
normalizeWebhookVerificationKey: () => normalizeWebhookVerificationKey
|
|
303
305
|
});
|
|
306
|
+
function normalizeWebhookVerificationKey(input) {
|
|
307
|
+
let key = String(input ?? "").trim().replace(/^["']|["']$/g, "");
|
|
308
|
+
if (key.includes("\\n")) key = key.replace(/\\n/g, "\n");
|
|
309
|
+
let pem;
|
|
310
|
+
if (key.includes(PEM_HEADER)) {
|
|
311
|
+
pem = key;
|
|
312
|
+
} else {
|
|
313
|
+
const b64 = key.replace(/\s+/g, "");
|
|
314
|
+
if (!b64 || !/^[A-Za-z0-9+/=]+$/.test(b64)) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
"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."
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
pem = `${PEM_HEADER}
|
|
320
|
+
${b64.match(/.{1,64}/g).join("\n")}
|
|
321
|
+
${PEM_FOOTER}
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
crypto2.createPublicKey(pem);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
`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.`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return pem;
|
|
332
|
+
}
|
|
333
|
+
function normalizeSendGridMessageId(raw) {
|
|
334
|
+
const id = String(raw ?? "");
|
|
335
|
+
const marker = /\.(?:filter|recvd)\S*$/i.exec(id);
|
|
336
|
+
return marker ? id.slice(0, marker.index) : id;
|
|
337
|
+
}
|
|
304
338
|
function normalizeSendGridEvent(e) {
|
|
305
339
|
const type = mapEventType(e.event);
|
|
306
340
|
if (!type) return null;
|
|
307
341
|
return {
|
|
308
342
|
type,
|
|
309
343
|
providerEventId: String(e.sg_event_id ?? e["smtp-id"] ?? `${e.event}-${e.timestamp}-${e.email}`),
|
|
310
|
-
providerMessageId:
|
|
344
|
+
providerMessageId: e.sg_message_id != null ? normalizeSendGridMessageId(e.sg_message_id) : String(e["smtp-id"] ?? ""),
|
|
311
345
|
email: String(e.email ?? "").toLowerCase(),
|
|
312
346
|
occurredAt: new Date(Number(e.timestamp) * 1e3),
|
|
313
347
|
details: {
|
|
@@ -339,24 +373,29 @@ function mapEventType(sgEvent) {
|
|
|
339
373
|
return null;
|
|
340
374
|
}
|
|
341
375
|
}
|
|
342
|
-
var SG_SIG_HEADER, SG_TS_HEADER, SendGridProvider;
|
|
376
|
+
var SG_SIG_HEADER, SG_TS_HEADER, PEM_HEADER, PEM_FOOTER, SendGridProvider;
|
|
343
377
|
var init_sendgrid = __esm({
|
|
344
378
|
"src/server/providers/sendgrid.ts"() {
|
|
345
379
|
init_webhook_tolerance();
|
|
346
380
|
SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
|
|
347
381
|
SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
|
|
382
|
+
PEM_HEADER = "-----BEGIN PUBLIC KEY-----";
|
|
383
|
+
PEM_FOOTER = "-----END PUBLIC KEY-----";
|
|
348
384
|
SendGridProvider = class {
|
|
349
385
|
constructor(opts) {
|
|
350
386
|
this.opts = opts;
|
|
351
387
|
sgMail.setApiKey(opts.apiKey);
|
|
352
388
|
this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
|
|
353
389
|
this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
|
|
390
|
+
this.verificationKeyPem = opts.webhookVerificationKey ? normalizeWebhookVerificationKey(opts.webhookVerificationKey) : null;
|
|
354
391
|
}
|
|
355
392
|
opts;
|
|
356
393
|
name = "sendgrid";
|
|
357
394
|
sendRatePerSecond;
|
|
358
395
|
/** Resolved replay window in seconds; `0` means the check is disabled. */
|
|
359
396
|
webhookToleranceSeconds;
|
|
397
|
+
/** The verification key as PEM, whatever shape it was configured in. */
|
|
398
|
+
verificationKeyPem;
|
|
360
399
|
async send(args) {
|
|
361
400
|
const msg = {
|
|
362
401
|
to: args.to,
|
|
@@ -388,7 +427,7 @@ var init_sendgrid = __esm({
|
|
|
388
427
|
};
|
|
389
428
|
}
|
|
390
429
|
async verifyWebhook(rawBody, headers) {
|
|
391
|
-
if (!this.
|
|
430
|
+
if (!this.verificationKeyPem) return false;
|
|
392
431
|
const sig = headers[SG_SIG_HEADER];
|
|
393
432
|
const ts = headers[SG_TS_HEADER];
|
|
394
433
|
if (!sig || !ts) return false;
|
|
@@ -396,7 +435,7 @@ var init_sendgrid = __esm({
|
|
|
396
435
|
try {
|
|
397
436
|
const verifier = crypto2.createVerify("sha256");
|
|
398
437
|
verifier.update(payload);
|
|
399
|
-
if (!verifier.verify(this.
|
|
438
|
+
if (!verifier.verify(this.verificationKeyPem, sig, "base64")) return false;
|
|
400
439
|
} catch {
|
|
401
440
|
return false;
|
|
402
441
|
}
|
|
@@ -15429,11 +15468,16 @@ async function processWebhookBacklog(ctx, opts = {}) {
|
|
|
15429
15468
|
}
|
|
15430
15469
|
}
|
|
15431
15470
|
}
|
|
15471
|
+
async function findSendForEvent(event, ctx) {
|
|
15472
|
+
if (event.providerMessageId) {
|
|
15473
|
+
const byId = await ctx.collections.sends.findOne({ providerMessageId: event.providerMessageId });
|
|
15474
|
+
if (byId) return byId;
|
|
15475
|
+
}
|
|
15476
|
+
if (!event.email) return null;
|
|
15477
|
+
return ctx.collections.sends.findOne({ emailAtSend: event.email, sentAt: { $ne: null } }, { sort: { sentAt: -1 } });
|
|
15478
|
+
}
|
|
15432
15479
|
async function applyWebhookEvent(event, ctx) {
|
|
15433
|
-
const send = await ctx
|
|
15434
|
-
event.providerMessageId ? { $or: [{ providerMessageId: event.providerMessageId }, { emailAtSend: event.email }] } : { emailAtSend: event.email },
|
|
15435
|
-
{ sort: { queuedAt: -1 } }
|
|
15436
|
-
);
|
|
15480
|
+
const send = await findSendForEvent(event, ctx);
|
|
15437
15481
|
switch (event.type) {
|
|
15438
15482
|
case "delivered":
|
|
15439
15483
|
if (send) {
|