dkimkit 0.0.0-stage → 0.7.0
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/README.md +161 -2
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +398 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/dns-host.d.ts +10 -0
- package/dist/src/dns-host.js +31 -0
- package/dist/src/dns-host.js.map +1 -0
- package/dist/src/errors.d.ts +17 -0
- package/dist/src/errors.js +51 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/hosted/api.d.ts +107 -0
- package/dist/src/hosted/api.js +919 -0
- package/dist/src/hosted/api.js.map +1 -0
- package/dist/src/hosted/auth.d.ts +80 -0
- package/dist/src/hosted/auth.js +147 -0
- package/dist/src/hosted/auth.js.map +1 -0
- package/dist/src/hosted/billing-dodo.d.ts +58 -0
- package/dist/src/hosted/billing-dodo.js +139 -0
- package/dist/src/hosted/billing-dodo.js.map +1 -0
- package/dist/src/hosted/billing-stripe.d.ts +82 -0
- package/dist/src/hosted/billing-stripe.js +205 -0
- package/dist/src/hosted/billing-stripe.js.map +1 -0
- package/dist/src/hosted/billing.d.ts +114 -0
- package/dist/src/hosted/billing.js +173 -0
- package/dist/src/hosted/billing.js.map +1 -0
- package/dist/src/hosted/bulk.d.ts +47 -0
- package/dist/src/hosted/bulk.js +66 -0
- package/dist/src/hosted/bulk.js.map +1 -0
- package/dist/src/hosted/cli.d.ts +2 -0
- package/dist/src/hosted/cli.js +318 -0
- package/dist/src/hosted/cli.js.map +1 -0
- package/dist/src/hosted/crypto.d.ts +22 -0
- package/dist/src/hosted/crypto.js +55 -0
- package/dist/src/hosted/crypto.js.map +1 -0
- package/dist/src/hosted/index.d.ts +7 -0
- package/dist/src/hosted/index.js +11 -0
- package/dist/src/hosted/index.js.map +1 -0
- package/dist/src/hosted/markdown.d.ts +16 -0
- package/dist/src/hosted/markdown.js +168 -0
- package/dist/src/hosted/markdown.js.map +1 -0
- package/dist/src/hosted/net.d.ts +9 -0
- package/dist/src/hosted/net.js +60 -0
- package/dist/src/hosted/net.js.map +1 -0
- package/dist/src/hosted/postgres-store.d.ts +62 -0
- package/dist/src/hosted/postgres-store.js +387 -0
- package/dist/src/hosted/postgres-store.js.map +1 -0
- package/dist/src/hosted/serialize.d.ts +144 -0
- package/dist/src/hosted/serialize.js +126 -0
- package/dist/src/hosted/serialize.js.map +1 -0
- package/dist/src/hosted/service.d.ts +170 -0
- package/dist/src/hosted/service.js +603 -0
- package/dist/src/hosted/service.js.map +1 -0
- package/dist/src/hosted/site.d.ts +84 -0
- package/dist/src/hosted/site.js +653 -0
- package/dist/src/hosted/site.js.map +1 -0
- package/dist/src/hosted/sqlite-store.d.ts +45 -0
- package/dist/src/hosted/sqlite-store.js +351 -0
- package/dist/src/hosted/sqlite-store.js.map +1 -0
- package/dist/src/hosted/store.d.ts +257 -0
- package/dist/src/hosted/store.js +181 -0
- package/dist/src/hosted/store.js.map +1 -0
- package/dist/src/hosted/widget.d.ts +46 -0
- package/dist/src/hosted/widget.js +130 -0
- package/dist/src/hosted/widget.js.map +1 -0
- package/dist/src/hosted/worker.d.ts +45 -0
- package/dist/src/hosted/worker.js +142 -0
- package/dist/src/hosted/worker.js.map +1 -0
- package/dist/src/http.d.ts +14 -0
- package/dist/src/http.js +54 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/orchestrator.d.ts +70 -0
- package/dist/src/orchestrator.js +69 -0
- package/dist/src/orchestrator.js.map +1 -0
- package/dist/src/propagation.d.ts +30 -0
- package/dist/src/propagation.js +76 -0
- package/dist/src/propagation.js.map +1 -0
- package/dist/src/providers/dns/aws.d.ts +28 -0
- package/dist/src/providers/dns/aws.js +103 -0
- package/dist/src/providers/dns/aws.js.map +1 -0
- package/dist/src/providers/dns/cloudflare.d.ts +26 -0
- package/dist/src/providers/dns/cloudflare.js +97 -0
- package/dist/src/providers/dns/cloudflare.js.map +1 -0
- package/dist/src/providers/dns/manual.d.ts +12 -0
- package/dist/src/providers/dns/manual.js +18 -0
- package/dist/src/providers/dns/manual.js.map +1 -0
- package/dist/src/providers/dns/route53.d.ts +34 -0
- package/dist/src/providers/dns/route53.js +176 -0
- package/dist/src/providers/dns/route53.js.map +1 -0
- package/dist/src/providers/email/factory.d.ts +29 -0
- package/dist/src/providers/email/factory.js +67 -0
- package/dist/src/providers/email/factory.js.map +1 -0
- package/dist/src/providers/email/mailgun.d.ts +36 -0
- package/dist/src/providers/email/mailgun.js +141 -0
- package/dist/src/providers/email/mailgun.js.map +1 -0
- package/dist/src/providers/email/postmark.d.ts +37 -0
- package/dist/src/providers/email/postmark.js +121 -0
- package/dist/src/providers/email/postmark.js.map +1 -0
- package/dist/src/providers/email/sendgrid.d.ts +31 -0
- package/dist/src/providers/email/sendgrid.js +109 -0
- package/dist/src/providers/email/sendgrid.js.map +1 -0
- package/dist/src/providers/email/ses.d.ts +39 -0
- package/dist/src/providers/email/ses.js +123 -0
- package/dist/src/providers/email/ses.js.map +1 -0
- package/dist/src/providers/factory.d.ts +14 -0
- package/dist/src/providers/factory.js +32 -0
- package/dist/src/providers/factory.js.map +1 -0
- package/dist/src/records.d.ts +35 -0
- package/dist/src/records.js +109 -0
- package/dist/src/records.js.map +1 -0
- package/dist/src/server.d.ts +67 -0
- package/dist/src/server.js +301 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/types.d.ts +75 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/xml.d.ts +6 -0
- package/dist/src/xml.js +23 -0
- package/dist/src/xml.js.map +1 -0
- package/docs/billing.md +43 -0
- package/docs/getting-started.md +66 -0
- package/docs/hosted-api.md +192 -0
- package/docs/library.md +253 -0
- package/docs/self-hosting.md +153 -0
- package/docs/widget.md +121 -0
- package/package.json +63 -4
- package/web/dashboard.html +669 -0
- package/web/index.html +697 -0
- package/web/widget-frame.html +480 -0
- package/web/widget.js +91 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type FetchLike } from '../../http.js';
|
|
2
|
+
import type { DnsRecord, DomainAuthentication, EmailProvider, InboundAction, InboundParseOptions, ValidationResult } from '../../types.js';
|
|
3
|
+
export interface MailgunOptions {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** Where the Mailgun account lives. Default "us". */
|
|
6
|
+
region?: 'us' | 'eu';
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
fetch?: FetchLike;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Mailgun sending domains (usually a subdomain like mg.example.com): SPF and
|
|
12
|
+
* DKIM TXT records plus a tracking CNAME. Inbound mail is received on the
|
|
13
|
+
* Mailgun domain itself (MX to mxa/mxb.mailgun.org) and forwarded by a route.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MailgunProvider implements EmailProvider {
|
|
16
|
+
private readonly opts;
|
|
17
|
+
readonly name = "mailgun";
|
|
18
|
+
readonly label = "Mailgun";
|
|
19
|
+
readonly supportsInbound = true;
|
|
20
|
+
private readonly baseUrl;
|
|
21
|
+
private readonly fetchImpl;
|
|
22
|
+
constructor(opts: MailgunOptions);
|
|
23
|
+
/** Mailgun only receives for domains added to it, so inbound goes to the sending domain itself. */
|
|
24
|
+
defaultInboundHostname(domain: string): string;
|
|
25
|
+
findDomain(domain: string): Promise<DomainAuthentication | null>;
|
|
26
|
+
ensureDomain(domain: string): Promise<DomainAuthentication>;
|
|
27
|
+
getDomain(id: string): Promise<DomainAuthentication>;
|
|
28
|
+
deleteDomain(id: string): Promise<void>;
|
|
29
|
+
validateDomain(id: string): Promise<ValidationResult>;
|
|
30
|
+
inboundRecords(hostname: string): DnsRecord[];
|
|
31
|
+
ensureInboundParse(opts: InboundParseOptions): Promise<InboundAction>;
|
|
32
|
+
deleteInboundParse(hostname: string): Promise<void>;
|
|
33
|
+
private routes;
|
|
34
|
+
/** Mailgun takes multipart form fields; repeated keys (e.g. `action`) come from arrays. */
|
|
35
|
+
private request;
|
|
36
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ConfigError, ApiError } from '../../errors.js';
|
|
2
|
+
import { requestText } from '../../http.js';
|
|
3
|
+
import { isSpf, normalizeName } from '../../records.js';
|
|
4
|
+
const BASE_URLS = { us: 'https://api.mailgun.net', eu: 'https://api.eu.mailgun.net' };
|
|
5
|
+
const RECEIVING_MX = ['mxa.mailgun.org', 'mxb.mailgun.org'];
|
|
6
|
+
const routeDescription = (hostname) => `dkimkit inbound ${hostname}`;
|
|
7
|
+
/**
|
|
8
|
+
* Mailgun sending domains (usually a subdomain like mg.example.com): SPF and
|
|
9
|
+
* DKIM TXT records plus a tracking CNAME. Inbound mail is received on the
|
|
10
|
+
* Mailgun domain itself (MX to mxa/mxb.mailgun.org) and forwarded by a route.
|
|
11
|
+
*/
|
|
12
|
+
export class MailgunProvider {
|
|
13
|
+
opts;
|
|
14
|
+
name = 'mailgun';
|
|
15
|
+
label = 'Mailgun';
|
|
16
|
+
supportsInbound = true;
|
|
17
|
+
baseUrl;
|
|
18
|
+
fetchImpl;
|
|
19
|
+
constructor(opts) {
|
|
20
|
+
this.opts = opts;
|
|
21
|
+
this.baseUrl = opts.baseUrl ?? BASE_URLS[opts.region ?? 'us'];
|
|
22
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
23
|
+
}
|
|
24
|
+
/** Mailgun only receives for domains added to it, so inbound goes to the sending domain itself. */
|
|
25
|
+
defaultInboundHostname(domain) {
|
|
26
|
+
return normalizeName(domain);
|
|
27
|
+
}
|
|
28
|
+
async findDomain(domain) {
|
|
29
|
+
try {
|
|
30
|
+
return toAuthentication(await this.request('GET', `/v4/domains/${encodeURIComponent(normalizeName(domain))}`), false);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err instanceof ApiError && err.status === 404)
|
|
34
|
+
return null;
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async ensureDomain(domain) {
|
|
39
|
+
const existing = await this.findDomain(domain);
|
|
40
|
+
if (existing)
|
|
41
|
+
return existing;
|
|
42
|
+
const created = await this.request('POST', '/v4/domains', { name: normalizeName(domain), web_scheme: 'https' });
|
|
43
|
+
return toAuthentication(created, true);
|
|
44
|
+
}
|
|
45
|
+
async getDomain(id) {
|
|
46
|
+
return toAuthentication(await this.request('GET', `/v4/domains/${encodeURIComponent(id)}`), false);
|
|
47
|
+
}
|
|
48
|
+
async deleteDomain(id) {
|
|
49
|
+
await this.request('DELETE', `/v3/domains/${encodeURIComponent(id)}`);
|
|
50
|
+
}
|
|
51
|
+
async validateDomain(id) {
|
|
52
|
+
const res = await this.request('PUT', `/v4/domains/${encodeURIComponent(id)}/verify`);
|
|
53
|
+
const records = toAuthentication(res, false).records;
|
|
54
|
+
return {
|
|
55
|
+
valid: res.domain.state === 'active',
|
|
56
|
+
results: (res.sending_dns_records ?? []).map((r, i) => {
|
|
57
|
+
const valid = r.valid === 'valid';
|
|
58
|
+
return { record: records[i]?.purpose ?? r.record_type.toLowerCase(), valid, ...(valid ? {} : { reason: 'Not found in DNS yet' }) };
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
inboundRecords(hostname) {
|
|
63
|
+
return RECEIVING_MX.map((value, i) => ({
|
|
64
|
+
type: 'MX',
|
|
65
|
+
name: normalizeName(hostname),
|
|
66
|
+
value,
|
|
67
|
+
priority: 10,
|
|
68
|
+
purpose: i === 0 ? 'inbound_mx' : 'inbound_mx_2',
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
async ensureInboundParse(opts) {
|
|
72
|
+
const hostname = normalizeName(opts.hostname);
|
|
73
|
+
if (!(await this.findDomain(hostname))) {
|
|
74
|
+
throw new ConfigError(`Mailgun only receives mail for domains added to Mailgun. Use the sending domain (such as mg.${hostname.split('.').slice(-2).join('.')}) as the inbound hostname.`);
|
|
75
|
+
}
|
|
76
|
+
if (/["\\]/.test(opts.url))
|
|
77
|
+
throw new ConfigError('The inbound webhook URL cannot contain quotes or backslashes.');
|
|
78
|
+
const expression = `match_recipient(".*@${hostname.replace(/\./g, '\\.')}")`;
|
|
79
|
+
const actions = [`forward("${opts.url}")`, 'stop()'];
|
|
80
|
+
const route = (await this.routes()).find((r) => r.description === routeDescription(hostname));
|
|
81
|
+
const form = { priority: '10', description: routeDescription(hostname), expression, action: actions };
|
|
82
|
+
if (!route) {
|
|
83
|
+
await this.request('POST', '/v3/routes', form);
|
|
84
|
+
return 'created';
|
|
85
|
+
}
|
|
86
|
+
if (route.expression === expression && JSON.stringify(route.actions ?? []) === JSON.stringify(actions))
|
|
87
|
+
return 'unchanged';
|
|
88
|
+
await this.request('PUT', `/v3/routes/${encodeURIComponent(route.id)}`, form);
|
|
89
|
+
return 'updated';
|
|
90
|
+
}
|
|
91
|
+
async deleteInboundParse(hostname) {
|
|
92
|
+
const route = (await this.routes()).find((r) => r.description === routeDescription(normalizeName(hostname)));
|
|
93
|
+
if (route)
|
|
94
|
+
await this.request('DELETE', `/v3/routes/${encodeURIComponent(route.id)}`);
|
|
95
|
+
}
|
|
96
|
+
async routes() {
|
|
97
|
+
return (await this.request('GET', '/v3/routes?limit=1000')).items ?? [];
|
|
98
|
+
}
|
|
99
|
+
/** Mailgun takes multipart form fields; repeated keys (e.g. `action`) come from arrays. */
|
|
100
|
+
async request(method, path, form) {
|
|
101
|
+
const text = await requestText(this.fetchImpl, 'Mailgun', this.baseUrl + path, () => {
|
|
102
|
+
let body;
|
|
103
|
+
if (form) {
|
|
104
|
+
body = new FormData();
|
|
105
|
+
for (const [k, v] of Object.entries(form))
|
|
106
|
+
for (const item of [v].flat())
|
|
107
|
+
body.append(k, item);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
method,
|
|
111
|
+
headers: { Authorization: `Basic ${Buffer.from(`api:${this.opts.apiKey}`).toString('base64')}`, Accept: 'application/json' },
|
|
112
|
+
body,
|
|
113
|
+
};
|
|
114
|
+
}, (t) => {
|
|
115
|
+
try {
|
|
116
|
+
return JSON.parse(t).message;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return (text ? JSON.parse(text) : {});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function toAuthentication(res, created) {
|
|
126
|
+
const domain = normalizeName(res.domain.name);
|
|
127
|
+
const records = (res.sending_dns_records ?? []).flatMap((r) => {
|
|
128
|
+
const type = r.record_type.toUpperCase();
|
|
129
|
+
if (type !== 'TXT' && type !== 'CNAME')
|
|
130
|
+
return [];
|
|
131
|
+
const name = normalizeName(r.name || domain);
|
|
132
|
+
if (type === 'CNAME')
|
|
133
|
+
return [{ type, name, value: normalizeName(r.value), purpose: 'tracking' }];
|
|
134
|
+
if (isSpf(r.value)) {
|
|
135
|
+
return [{ type, name, value: r.value, purpose: 'spf', note: 'If this name already has an SPF record (v=spf1 …), add include:mailgun.org to it instead of creating a second one.' }];
|
|
136
|
+
}
|
|
137
|
+
return [{ type, name, value: r.value, purpose: name.includes('._domainkey.') ? 'dkim' : 'txt' }];
|
|
138
|
+
});
|
|
139
|
+
return { id: domain, domain, valid: res.domain.state === 'active', records, created };
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=mailgun.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailgun.js","sourceRoot":"","sources":["../../../../src/providers/email/mailgun.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type FetchLike } from '../../http.js';
|
|
2
|
+
import type { DnsRecord, DomainAuthentication, EmailProvider, InboundAction, InboundParseOptions, ValidationResult } from '../../types.js';
|
|
3
|
+
export declare const POSTMARK_INBOUND_MX = "inbound.postmarkapp.com";
|
|
4
|
+
export interface PostmarkOptions {
|
|
5
|
+
/** Account API token (X-Postmark-Account-Token). */
|
|
6
|
+
accountToken: string;
|
|
7
|
+
/** Server whose inbound stream receives mail. Postmark allows one inbound domain per server. */
|
|
8
|
+
inboundServerId?: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
fetch?: FetchLike;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Postmark sender signatures for domains: a DKIM TXT record and a custom
|
|
14
|
+
* Return-Path CNAME. SPF is no longer required by Postmark.
|
|
15
|
+
*/
|
|
16
|
+
export declare class PostmarkProvider implements EmailProvider {
|
|
17
|
+
private readonly opts;
|
|
18
|
+
readonly name = "postmark";
|
|
19
|
+
readonly label = "Postmark";
|
|
20
|
+
readonly supportsInbound = true;
|
|
21
|
+
private readonly baseUrl;
|
|
22
|
+
private readonly fetchImpl;
|
|
23
|
+
constructor(opts: PostmarkOptions);
|
|
24
|
+
defaultInboundHostname(domain: string): string;
|
|
25
|
+
findDomain(domain: string): Promise<DomainAuthentication | null>;
|
|
26
|
+
ensureDomain(domain: string, opts?: {
|
|
27
|
+
subdomain?: string;
|
|
28
|
+
}): Promise<DomainAuthentication>;
|
|
29
|
+
getDomain(id: string): Promise<DomainAuthentication>;
|
|
30
|
+
deleteDomain(id: string): Promise<void>;
|
|
31
|
+
validateDomain(id: string): Promise<ValidationResult>;
|
|
32
|
+
inboundRecords(hostname: string): DnsRecord[];
|
|
33
|
+
ensureInboundParse(opts: InboundParseOptions): Promise<InboundAction>;
|
|
34
|
+
deleteInboundParse(hostname: string): Promise<void>;
|
|
35
|
+
private inboundServer;
|
|
36
|
+
private request;
|
|
37
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ConfigError } from '../../errors.js';
|
|
2
|
+
import { requestJson } from '../../http.js';
|
|
3
|
+
import { normalizeName } from '../../records.js';
|
|
4
|
+
export const POSTMARK_INBOUND_MX = 'inbound.postmarkapp.com';
|
|
5
|
+
const DEFAULT_RETURN_PATH_CNAME = 'pm.mtasv.net';
|
|
6
|
+
/**
|
|
7
|
+
* Postmark sender signatures for domains: a DKIM TXT record and a custom
|
|
8
|
+
* Return-Path CNAME. SPF is no longer required by Postmark.
|
|
9
|
+
*/
|
|
10
|
+
export class PostmarkProvider {
|
|
11
|
+
opts;
|
|
12
|
+
name = 'postmark';
|
|
13
|
+
label = 'Postmark';
|
|
14
|
+
supportsInbound = true;
|
|
15
|
+
baseUrl;
|
|
16
|
+
fetchImpl;
|
|
17
|
+
constructor(opts) {
|
|
18
|
+
this.opts = opts;
|
|
19
|
+
this.baseUrl = opts.baseUrl ?? 'https://api.postmarkapp.com';
|
|
20
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
21
|
+
}
|
|
22
|
+
defaultInboundHostname(domain) {
|
|
23
|
+
return `parse.${normalizeName(domain)}`;
|
|
24
|
+
}
|
|
25
|
+
async findDomain(domain) {
|
|
26
|
+
const wanted = normalizeName(domain);
|
|
27
|
+
for (let offset = 0;; offset += 500) {
|
|
28
|
+
const page = await this.request('GET', `/domains?count=500&offset=${offset}`);
|
|
29
|
+
const hit = page.Domains.find((d) => normalizeName(d.Name) === wanted);
|
|
30
|
+
if (hit)
|
|
31
|
+
return this.getDomain(String(hit.ID));
|
|
32
|
+
if (page.Domains.length === 0 || offset + page.Domains.length >= page.TotalCount)
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async ensureDomain(domain, opts = {}) {
|
|
37
|
+
const existing = await this.findDomain(domain);
|
|
38
|
+
if (existing)
|
|
39
|
+
return existing;
|
|
40
|
+
const name = normalizeName(domain);
|
|
41
|
+
const created = await this.request('POST', '/domains', {
|
|
42
|
+
body: { Name: name, ReturnPathDomain: `${opts.subdomain ?? 'pm-bounces'}.${name}` },
|
|
43
|
+
});
|
|
44
|
+
return toAuthentication(created, true);
|
|
45
|
+
}
|
|
46
|
+
async getDomain(id) {
|
|
47
|
+
return toAuthentication(await this.request('GET', `/domains/${encodeURIComponent(id)}`), false);
|
|
48
|
+
}
|
|
49
|
+
async deleteDomain(id) {
|
|
50
|
+
await this.request('DELETE', `/domains/${encodeURIComponent(id)}`);
|
|
51
|
+
}
|
|
52
|
+
async validateDomain(id) {
|
|
53
|
+
await this.request('PUT', `/domains/${encodeURIComponent(id)}/verifyDkim`);
|
|
54
|
+
// Both calls return the domain; the second has both up-to-date flags.
|
|
55
|
+
const d = await this.request('PUT', `/domains/${encodeURIComponent(id)}/verifyReturnPath`);
|
|
56
|
+
const dkim = Boolean(d.DKIMVerified);
|
|
57
|
+
const returnPath = Boolean(d.ReturnPathDomainVerified);
|
|
58
|
+
return {
|
|
59
|
+
valid: dkim && returnPath,
|
|
60
|
+
results: [
|
|
61
|
+
{ record: 'dkim', valid: dkim, ...(dkim ? {} : { reason: 'DKIM record not found yet' }) },
|
|
62
|
+
{ record: 'return_path', valid: returnPath, ...(returnPath ? {} : { reason: 'Return-Path CNAME not found yet' }) },
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
inboundRecords(hostname) {
|
|
67
|
+
return [{ type: 'MX', name: normalizeName(hostname), value: POSTMARK_INBOUND_MX, priority: 10, purpose: 'inbound_mx' }];
|
|
68
|
+
}
|
|
69
|
+
async ensureInboundParse(opts) {
|
|
70
|
+
const server = await this.inboundServer();
|
|
71
|
+
const hostname = normalizeName(opts.hostname);
|
|
72
|
+
const current = server.InboundDomain ? normalizeName(server.InboundDomain) : '';
|
|
73
|
+
if (current && current !== hostname) {
|
|
74
|
+
throw new ConfigError(`Postmark server ${server.ID} already receives mail for ${current}. Postmark allows one inbound domain per server, so use another server for ${hostname}.`);
|
|
75
|
+
}
|
|
76
|
+
if (current === hostname && server.InboundHookUrl === opts.url)
|
|
77
|
+
return 'unchanged';
|
|
78
|
+
await this.request('PUT', `/servers/${server.ID}`, { body: { InboundDomain: hostname, InboundHookUrl: opts.url } });
|
|
79
|
+
return current ? 'updated' : 'created';
|
|
80
|
+
}
|
|
81
|
+
async deleteInboundParse(hostname) {
|
|
82
|
+
if (!this.opts.inboundServerId)
|
|
83
|
+
return;
|
|
84
|
+
const server = await this.inboundServer();
|
|
85
|
+
if (server.InboundDomain && normalizeName(server.InboundDomain) === normalizeName(hostname)) {
|
|
86
|
+
await this.request('PUT', `/servers/${server.ID}`, { body: { InboundDomain: '', InboundHookUrl: '' } });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
inboundServer() {
|
|
90
|
+
if (!this.opts.inboundServerId)
|
|
91
|
+
throw new ConfigError('Receiving email with Postmark needs the id of the Postmark server to receive on.');
|
|
92
|
+
return this.request('GET', `/servers/${encodeURIComponent(this.opts.inboundServerId)}`);
|
|
93
|
+
}
|
|
94
|
+
request(method, path, opts = {}) {
|
|
95
|
+
return requestJson(this.fetchImpl, 'Postmark', this.baseUrl + path, { ...opts, method, headers: { 'X-Postmark-Account-Token': this.opts.accountToken } }, (body) => body?.Message);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function toAuthentication(d, created) {
|
|
99
|
+
const domain = normalizeName(d.Name);
|
|
100
|
+
// Postmark may give hosts relative to the domain.
|
|
101
|
+
const qualify = (host) => {
|
|
102
|
+
const h = normalizeName(host);
|
|
103
|
+
return h === domain || h.endsWith(`.${domain}`) ? h : `${h}.${domain}`;
|
|
104
|
+
};
|
|
105
|
+
const records = [];
|
|
106
|
+
// A new or rotating key is published at the pending host first.
|
|
107
|
+
const dkimHost = d.DKIMPendingHost || d.DKIMHost;
|
|
108
|
+
const dkimValue = d.DKIMPendingTextValue || d.DKIMTextValue;
|
|
109
|
+
if (dkimHost && dkimValue)
|
|
110
|
+
records.push({ type: 'TXT', name: qualify(dkimHost), value: dkimValue, purpose: 'dkim' });
|
|
111
|
+
if (d.ReturnPathDomain) {
|
|
112
|
+
records.push({
|
|
113
|
+
type: 'CNAME',
|
|
114
|
+
name: qualify(d.ReturnPathDomain),
|
|
115
|
+
value: d.ReturnPathDomainCNAMEValue || DEFAULT_RETURN_PATH_CNAME,
|
|
116
|
+
purpose: 'return_path',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { id: String(d.ID), domain, valid: Boolean(d.DKIMVerified && d.ReturnPathDomainVerified), records, created };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=postmark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postmark.js","sourceRoot":"","sources":["../../../../src/providers/email/postmark.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type FetchLike } from '../../http.js';
|
|
2
|
+
import type { DnsRecord, DomainAuthentication, EmailProvider, InboundAction, InboundParseOptions, ValidationResult } from '../../types.js';
|
|
3
|
+
export declare const SENDGRID_INBOUND_MX = "mx.sendgrid.net";
|
|
4
|
+
export interface SendGridOptions {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/** Act on behalf of a subuser — how multi-tenant apps keep each customer's domains separate. */
|
|
7
|
+
onBehalfOf?: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
fetch?: FetchLike;
|
|
10
|
+
}
|
|
11
|
+
export declare class SendGridProvider implements EmailProvider {
|
|
12
|
+
private readonly opts;
|
|
13
|
+
readonly name = "sendgrid";
|
|
14
|
+
readonly label = "SendGrid";
|
|
15
|
+
readonly supportsInbound = true;
|
|
16
|
+
defaultInboundHostname(domain: string): string;
|
|
17
|
+
private readonly baseUrl;
|
|
18
|
+
private readonly fetchImpl;
|
|
19
|
+
constructor(opts: SendGridOptions);
|
|
20
|
+
findDomain(domain: string, subdomain?: string): Promise<DomainAuthentication | null>;
|
|
21
|
+
ensureDomain(domain: string, opts?: {
|
|
22
|
+
subdomain?: string;
|
|
23
|
+
}): Promise<DomainAuthentication>;
|
|
24
|
+
getDomain(id: string): Promise<DomainAuthentication>;
|
|
25
|
+
deleteDomain(id: string): Promise<void>;
|
|
26
|
+
validateDomain(id: string): Promise<ValidationResult>;
|
|
27
|
+
inboundRecords(hostname: string): DnsRecord[];
|
|
28
|
+
ensureInboundParse(opts: InboundParseOptions): Promise<InboundAction>;
|
|
29
|
+
deleteInboundParse(hostname: string): Promise<void>;
|
|
30
|
+
private request;
|
|
31
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { requestJson } from '../../http.js';
|
|
2
|
+
import { isRecordType, normalizeName } from '../../records.js';
|
|
3
|
+
export const SENDGRID_INBOUND_MX = 'mx.sendgrid.net';
|
|
4
|
+
export class SendGridProvider {
|
|
5
|
+
opts;
|
|
6
|
+
name = 'sendgrid';
|
|
7
|
+
label = 'SendGrid';
|
|
8
|
+
supportsInbound = true;
|
|
9
|
+
defaultInboundHostname(domain) {
|
|
10
|
+
return `parse.${normalizeName(domain)}`;
|
|
11
|
+
}
|
|
12
|
+
baseUrl;
|
|
13
|
+
fetchImpl;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.opts = opts;
|
|
16
|
+
this.baseUrl = opts.baseUrl ?? 'https://api.sendgrid.com/v3';
|
|
17
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
18
|
+
}
|
|
19
|
+
async findDomain(domain, subdomain) {
|
|
20
|
+
// `domain` is a search filter on SendGrid's side, so match exactly here.
|
|
21
|
+
const wanted = normalizeName(domain);
|
|
22
|
+
const list = await this.request(`/whitelabel/domains?domain=${encodeURIComponent(wanted)}&limit=100`);
|
|
23
|
+
const match = list.find((d) => normalizeName(d.domain) === wanted &&
|
|
24
|
+
(!subdomain || d.subdomain?.toLowerCase() === subdomain.toLowerCase()));
|
|
25
|
+
return match ? toAuthentication(match, false) : null;
|
|
26
|
+
}
|
|
27
|
+
async ensureDomain(domain, opts = {}) {
|
|
28
|
+
const existing = await this.findDomain(domain, opts.subdomain);
|
|
29
|
+
if (existing)
|
|
30
|
+
return existing;
|
|
31
|
+
const created = await this.request('/whitelabel/domains', {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
body: { domain: normalizeName(domain), subdomain: opts.subdomain, automatic_security: true },
|
|
34
|
+
});
|
|
35
|
+
return toAuthentication(created, true);
|
|
36
|
+
}
|
|
37
|
+
async getDomain(id) {
|
|
38
|
+
return toAuthentication(await this.request(`/whitelabel/domains/${encodeURIComponent(id)}`), false);
|
|
39
|
+
}
|
|
40
|
+
async deleteDomain(id) {
|
|
41
|
+
await this.request(`/whitelabel/domains/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
42
|
+
}
|
|
43
|
+
async validateDomain(id) {
|
|
44
|
+
const res = await this.request(`/whitelabel/domains/${encodeURIComponent(id)}/validate`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
valid: res.valid,
|
|
49
|
+
results: Object.entries(res.validation_results ?? {}).map(([record, r]) => ({
|
|
50
|
+
record,
|
|
51
|
+
valid: r.valid,
|
|
52
|
+
...(r.reason ? { reason: r.reason } : {}),
|
|
53
|
+
})),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
inboundRecords(hostname) {
|
|
57
|
+
return [{ type: 'MX', name: normalizeName(hostname), value: SENDGRID_INBOUND_MX, priority: 10, purpose: 'inbound_mx' }];
|
|
58
|
+
}
|
|
59
|
+
async ensureInboundParse(opts) {
|
|
60
|
+
const hostname = normalizeName(opts.hostname);
|
|
61
|
+
const desired = { url: opts.url, spam_check: opts.spamCheck ?? false, send_raw: opts.sendRaw ?? false };
|
|
62
|
+
const { result = [] } = await this.request('/user/parse/settings');
|
|
63
|
+
const existing = result.find((s) => normalizeName(s.hostname) === hostname);
|
|
64
|
+
if (!existing) {
|
|
65
|
+
await this.request('/user/parse/settings', { method: 'POST', body: { hostname, ...desired } });
|
|
66
|
+
return 'created';
|
|
67
|
+
}
|
|
68
|
+
if (existing.url === desired.url &&
|
|
69
|
+
Boolean(existing.spam_check) === desired.spam_check &&
|
|
70
|
+
Boolean(existing.send_raw) === desired.send_raw) {
|
|
71
|
+
return 'unchanged';
|
|
72
|
+
}
|
|
73
|
+
await this.request(`/user/parse/settings/${encodeURIComponent(hostname)}`, { method: 'PATCH', body: desired });
|
|
74
|
+
return 'updated';
|
|
75
|
+
}
|
|
76
|
+
async deleteInboundParse(hostname) {
|
|
77
|
+
await this.request(`/user/parse/settings/${encodeURIComponent(normalizeName(hostname))}`, { method: 'DELETE' });
|
|
78
|
+
}
|
|
79
|
+
request(path, opts = {}) {
|
|
80
|
+
const headers = { Authorization: `Bearer ${this.opts.apiKey}` };
|
|
81
|
+
if (this.opts.onBehalfOf)
|
|
82
|
+
headers['on-behalf-of'] = this.opts.onBehalfOf;
|
|
83
|
+
return requestJson(this.fetchImpl, 'SendGrid', this.baseUrl + path, { ...opts, headers }, describeSendGridError);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function toAuthentication(d, created) {
|
|
87
|
+
const records = Object.entries(d.dns ?? {}).flatMap(([purpose, entry]) => {
|
|
88
|
+
const type = entry.type.toUpperCase();
|
|
89
|
+
if (!isRecordType(type))
|
|
90
|
+
return [];
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
type,
|
|
94
|
+
name: normalizeName(entry.host),
|
|
95
|
+
value: entry.data,
|
|
96
|
+
purpose,
|
|
97
|
+
...(type === 'MX' ? { priority: 10 } : {}),
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
});
|
|
101
|
+
return { id: String(d.id), domain: d.domain, valid: d.valid, records, created };
|
|
102
|
+
}
|
|
103
|
+
function describeSendGridError(body) {
|
|
104
|
+
const errors = body?.errors;
|
|
105
|
+
if (!Array.isArray(errors) || errors.length === 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
return errors.map((e) => (e.field ? `${e.message} (${e.field})` : e.message)).join('; ');
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=sendgrid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendgrid.js","sourceRoot":"","sources":["../../../../src/providers/email/sendgrid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type FetchLike } from '../../http.js';
|
|
2
|
+
import type { DnsRecord, DomainAuthentication, EmailProvider, InboundAction, ValidationResult } from '../../types.js';
|
|
3
|
+
import { type AwsCredentials } from '../dns/aws.js';
|
|
4
|
+
export interface SesOptions {
|
|
5
|
+
credentials: AwsCredentials;
|
|
6
|
+
/** The SES region, e.g. us-east-1. */
|
|
7
|
+
region: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
fetch?: FetchLike;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Amazon SES (API v2) domain identities with Easy DKIM (three CNAMEs) and a
|
|
13
|
+
* custom MAIL FROM subdomain (an MX for bounces and an SPF TXT). SES checks DNS
|
|
14
|
+
* by itself, so "validate" reads the current status.
|
|
15
|
+
*/
|
|
16
|
+
export declare class SesProvider implements EmailProvider {
|
|
17
|
+
private readonly opts;
|
|
18
|
+
readonly name = "ses";
|
|
19
|
+
readonly label = "Amazon SES";
|
|
20
|
+
readonly supportsInbound = false;
|
|
21
|
+
private readonly baseUrl;
|
|
22
|
+
private readonly fetchImpl;
|
|
23
|
+
constructor(opts: SesOptions);
|
|
24
|
+
defaultInboundHostname(domain: string): string;
|
|
25
|
+
findDomain(domain: string): Promise<DomainAuthentication | null>;
|
|
26
|
+
/** `subdomain` is the MAIL FROM label (default "mail", giving mail.<domain>). */
|
|
27
|
+
ensureDomain(domain: string, opts?: {
|
|
28
|
+
subdomain?: string;
|
|
29
|
+
}): Promise<DomainAuthentication>;
|
|
30
|
+
getDomain(id: string): Promise<DomainAuthentication>;
|
|
31
|
+
deleteDomain(id: string): Promise<void>;
|
|
32
|
+
validateDomain(id: string): Promise<ValidationResult>;
|
|
33
|
+
inboundRecords(): DnsRecord[];
|
|
34
|
+
ensureInboundParse(): Promise<InboundAction>;
|
|
35
|
+
deleteInboundParse(): Promise<void>;
|
|
36
|
+
private identity;
|
|
37
|
+
private toAuthentication;
|
|
38
|
+
private request;
|
|
39
|
+
}
|