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/index.cjs
CHANGED
|
@@ -286,15 +286,49 @@ var init_webhook_tolerance = __esm({
|
|
|
286
286
|
// src/server/providers/sendgrid.ts
|
|
287
287
|
var sendgrid_exports = {};
|
|
288
288
|
__export(sendgrid_exports, {
|
|
289
|
-
SendGridProvider: () => exports.SendGridProvider
|
|
289
|
+
SendGridProvider: () => exports.SendGridProvider,
|
|
290
|
+
normalizeSendGridMessageId: () => normalizeSendGridMessageId,
|
|
291
|
+
normalizeWebhookVerificationKey: () => normalizeWebhookVerificationKey
|
|
290
292
|
});
|
|
293
|
+
function normalizeWebhookVerificationKey(input) {
|
|
294
|
+
let key = String(input ?? "").trim().replace(/^["']|["']$/g, "");
|
|
295
|
+
if (key.includes("\\n")) key = key.replace(/\\n/g, "\n");
|
|
296
|
+
let pem;
|
|
297
|
+
if (key.includes(PEM_HEADER)) {
|
|
298
|
+
pem = key;
|
|
299
|
+
} else {
|
|
300
|
+
const b64 = key.replace(/\s+/g, "");
|
|
301
|
+
if (!b64 || !/^[A-Za-z0-9+/=]+$/.test(b64)) {
|
|
302
|
+
throw new Error(
|
|
303
|
+
"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."
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
pem = `${PEM_HEADER}
|
|
307
|
+
${b64.match(/.{1,64}/g).join("\n")}
|
|
308
|
+
${PEM_FOOTER}
|
|
309
|
+
`;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
crypto2__default.default.createPublicKey(pem);
|
|
313
|
+
} catch (err) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`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.`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return pem;
|
|
319
|
+
}
|
|
320
|
+
function normalizeSendGridMessageId(raw) {
|
|
321
|
+
const id = String(raw ?? "");
|
|
322
|
+
const marker = /\.(?:filter|recvd)\S*$/i.exec(id);
|
|
323
|
+
return marker ? id.slice(0, marker.index) : id;
|
|
324
|
+
}
|
|
291
325
|
function normalizeSendGridEvent(e) {
|
|
292
326
|
const type = mapEventType(e.event);
|
|
293
327
|
if (!type) return null;
|
|
294
328
|
return {
|
|
295
329
|
type,
|
|
296
330
|
providerEventId: String(e.sg_event_id ?? e["smtp-id"] ?? `${e.event}-${e.timestamp}-${e.email}`),
|
|
297
|
-
providerMessageId:
|
|
331
|
+
providerMessageId: e.sg_message_id != null ? normalizeSendGridMessageId(e.sg_message_id) : String(e["smtp-id"] ?? ""),
|
|
298
332
|
email: String(e.email ?? "").toLowerCase(),
|
|
299
333
|
occurredAt: new Date(Number(e.timestamp) * 1e3),
|
|
300
334
|
details: {
|
|
@@ -326,24 +360,29 @@ function mapEventType(sgEvent) {
|
|
|
326
360
|
return null;
|
|
327
361
|
}
|
|
328
362
|
}
|
|
329
|
-
var SG_SIG_HEADER, SG_TS_HEADER; exports.SendGridProvider = void 0;
|
|
363
|
+
var SG_SIG_HEADER, SG_TS_HEADER, PEM_HEADER, PEM_FOOTER; exports.SendGridProvider = void 0;
|
|
330
364
|
var init_sendgrid = __esm({
|
|
331
365
|
"src/server/providers/sendgrid.ts"() {
|
|
332
366
|
init_webhook_tolerance();
|
|
333
367
|
SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
|
|
334
368
|
SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
|
|
369
|
+
PEM_HEADER = "-----BEGIN PUBLIC KEY-----";
|
|
370
|
+
PEM_FOOTER = "-----END PUBLIC KEY-----";
|
|
335
371
|
exports.SendGridProvider = class {
|
|
336
372
|
constructor(opts) {
|
|
337
373
|
this.opts = opts;
|
|
338
374
|
sgMail__default.default.setApiKey(opts.apiKey);
|
|
339
375
|
this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
|
|
340
376
|
this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
|
|
377
|
+
this.verificationKeyPem = opts.webhookVerificationKey ? normalizeWebhookVerificationKey(opts.webhookVerificationKey) : null;
|
|
341
378
|
}
|
|
342
379
|
opts;
|
|
343
380
|
name = "sendgrid";
|
|
344
381
|
sendRatePerSecond;
|
|
345
382
|
/** Resolved replay window in seconds; `0` means the check is disabled. */
|
|
346
383
|
webhookToleranceSeconds;
|
|
384
|
+
/** The verification key as PEM, whatever shape it was configured in. */
|
|
385
|
+
verificationKeyPem;
|
|
347
386
|
async send(args) {
|
|
348
387
|
const msg = {
|
|
349
388
|
to: args.to,
|
|
@@ -375,7 +414,7 @@ var init_sendgrid = __esm({
|
|
|
375
414
|
};
|
|
376
415
|
}
|
|
377
416
|
async verifyWebhook(rawBody, headers) {
|
|
378
|
-
if (!this.
|
|
417
|
+
if (!this.verificationKeyPem) return false;
|
|
379
418
|
const sig = headers[SG_SIG_HEADER];
|
|
380
419
|
const ts = headers[SG_TS_HEADER];
|
|
381
420
|
if (!sig || !ts) return false;
|
|
@@ -383,7 +422,7 @@ var init_sendgrid = __esm({
|
|
|
383
422
|
try {
|
|
384
423
|
const verifier = crypto2__default.default.createVerify("sha256");
|
|
385
424
|
verifier.update(payload);
|
|
386
|
-
if (!verifier.verify(this.
|
|
425
|
+
if (!verifier.verify(this.verificationKeyPem, sig, "base64")) return false;
|
|
387
426
|
} catch {
|
|
388
427
|
return false;
|
|
389
428
|
}
|
|
@@ -1726,11 +1765,20 @@ async function processWebhookBacklog(ctx, opts = {}) {
|
|
|
1726
1765
|
}
|
|
1727
1766
|
}
|
|
1728
1767
|
}
|
|
1768
|
+
async function findSendForEvent(event, ctx) {
|
|
1769
|
+
if (event.providerMessageId) {
|
|
1770
|
+
const byId = await ctx.collections.sends.findOne({ providerMessageId: event.providerMessageId });
|
|
1771
|
+
if (byId) return byId;
|
|
1772
|
+
}
|
|
1773
|
+
if (!event.email) return null;
|
|
1774
|
+
return ctx.collections.sends.findOne({ emailAtSend: event.email, sentAt: { $ne: null } }, { sort: { sentAt: -1 } });
|
|
1775
|
+
}
|
|
1776
|
+
function webhookEventsForMessageId(providerMessageId) {
|
|
1777
|
+
const escaped = providerMessageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1778
|
+
return { $or: [{ providerMessageId }, { providerMessageId: { $regex: `^${escaped}\\.` } }] };
|
|
1779
|
+
}
|
|
1729
1780
|
async function applyWebhookEvent(event, ctx) {
|
|
1730
|
-
const send = await ctx
|
|
1731
|
-
event.providerMessageId ? { $or: [{ providerMessageId: event.providerMessageId }, { emailAtSend: event.email }] } : { emailAtSend: event.email },
|
|
1732
|
-
{ sort: { queuedAt: -1 } }
|
|
1733
|
-
);
|
|
1781
|
+
const send = await findSendForEvent(event, ctx);
|
|
1734
1782
|
switch (event.type) {
|
|
1735
1783
|
case "delivered":
|
|
1736
1784
|
if (send) {
|
|
@@ -6484,7 +6532,7 @@ function createAdminApiRouter(mailer, opts = {}) {
|
|
|
6484
6532
|
if (!mongodb.ObjectId.isValid(id)) return res.status(400).json({ error: "bad_id" });
|
|
6485
6533
|
const send = await c.sends.findOne({ _id: new mongodb.ObjectId(id) });
|
|
6486
6534
|
if (!send) return res.status(404).json({ error: "not_found" });
|
|
6487
|
-
const events = send.providerMessageId ? await c.webhookEvents.find(
|
|
6535
|
+
const events = send.providerMessageId ? await c.webhookEvents.find(webhookEventsForMessageId(send.providerMessageId)).sort({ receivedAt: -1 }).limit(100).toArray() : [];
|
|
6488
6536
|
return res.json({ send, webhookEvents: events });
|
|
6489
6537
|
})
|
|
6490
6538
|
);
|
|
@@ -8223,7 +8271,7 @@ function unitToMs2(value, unit) {
|
|
|
8223
8271
|
}
|
|
8224
8272
|
|
|
8225
8273
|
// src/server/api/agent.ts
|
|
8226
|
-
var VERSION = "0.16.
|
|
8274
|
+
var VERSION = "0.16.2" ;
|
|
8227
8275
|
var MIN_AGENT_TOKEN_LENGTH = 24;
|
|
8228
8276
|
function createAgentRouter(mailer, opts) {
|
|
8229
8277
|
if (!opts || !Array.isArray(opts.tokens) || opts.tokens.length === 0) {
|
|
@@ -8426,7 +8474,7 @@ function createAgentRouter(mailer, opts) {
|
|
|
8426
8474
|
if (reached || Date.now() - started >= timeoutMs) break;
|
|
8427
8475
|
await sleep2(1e3);
|
|
8428
8476
|
}
|
|
8429
|
-
const webhookEvents = send.providerMessageId ? await c.webhookEvents.find(
|
|
8477
|
+
const webhookEvents = send.providerMessageId ? await c.webhookEvents.find(webhookEventsForMessageId(send.providerMessageId)).sort({ receivedAt: 1 }).limit(100).toArray() : [];
|
|
8430
8478
|
res.json({ reached, target, waitedMs: Date.now() - started, send: sendSummary(send), webhookEvents });
|
|
8431
8479
|
})
|
|
8432
8480
|
);
|
|
@@ -9923,7 +9971,7 @@ var DEDUPE_POLICIES = [
|
|
|
9923
9971
|
];
|
|
9924
9972
|
|
|
9925
9973
|
// src/server/index.ts
|
|
9926
|
-
var VERSION2 = "0.16.
|
|
9974
|
+
var VERSION2 = "0.16.2" ;
|
|
9927
9975
|
|
|
9928
9976
|
exports.DEDUPE_POLICIES = DEDUPE_POLICIES;
|
|
9929
9977
|
exports.DEFAULT_BOT_UA_RE = DEFAULT_BOT_UA_RE;
|