@wraps.dev/cli 2.14.8 → 2.15.1
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/cli.js +629 -74
- package/dist/cli.js.map +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/dist/lambda/inbound-processor/.bundled +1 -1
- package/dist/lambda/inbound-processor/index.js +31 -31
- package/dist/lambda/inbound-processor/index.ts +4 -0
- package/dist/lambda/sms-event-processor/.bundled +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4102,6 +4102,7 @@ var init_route53 = __esm({
|
|
|
4102
4102
|
var metadata_exports = {};
|
|
4103
4103
|
__export(metadata_exports, {
|
|
4104
4104
|
addDomainToMetadata: () => addDomainToMetadata,
|
|
4105
|
+
addInboundDomainToMetadata: () => addInboundDomainToMetadata,
|
|
4105
4106
|
addServiceToConnection: () => addServiceToConnection,
|
|
4106
4107
|
applyConfigUpdates: () => applyConfigUpdates,
|
|
4107
4108
|
buildEmailStackConfig: () => buildEmailStackConfig,
|
|
@@ -4117,7 +4118,9 @@ __export(metadata_exports, {
|
|
|
4117
4118
|
hasService: () => hasService,
|
|
4118
4119
|
listConnections: () => listConnections,
|
|
4119
4120
|
loadConnectionMetadata: () => loadConnectionMetadata,
|
|
4121
|
+
migrateInboundToMultiDomain: () => migrateInboundToMultiDomain,
|
|
4120
4122
|
removeDomainFromMetadata: () => removeDomainFromMetadata,
|
|
4123
|
+
removeInboundDomainFromMetadata: () => removeInboundDomainFromMetadata,
|
|
4121
4124
|
removeServiceFromConnection: () => removeServiceFromConnection,
|
|
4122
4125
|
saveConnectionMetadata: () => saveConnectionMetadata,
|
|
4123
4126
|
updateEmailConfig: () => updateEmailConfig,
|
|
@@ -4180,6 +4183,9 @@ async function loadConnectionMetadata(accountId, region) {
|
|
|
4180
4183
|
}
|
|
4181
4184
|
localData = data;
|
|
4182
4185
|
}
|
|
4186
|
+
if (localData && migrateInboundToMultiDomain(localData)) {
|
|
4187
|
+
await saveConnectionMetadataLocal(localData);
|
|
4188
|
+
}
|
|
4183
4189
|
} catch (error) {
|
|
4184
4190
|
console.error(
|
|
4185
4191
|
"Error loading connection metadata:",
|
|
@@ -4599,6 +4605,61 @@ function getAllTrackedDomains(metadata) {
|
|
|
4599
4605
|
}
|
|
4600
4606
|
return result;
|
|
4601
4607
|
}
|
|
4608
|
+
function migrateInboundToMultiDomain(metadata) {
|
|
4609
|
+
const emailConfig = metadata.services.email?.config;
|
|
4610
|
+
if (!emailConfig?.inbound?.enabled) {
|
|
4611
|
+
return false;
|
|
4612
|
+
}
|
|
4613
|
+
if (emailConfig.inboundDomains && emailConfig.inboundDomains.length > 0) {
|
|
4614
|
+
return false;
|
|
4615
|
+
}
|
|
4616
|
+
const inbound = emailConfig.inbound;
|
|
4617
|
+
const receivingDomain = inbound.receivingDomain || (inbound.subdomain && emailConfig.domain ? `${inbound.subdomain}.${emailConfig.domain}` : null);
|
|
4618
|
+
if (!receivingDomain) {
|
|
4619
|
+
return false;
|
|
4620
|
+
}
|
|
4621
|
+
const parentDomain = emailConfig.domain || "";
|
|
4622
|
+
const subdomain = inbound.subdomain || "inbound";
|
|
4623
|
+
emailConfig.inboundDomains = [
|
|
4624
|
+
{
|
|
4625
|
+
subdomain,
|
|
4626
|
+
receivingDomain,
|
|
4627
|
+
parentDomain,
|
|
4628
|
+
addedAt: metadata.services.email?.deployedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
4629
|
+
}
|
|
4630
|
+
];
|
|
4631
|
+
return true;
|
|
4632
|
+
}
|
|
4633
|
+
function addInboundDomainToMetadata(metadata, entry) {
|
|
4634
|
+
if (!metadata.services.email) {
|
|
4635
|
+
throw new Error("Email service not configured in metadata");
|
|
4636
|
+
}
|
|
4637
|
+
const config2 = metadata.services.email.config;
|
|
4638
|
+
const existing = config2.inboundDomains ?? [];
|
|
4639
|
+
const idx = existing.findIndex(
|
|
4640
|
+
(d) => d.receivingDomain === entry.receivingDomain
|
|
4641
|
+
);
|
|
4642
|
+
if (idx >= 0) {
|
|
4643
|
+
existing[idx] = entry;
|
|
4644
|
+
} else {
|
|
4645
|
+
existing.push(entry);
|
|
4646
|
+
}
|
|
4647
|
+
config2.inboundDomains = existing;
|
|
4648
|
+
metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4649
|
+
}
|
|
4650
|
+
function removeInboundDomainFromMetadata(metadata, receivingDomain) {
|
|
4651
|
+
if (!metadata.services.email) {
|
|
4652
|
+
return;
|
|
4653
|
+
}
|
|
4654
|
+
const config2 = metadata.services.email.config;
|
|
4655
|
+
if (!config2.inboundDomains) {
|
|
4656
|
+
return;
|
|
4657
|
+
}
|
|
4658
|
+
config2.inboundDomains = config2.inboundDomains.filter(
|
|
4659
|
+
(d) => d.receivingDomain !== receivingDomain
|
|
4660
|
+
);
|
|
4661
|
+
metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4662
|
+
}
|
|
4602
4663
|
var init_metadata = __esm({
|
|
4603
4664
|
"src/utils/shared/metadata.ts"() {
|
|
4604
4665
|
"use strict";
|
|
@@ -5781,14 +5842,18 @@ var init_lambda = __esm({
|
|
|
5781
5842
|
var acm_exports = {};
|
|
5782
5843
|
__export(acm_exports, {
|
|
5783
5844
|
checkCertificateValidation: () => checkCertificateValidation,
|
|
5784
|
-
createACMCertificate: () => createACMCertificate
|
|
5845
|
+
createACMCertificate: () => createACMCertificate,
|
|
5846
|
+
getCertificateValidationRecords: () => getCertificateValidationRecords
|
|
5785
5847
|
});
|
|
5786
|
-
import {
|
|
5848
|
+
import {
|
|
5849
|
+
ACMClient as ACMClient2,
|
|
5850
|
+
DescribeCertificateCommand as DescribeCertificateCommand2,
|
|
5851
|
+
ListCertificatesCommand
|
|
5852
|
+
} from "@aws-sdk/client-acm";
|
|
5787
5853
|
import * as aws12 from "@pulumi/aws";
|
|
5788
5854
|
async function checkCertificateValidation(domain) {
|
|
5789
5855
|
try {
|
|
5790
5856
|
const acm3 = new ACMClient2({ region: "us-east-1" });
|
|
5791
|
-
const { ListCertificatesCommand } = await import("@aws-sdk/client-acm");
|
|
5792
5857
|
const listResponse = await acm3.send(
|
|
5793
5858
|
new ListCertificatesCommand({
|
|
5794
5859
|
CertificateStatuses: ["ISSUED"]
|
|
@@ -5864,6 +5929,31 @@ async function createACMCertificate(config2) {
|
|
|
5864
5929
|
validationRecords
|
|
5865
5930
|
};
|
|
5866
5931
|
}
|
|
5932
|
+
async function getCertificateValidationRecords(domain) {
|
|
5933
|
+
const acm3 = new ACMClient2({ region: "us-east-1" });
|
|
5934
|
+
const listResponse = await acm3.send(
|
|
5935
|
+
new ListCertificatesCommand({
|
|
5936
|
+
CertificateStatuses: ["PENDING_VALIDATION", "ISSUED"]
|
|
5937
|
+
})
|
|
5938
|
+
);
|
|
5939
|
+
const cert = listResponse.CertificateSummaryList?.find(
|
|
5940
|
+
(c) => c.DomainName === domain
|
|
5941
|
+
);
|
|
5942
|
+
if (!cert?.CertificateArn) {
|
|
5943
|
+
return [];
|
|
5944
|
+
}
|
|
5945
|
+
const describeResponse = await acm3.send(
|
|
5946
|
+
new DescribeCertificateCommand2({
|
|
5947
|
+
CertificateArn: cert.CertificateArn
|
|
5948
|
+
})
|
|
5949
|
+
);
|
|
5950
|
+
const options = describeResponse.Certificate?.DomainValidationOptions ?? [];
|
|
5951
|
+
return options.filter((opt) => opt.ResourceRecord?.Name && opt.ResourceRecord?.Value).map((opt) => ({
|
|
5952
|
+
name: opt.ResourceRecord.Name,
|
|
5953
|
+
type: opt.ResourceRecord.Type ?? "CNAME",
|
|
5954
|
+
value: opt.ResourceRecord.Value
|
|
5955
|
+
}));
|
|
5956
|
+
}
|
|
5867
5957
|
var init_acm = __esm({
|
|
5868
5958
|
"src/infrastructure/resources/acm.ts"() {
|
|
5869
5959
|
"use strict";
|
|
@@ -6638,7 +6728,7 @@ var init_cloudflare = __esm({
|
|
|
6638
6728
|
};
|
|
6639
6729
|
}
|
|
6640
6730
|
async createEmailRecords(data) {
|
|
6641
|
-
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
6731
|
+
const { domain, dkimTokens, mailFromDomain, customTrackingDomain, region } = data;
|
|
6642
6732
|
const errors2 = [];
|
|
6643
6733
|
let recordsCreated = 0;
|
|
6644
6734
|
try {
|
|
@@ -6675,6 +6765,20 @@ var init_cloudflare = __esm({
|
|
|
6675
6765
|
} else {
|
|
6676
6766
|
errors2.push(`Failed to create DMARC record for ${domain}`);
|
|
6677
6767
|
}
|
|
6768
|
+
if (customTrackingDomain) {
|
|
6769
|
+
const trackingSuccess = await this.createRecord(
|
|
6770
|
+
customTrackingDomain,
|
|
6771
|
+
"CNAME",
|
|
6772
|
+
`r.${region}.awstrack.me`
|
|
6773
|
+
);
|
|
6774
|
+
if (trackingSuccess) {
|
|
6775
|
+
recordsCreated++;
|
|
6776
|
+
} else {
|
|
6777
|
+
errors2.push(
|
|
6778
|
+
`Failed to create tracking CNAME for ${customTrackingDomain}`
|
|
6779
|
+
);
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6678
6782
|
if (mailFromDomain) {
|
|
6679
6783
|
const mxSuccess = await this.createRecord(
|
|
6680
6784
|
mailFromDomain,
|
|
@@ -7006,7 +7110,7 @@ var init_vercel = __esm({
|
|
|
7006
7110
|
};
|
|
7007
7111
|
}
|
|
7008
7112
|
async createEmailRecords(data) {
|
|
7009
|
-
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
7113
|
+
const { domain, dkimTokens, mailFromDomain, customTrackingDomain, region } = data;
|
|
7010
7114
|
const errors2 = [];
|
|
7011
7115
|
let recordsCreated = 0;
|
|
7012
7116
|
try {
|
|
@@ -7043,6 +7147,20 @@ var init_vercel = __esm({
|
|
|
7043
7147
|
} else {
|
|
7044
7148
|
errors2.push(`Failed to create DMARC record for ${domain}`);
|
|
7045
7149
|
}
|
|
7150
|
+
if (customTrackingDomain) {
|
|
7151
|
+
const trackingSuccess = await this.createRecord(
|
|
7152
|
+
customTrackingDomain,
|
|
7153
|
+
"CNAME",
|
|
7154
|
+
`r.${region}.awstrack.me`
|
|
7155
|
+
);
|
|
7156
|
+
if (trackingSuccess) {
|
|
7157
|
+
recordsCreated++;
|
|
7158
|
+
} else {
|
|
7159
|
+
errors2.push(
|
|
7160
|
+
`Failed to create tracking CNAME for ${customTrackingDomain}`
|
|
7161
|
+
);
|
|
7162
|
+
}
|
|
7163
|
+
}
|
|
7046
7164
|
if (mailFromDomain) {
|
|
7047
7165
|
const mxSuccess = await this.createRecord(
|
|
7048
7166
|
mailFromDomain,
|
|
@@ -7258,7 +7376,7 @@ import {
|
|
|
7258
7376
|
Route53Client as Route53Client2
|
|
7259
7377
|
} from "@aws-sdk/client-route-53";
|
|
7260
7378
|
function buildEmailDNSRecords(data) {
|
|
7261
|
-
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
7379
|
+
const { domain, dkimTokens, mailFromDomain, customTrackingDomain, region } = data;
|
|
7262
7380
|
const records = [];
|
|
7263
7381
|
for (const token of dkimTokens) {
|
|
7264
7382
|
records.push({
|
|
@@ -7281,6 +7399,14 @@ function buildEmailDNSRecords(data) {
|
|
|
7281
7399
|
value: `v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}`,
|
|
7282
7400
|
category: "dmarc"
|
|
7283
7401
|
});
|
|
7402
|
+
if (customTrackingDomain) {
|
|
7403
|
+
records.push({
|
|
7404
|
+
name: customTrackingDomain,
|
|
7405
|
+
type: "CNAME",
|
|
7406
|
+
value: `r.${region}.awstrack.me`,
|
|
7407
|
+
category: "tracking"
|
|
7408
|
+
});
|
|
7409
|
+
}
|
|
7284
7410
|
if (mailFromDomain) {
|
|
7285
7411
|
records.push({
|
|
7286
7412
|
name: mailFromDomain,
|
|
@@ -7341,8 +7467,7 @@ async function createDNSRecordsForProvider(credentials, data, selectedCategories
|
|
|
7341
7467
|
data.dkimTokens,
|
|
7342
7468
|
data.region,
|
|
7343
7469
|
categories,
|
|
7344
|
-
|
|
7345
|
-
// customTrackingDomain - not used here
|
|
7470
|
+
data.customTrackingDomain,
|
|
7346
7471
|
data.mailFromDomain
|
|
7347
7472
|
);
|
|
7348
7473
|
let recordsCreated = 0;
|
|
@@ -19098,7 +19223,8 @@ import {
|
|
|
19098
19223
|
DescribeActiveReceiptRuleSetCommand,
|
|
19099
19224
|
DescribeReceiptRuleCommand,
|
|
19100
19225
|
SESClient as SESClient3,
|
|
19101
|
-
SetActiveReceiptRuleSetCommand
|
|
19226
|
+
SetActiveReceiptRuleSetCommand,
|
|
19227
|
+
UpdateReceiptRuleCommand
|
|
19102
19228
|
} from "@aws-sdk/client-ses";
|
|
19103
19229
|
var RULE_SET_NAME = "wraps-inbound-rules";
|
|
19104
19230
|
var RULE_NAME = "wraps-inbound-catch-all";
|
|
@@ -19221,6 +19347,77 @@ async function deleteReceiptRuleSet(region) {
|
|
|
19221
19347
|
throw error;
|
|
19222
19348
|
}
|
|
19223
19349
|
}
|
|
19350
|
+
async function addDomainToReceiptRule(region, domain, s3BucketName) {
|
|
19351
|
+
const ses = createSESClient(region);
|
|
19352
|
+
try {
|
|
19353
|
+
const response = await ses.send(
|
|
19354
|
+
new DescribeReceiptRuleCommand({
|
|
19355
|
+
RuleSetName: RULE_SET_NAME,
|
|
19356
|
+
RuleName: RULE_NAME
|
|
19357
|
+
})
|
|
19358
|
+
);
|
|
19359
|
+
const existingRecipients = response.Rule?.Recipients ?? [];
|
|
19360
|
+
if (existingRecipients.includes(domain)) {
|
|
19361
|
+
return;
|
|
19362
|
+
}
|
|
19363
|
+
await ses.send(
|
|
19364
|
+
new UpdateReceiptRuleCommand({
|
|
19365
|
+
RuleSetName: RULE_SET_NAME,
|
|
19366
|
+
Rule: {
|
|
19367
|
+
Name: RULE_NAME,
|
|
19368
|
+
Enabled: response.Rule?.Enabled,
|
|
19369
|
+
ScanEnabled: response.Rule?.ScanEnabled,
|
|
19370
|
+
TlsPolicy: response.Rule?.TlsPolicy,
|
|
19371
|
+
Actions: response.Rule?.Actions,
|
|
19372
|
+
Recipients: [...existingRecipients, domain]
|
|
19373
|
+
}
|
|
19374
|
+
})
|
|
19375
|
+
);
|
|
19376
|
+
} catch (error) {
|
|
19377
|
+
if (error instanceof Error && (error.name === "RuleDoesNotExistException" || error.name === "RuleSetDoesNotExistException")) {
|
|
19378
|
+
await createReceiptRuleSet(region);
|
|
19379
|
+
await createReceiptRule(region, domain, s3BucketName);
|
|
19380
|
+
await setActiveReceiptRuleSet(region, RULE_SET_NAME);
|
|
19381
|
+
return;
|
|
19382
|
+
}
|
|
19383
|
+
throw error;
|
|
19384
|
+
}
|
|
19385
|
+
}
|
|
19386
|
+
async function removeDomainFromReceiptRule(region, domain) {
|
|
19387
|
+
const ses = createSESClient(region);
|
|
19388
|
+
try {
|
|
19389
|
+
const response = await ses.send(
|
|
19390
|
+
new DescribeReceiptRuleCommand({
|
|
19391
|
+
RuleSetName: RULE_SET_NAME,
|
|
19392
|
+
RuleName: RULE_NAME
|
|
19393
|
+
})
|
|
19394
|
+
);
|
|
19395
|
+
const existingRecipients = response.Rule?.Recipients ?? [];
|
|
19396
|
+
const updated = existingRecipients.filter((r) => r !== domain);
|
|
19397
|
+
if (updated.length === 0) {
|
|
19398
|
+
await deleteReceiptRule(region);
|
|
19399
|
+
return;
|
|
19400
|
+
}
|
|
19401
|
+
await ses.send(
|
|
19402
|
+
new UpdateReceiptRuleCommand({
|
|
19403
|
+
RuleSetName: RULE_SET_NAME,
|
|
19404
|
+
Rule: {
|
|
19405
|
+
Name: RULE_NAME,
|
|
19406
|
+
Enabled: response.Rule?.Enabled,
|
|
19407
|
+
ScanEnabled: response.Rule?.ScanEnabled,
|
|
19408
|
+
TlsPolicy: response.Rule?.TlsPolicy,
|
|
19409
|
+
Actions: response.Rule?.Actions,
|
|
19410
|
+
Recipients: updated
|
|
19411
|
+
}
|
|
19412
|
+
})
|
|
19413
|
+
);
|
|
19414
|
+
} catch (error) {
|
|
19415
|
+
if (error instanceof Error && (error.name === "RuleDoesNotExistException" || error.name === "RuleSetDoesNotExistException")) {
|
|
19416
|
+
return;
|
|
19417
|
+
}
|
|
19418
|
+
throw error;
|
|
19419
|
+
}
|
|
19420
|
+
}
|
|
19224
19421
|
|
|
19225
19422
|
// src/commands/email/inbound.ts
|
|
19226
19423
|
init_aws();
|
|
@@ -19299,7 +19496,15 @@ async function inboundInit(options) {
|
|
|
19299
19496
|
bucketName: `wraps-inbound-${identity.accountId}-${region}`,
|
|
19300
19497
|
webhookUrl,
|
|
19301
19498
|
webhookSecret
|
|
19302
|
-
}
|
|
19499
|
+
},
|
|
19500
|
+
inboundDomains: [
|
|
19501
|
+
{
|
|
19502
|
+
subdomain,
|
|
19503
|
+
receivingDomain,
|
|
19504
|
+
parentDomain: domain,
|
|
19505
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19506
|
+
}
|
|
19507
|
+
]
|
|
19303
19508
|
};
|
|
19304
19509
|
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
19305
19510
|
emailConfig: updatedEmailConfig
|
|
@@ -19540,7 +19745,8 @@ Deploy first: ${pc21.cyan("wraps email inbound init")}
|
|
|
19540
19745
|
const stackName = emailService.pulumiStackName || `wraps-${identity.accountId}-${region}`;
|
|
19541
19746
|
const updatedEmailConfig = {
|
|
19542
19747
|
...emailService.config,
|
|
19543
|
-
inbound: void 0
|
|
19748
|
+
inbound: void 0,
|
|
19749
|
+
inboundDomains: void 0
|
|
19544
19750
|
};
|
|
19545
19751
|
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
19546
19752
|
emailConfig: updatedEmailConfig
|
|
@@ -19609,13 +19815,18 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19609
19815
|
`);
|
|
19610
19816
|
return;
|
|
19611
19817
|
}
|
|
19612
|
-
const
|
|
19818
|
+
const emailConfig = metadata.services.email.config;
|
|
19819
|
+
const inbound = emailConfig.inbound;
|
|
19820
|
+
const inboundDomains = emailConfig.inboundDomains ?? [];
|
|
19613
19821
|
const activeRuleSet = await getActiveReceiptRuleSet(region);
|
|
19614
|
-
const
|
|
19822
|
+
const domainList = inboundDomains.length > 0 ? inboundDomains.map((d) => d.receivingDomain) : [
|
|
19823
|
+
inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`
|
|
19824
|
+
];
|
|
19615
19825
|
if (isJsonMode()) {
|
|
19616
19826
|
jsonSuccess("email.inbound.status", {
|
|
19617
19827
|
enabled: true,
|
|
19618
|
-
|
|
19828
|
+
receivingDomains: domainList,
|
|
19829
|
+
receivingDomain: domainList[0],
|
|
19619
19830
|
bucketName: inbound.bucketName || "",
|
|
19620
19831
|
region,
|
|
19621
19832
|
webhookUrl: inbound.webhookUrl || null,
|
|
@@ -19627,7 +19838,14 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19627
19838
|
console.log();
|
|
19628
19839
|
console.log(pc21.bold(" Inbound Email Configuration"));
|
|
19629
19840
|
console.log();
|
|
19630
|
-
|
|
19841
|
+
if (domainList.length === 1) {
|
|
19842
|
+
console.log(` ${pc21.dim("Receiving domain:")} ${pc21.cyan(domainList[0])}`);
|
|
19843
|
+
} else {
|
|
19844
|
+
console.log(` ${pc21.dim("Receiving domains:")}`);
|
|
19845
|
+
for (const d of domainList) {
|
|
19846
|
+
console.log(` ${pc21.cyan(d)}`);
|
|
19847
|
+
}
|
|
19848
|
+
}
|
|
19631
19849
|
console.log(
|
|
19632
19850
|
` ${pc21.dim("S3 bucket:")} ${pc21.cyan(inbound.bucketName || "")}`
|
|
19633
19851
|
);
|
|
@@ -19661,61 +19879,77 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19661
19879
|
`);
|
|
19662
19880
|
process.exit(1);
|
|
19663
19881
|
}
|
|
19664
|
-
const
|
|
19665
|
-
const
|
|
19882
|
+
const emailConfig = metadata.services.email.config;
|
|
19883
|
+
const inbound = emailConfig.inbound;
|
|
19884
|
+
const inboundDomains = emailConfig.inboundDomains ?? [];
|
|
19885
|
+
const domainList = inboundDomains.length > 0 ? inboundDomains.map((d) => d.receivingDomain) : [
|
|
19886
|
+
inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`
|
|
19887
|
+
];
|
|
19666
19888
|
let allPassed = true;
|
|
19889
|
+
const domainChecks = {};
|
|
19667
19890
|
console.log();
|
|
19668
|
-
const
|
|
19669
|
-
|
|
19670
|
-
|
|
19671
|
-
try {
|
|
19672
|
-
const records = await dns2.resolveMx(receivingDomain);
|
|
19673
|
-
const hasSES = records.some((r) => r.exchange.includes("inbound-smtp"));
|
|
19674
|
-
return { found: true, hasSES, records };
|
|
19675
|
-
} catch {
|
|
19676
|
-
return { found: false, hasSES: false, records: [] };
|
|
19677
|
-
}
|
|
19891
|
+
for (const receivingDomain of domainList) {
|
|
19892
|
+
if (domainList.length > 1) {
|
|
19893
|
+
clack20.log.info(pc21.bold(`Checking ${pc21.cyan(receivingDomain)}`));
|
|
19678
19894
|
}
|
|
19679
|
-
|
|
19680
|
-
|
|
19681
|
-
|
|
19682
|
-
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19688
|
-
|
|
19689
|
-
|
|
19690
|
-
|
|
19691
|
-
`MX record: ${pc21.red("not found")}. Add: ${pc21.cyan(`${receivingDomain} MX 10 inbound-smtp.${region}.amazonaws.com`)}`
|
|
19692
|
-
);
|
|
19693
|
-
allPassed = false;
|
|
19694
|
-
}
|
|
19695
|
-
const spfResult = await progress.execute(
|
|
19696
|
-
`Checking SPF record for ${receivingDomain}`,
|
|
19697
|
-
async () => {
|
|
19698
|
-
try {
|
|
19699
|
-
const records = await dns2.resolveTxt(receivingDomain);
|
|
19700
|
-
const flat = records.map((r) => r.join(""));
|
|
19701
|
-
const spf = flat.find((r) => r.startsWith("v=spf1"));
|
|
19702
|
-
const hasSES = spf?.includes("amazonses.com") ?? false;
|
|
19703
|
-
return { found: !!spf, hasSES, value: spf };
|
|
19704
|
-
} catch {
|
|
19705
|
-
return { found: false, hasSES: false, value: null };
|
|
19895
|
+
const mxResult = await progress.execute(
|
|
19896
|
+
`Checking MX record for ${receivingDomain}`,
|
|
19897
|
+
async () => {
|
|
19898
|
+
try {
|
|
19899
|
+
const records = await dns2.resolveMx(receivingDomain);
|
|
19900
|
+
const hasSES = records.some(
|
|
19901
|
+
(r) => r.exchange.includes("inbound-smtp")
|
|
19902
|
+
);
|
|
19903
|
+
return { found: true, hasSES, records };
|
|
19904
|
+
} catch {
|
|
19905
|
+
return { found: false, hasSES: false, records: [] };
|
|
19906
|
+
}
|
|
19706
19907
|
}
|
|
19908
|
+
);
|
|
19909
|
+
if (mxResult.hasSES) {
|
|
19910
|
+
clack20.log.success(
|
|
19911
|
+
`MX record: ${pc21.green("verified")} \u2192 inbound-smtp.${region}.amazonaws.com`
|
|
19912
|
+
);
|
|
19913
|
+
} else if (mxResult.found) {
|
|
19914
|
+
clack20.log.warn(
|
|
19915
|
+
`MX record found but not pointing to SES. Expected: ${pc21.cyan(`10 inbound-smtp.${region}.amazonaws.com`)}`
|
|
19916
|
+
);
|
|
19917
|
+
allPassed = false;
|
|
19918
|
+
} else {
|
|
19919
|
+
clack20.log.error(
|
|
19920
|
+
`MX record: ${pc21.red("not found")}. Add: ${pc21.cyan(`${receivingDomain} MX 10 inbound-smtp.${region}.amazonaws.com`)}`
|
|
19921
|
+
);
|
|
19922
|
+
allPassed = false;
|
|
19707
19923
|
}
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
|
|
19711
|
-
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
|
|
19716
|
-
|
|
19924
|
+
const spfResult = await progress.execute(
|
|
19925
|
+
`Checking SPF record for ${receivingDomain}`,
|
|
19926
|
+
async () => {
|
|
19927
|
+
try {
|
|
19928
|
+
const records = await dns2.resolveTxt(receivingDomain);
|
|
19929
|
+
const flat = records.map((r) => r.join(""));
|
|
19930
|
+
const spf = flat.find((r) => r.startsWith("v=spf1"));
|
|
19931
|
+
const hasSES = spf?.includes("amazonses.com") ?? false;
|
|
19932
|
+
return { found: !!spf, hasSES, value: spf };
|
|
19933
|
+
} catch {
|
|
19934
|
+
return { found: false, hasSES: false, value: null };
|
|
19935
|
+
}
|
|
19936
|
+
}
|
|
19717
19937
|
);
|
|
19718
|
-
|
|
19938
|
+
if (spfResult.hasSES) {
|
|
19939
|
+
clack20.log.success(`SPF record: ${pc21.green("verified")}`);
|
|
19940
|
+
} else if (spfResult.found) {
|
|
19941
|
+
clack20.log.warn("SPF record exists but missing amazonses.com include");
|
|
19942
|
+
allPassed = false;
|
|
19943
|
+
} else {
|
|
19944
|
+
clack20.log.error(
|
|
19945
|
+
`SPF record: ${pc21.red("not found")}. Add TXT: ${pc21.cyan("v=spf1 include:amazonses.com ~all")}`
|
|
19946
|
+
);
|
|
19947
|
+
allPassed = false;
|
|
19948
|
+
}
|
|
19949
|
+
domainChecks[receivingDomain] = {
|
|
19950
|
+
mx: { found: mxResult.found, verified: mxResult.hasSES },
|
|
19951
|
+
spf: { found: spfResult.found, verified: spfResult.hasSES }
|
|
19952
|
+
};
|
|
19719
19953
|
}
|
|
19720
19954
|
const activeRuleSet = await getActiveReceiptRuleSet(region);
|
|
19721
19955
|
if (activeRuleSet === RULE_SET_NAME) {
|
|
@@ -19728,13 +19962,11 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19728
19962
|
}
|
|
19729
19963
|
if (isJsonMode()) {
|
|
19730
19964
|
jsonSuccess("email.inbound.verify", {
|
|
19731
|
-
|
|
19965
|
+
receivingDomains: domainList,
|
|
19966
|
+
receivingDomain: domainList[0],
|
|
19732
19967
|
allPassed,
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
spf: { found: spfResult.found, verified: spfResult.hasSES },
|
|
19736
|
-
receiptRuleSet: { active: activeRuleSet === RULE_SET_NAME }
|
|
19737
|
-
}
|
|
19968
|
+
domainChecks,
|
|
19969
|
+
receiptRuleSet: { active: activeRuleSet === RULE_SET_NAME }
|
|
19738
19970
|
});
|
|
19739
19971
|
return;
|
|
19740
19972
|
}
|
|
@@ -19891,6 +20123,296 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19891
20123
|
clack20.log.success(pc21.bold("Inbound email is working correctly!"));
|
|
19892
20124
|
console.log();
|
|
19893
20125
|
}
|
|
20126
|
+
async function inboundAdd(options) {
|
|
20127
|
+
if (!isJsonMode()) {
|
|
20128
|
+
clack20.intro(pc21.bold("Add Inbound Receiving Domain"));
|
|
20129
|
+
}
|
|
20130
|
+
const progress = new DeploymentProgress();
|
|
20131
|
+
const identity = await progress.execute(
|
|
20132
|
+
"Validating AWS credentials",
|
|
20133
|
+
async () => validateAWSCredentials()
|
|
20134
|
+
);
|
|
20135
|
+
const region = options.region || await getAWSRegion();
|
|
20136
|
+
if (!SES_RECEIVING_REGIONS.includes(
|
|
20137
|
+
region
|
|
20138
|
+
)) {
|
|
20139
|
+
throw errors.inboundRegionNotSupported(region);
|
|
20140
|
+
}
|
|
20141
|
+
const metadata = await loadConnectionMetadata(identity.accountId, region);
|
|
20142
|
+
if (!metadata?.services?.email?.config?.inbound?.enabled) {
|
|
20143
|
+
clack20.log.error("Inbound email infrastructure is not deployed.");
|
|
20144
|
+
console.log(`
|
|
20145
|
+
Deploy first: ${pc21.cyan("wraps email inbound init")}
|
|
20146
|
+
`);
|
|
20147
|
+
process.exit(1);
|
|
20148
|
+
}
|
|
20149
|
+
const emailConfig = metadata.services.email.config;
|
|
20150
|
+
const primaryDomain = emailConfig.domain || "";
|
|
20151
|
+
const allDomains = [primaryDomain];
|
|
20152
|
+
for (const d of emailConfig.additionalDomains ?? []) {
|
|
20153
|
+
if (!allDomains.includes(d.domain)) {
|
|
20154
|
+
allDomains.push(d.domain);
|
|
20155
|
+
}
|
|
20156
|
+
}
|
|
20157
|
+
let parentDomain = options.domain;
|
|
20158
|
+
if (!parentDomain) {
|
|
20159
|
+
if (options.yes) {
|
|
20160
|
+
parentDomain = primaryDomain;
|
|
20161
|
+
} else if (allDomains.length === 1) {
|
|
20162
|
+
parentDomain = allDomains[0];
|
|
20163
|
+
} else {
|
|
20164
|
+
const selected = await clack20.select({
|
|
20165
|
+
message: "Which domain should the inbound subdomain be under?",
|
|
20166
|
+
options: allDomains.map((d) => ({
|
|
20167
|
+
value: d,
|
|
20168
|
+
label: d,
|
|
20169
|
+
hint: d === primaryDomain ? "primary" : void 0
|
|
20170
|
+
}))
|
|
20171
|
+
});
|
|
20172
|
+
if (clack20.isCancel(selected)) {
|
|
20173
|
+
clack20.cancel("Operation cancelled.");
|
|
20174
|
+
process.exit(0);
|
|
20175
|
+
}
|
|
20176
|
+
parentDomain = selected;
|
|
20177
|
+
}
|
|
20178
|
+
}
|
|
20179
|
+
const subdomain = options.subdomain || (options.yes ? "inbound" : await promptInboundSubdomain(parentDomain));
|
|
20180
|
+
const receivingDomain = `${subdomain}.${parentDomain}`;
|
|
20181
|
+
const existingDomains = emailConfig.inboundDomains ?? [];
|
|
20182
|
+
if (existingDomains.some((d) => d.receivingDomain === receivingDomain)) {
|
|
20183
|
+
clack20.log.warn(
|
|
20184
|
+
`${pc21.cyan(receivingDomain)} is already configured as an inbound domain.`
|
|
20185
|
+
);
|
|
20186
|
+
return;
|
|
20187
|
+
}
|
|
20188
|
+
clack20.log.info(`Adding receiving domain: ${pc21.cyan(receivingDomain)}`);
|
|
20189
|
+
const bucketName = emailConfig.inbound?.bucketName || `wraps-inbound-${identity.accountId}-${region}`;
|
|
20190
|
+
await progress.execute("Updating SES receipt rule", async () => {
|
|
20191
|
+
await addDomainToReceiptRule(region, receivingDomain, bucketName);
|
|
20192
|
+
});
|
|
20193
|
+
let dnsAutoCreated = false;
|
|
20194
|
+
const {
|
|
20195
|
+
detectAvailableDNSProviders: detectAvailableDNSProviders2,
|
|
20196
|
+
getDNSCredentials: getDNSCredentials2,
|
|
20197
|
+
createInboundDNSRecordsForProvider: createInboundDNSRecordsForProvider2,
|
|
20198
|
+
getDNSProviderDisplayName: getDNSProviderDisplayName2,
|
|
20199
|
+
getDNSProviderTokenUrl: getDNSProviderTokenUrl2,
|
|
20200
|
+
buildInboundDNSRecords: buildRecords,
|
|
20201
|
+
formatDNSRecordsForDisplay: formatRecords
|
|
20202
|
+
} = await Promise.resolve().then(() => (init_dns(), dns_exports));
|
|
20203
|
+
const { promptDNSProvider: promptDNSProvider2, promptContinueManualDNS: promptContinueManualDNS2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
20204
|
+
const existingDnsProvider = metadata.services.email.dnsProvider;
|
|
20205
|
+
let dnsProvider = existingDnsProvider;
|
|
20206
|
+
if (!dnsProvider || dnsProvider === "manual") {
|
|
20207
|
+
progress.start("Detecting DNS providers");
|
|
20208
|
+
const availableProviders = await detectAvailableDNSProviders2(
|
|
20209
|
+
parentDomain,
|
|
20210
|
+
region
|
|
20211
|
+
);
|
|
20212
|
+
progress.stop();
|
|
20213
|
+
dnsProvider = options.yes ? "manual" : await promptDNSProvider2(parentDomain, availableProviders);
|
|
20214
|
+
}
|
|
20215
|
+
if (dnsProvider !== "manual") {
|
|
20216
|
+
progress.start(
|
|
20217
|
+
`Validating ${getDNSProviderDisplayName2(dnsProvider)} credentials`
|
|
20218
|
+
);
|
|
20219
|
+
const credentialResult = await getDNSCredentials2(
|
|
20220
|
+
dnsProvider,
|
|
20221
|
+
parentDomain,
|
|
20222
|
+
region
|
|
20223
|
+
);
|
|
20224
|
+
progress.stop();
|
|
20225
|
+
if (credentialResult.valid && credentialResult.credentials) {
|
|
20226
|
+
const records = buildRecords(receivingDomain, region);
|
|
20227
|
+
clack20.log.info(pc21.bold("DNS records to create:"));
|
|
20228
|
+
for (const record of records) {
|
|
20229
|
+
const value = record.priority ? `${record.priority} ${record.value}` : record.value;
|
|
20230
|
+
clack20.log.info(pc21.dim(` ${record.type} ${record.name} \u2192 ${value}`));
|
|
20231
|
+
}
|
|
20232
|
+
progress.start(
|
|
20233
|
+
`Creating DNS records in ${getDNSProviderDisplayName2(dnsProvider)}`
|
|
20234
|
+
);
|
|
20235
|
+
const result = await createInboundDNSRecordsForProvider2(
|
|
20236
|
+
credentialResult.credentials,
|
|
20237
|
+
receivingDomain,
|
|
20238
|
+
region,
|
|
20239
|
+
parentDomain
|
|
20240
|
+
);
|
|
20241
|
+
if (result.success && result.recordsCreated > 0) {
|
|
20242
|
+
progress.succeed(
|
|
20243
|
+
`Created ${result.recordsCreated} DNS records in ${getDNSProviderDisplayName2(dnsProvider)}`
|
|
20244
|
+
);
|
|
20245
|
+
dnsAutoCreated = true;
|
|
20246
|
+
} else {
|
|
20247
|
+
progress.fail("Failed to create some DNS records");
|
|
20248
|
+
if (result.errors) {
|
|
20249
|
+
for (const err of result.errors) {
|
|
20250
|
+
clack20.log.warn(err);
|
|
20251
|
+
}
|
|
20252
|
+
}
|
|
20253
|
+
}
|
|
20254
|
+
} else {
|
|
20255
|
+
clack20.log.warn(
|
|
20256
|
+
credentialResult.error || "Could not validate credentials"
|
|
20257
|
+
);
|
|
20258
|
+
if (dnsProvider === "vercel" || dnsProvider === "cloudflare") {
|
|
20259
|
+
clack20.log.info(
|
|
20260
|
+
`Set the ${dnsProvider === "vercel" ? "VERCEL_TOKEN" : "CLOUDFLARE_API_TOKEN"} environment variable to enable automatic DNS management.`
|
|
20261
|
+
);
|
|
20262
|
+
clack20.log.info(
|
|
20263
|
+
`You can create a token at: ${pc21.cyan(getDNSProviderTokenUrl2(dnsProvider))}`
|
|
20264
|
+
);
|
|
20265
|
+
}
|
|
20266
|
+
if (!options.yes) {
|
|
20267
|
+
const continueManual = await promptContinueManualDNS2();
|
|
20268
|
+
if (continueManual) {
|
|
20269
|
+
dnsProvider = "manual";
|
|
20270
|
+
}
|
|
20271
|
+
}
|
|
20272
|
+
}
|
|
20273
|
+
}
|
|
20274
|
+
if (!dnsAutoCreated) {
|
|
20275
|
+
const dnsRecords = buildRecords(receivingDomain, region);
|
|
20276
|
+
const formattedRecords = formatRecords(dnsRecords);
|
|
20277
|
+
console.log();
|
|
20278
|
+
clack20.log.info(pc21.bold("DNS Records Required"));
|
|
20279
|
+
console.log(
|
|
20280
|
+
` Add these records to your DNS provider for ${pc21.cyan(receivingDomain)}:
|
|
20281
|
+
`
|
|
20282
|
+
);
|
|
20283
|
+
for (const record of formattedRecords) {
|
|
20284
|
+
console.log(` ${pc21.dim(record.type.padEnd(6))} ${record.name}`);
|
|
20285
|
+
console.log(` ${pc21.dim("\u2192")} ${pc21.green(record.value)}
|
|
20286
|
+
`);
|
|
20287
|
+
}
|
|
20288
|
+
}
|
|
20289
|
+
await progress.execute("Saving configuration", async () => {
|
|
20290
|
+
addInboundDomainToMetadata(metadata, {
|
|
20291
|
+
subdomain,
|
|
20292
|
+
receivingDomain,
|
|
20293
|
+
parentDomain,
|
|
20294
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20295
|
+
});
|
|
20296
|
+
await saveConnectionMetadata(metadata);
|
|
20297
|
+
});
|
|
20298
|
+
if (isJsonMode()) {
|
|
20299
|
+
jsonSuccess("email.inbound.add", {
|
|
20300
|
+
receivingDomain,
|
|
20301
|
+
subdomain,
|
|
20302
|
+
parentDomain,
|
|
20303
|
+
dnsAutoCreated,
|
|
20304
|
+
region
|
|
20305
|
+
});
|
|
20306
|
+
return;
|
|
20307
|
+
}
|
|
20308
|
+
console.log();
|
|
20309
|
+
clack20.log.success(
|
|
20310
|
+
`${pc21.bold("Added inbound domain:")} ${pc21.cyan(receivingDomain)}`
|
|
20311
|
+
);
|
|
20312
|
+
console.log();
|
|
20313
|
+
if (dnsAutoCreated) {
|
|
20314
|
+
console.log(` Verify: ${pc21.cyan("wraps email inbound verify")}`);
|
|
20315
|
+
} else {
|
|
20316
|
+
console.log(` ${pc21.dim("1.")} Add DNS records above to your DNS provider`);
|
|
20317
|
+
console.log(
|
|
20318
|
+
` ${pc21.dim("2.")} Verify: ${pc21.cyan("wraps email inbound verify")}`
|
|
20319
|
+
);
|
|
20320
|
+
}
|
|
20321
|
+
console.log();
|
|
20322
|
+
}
|
|
20323
|
+
async function inboundRemove(options) {
|
|
20324
|
+
if (!isJsonMode()) {
|
|
20325
|
+
clack20.intro(pc21.bold("Remove Inbound Receiving Domain"));
|
|
20326
|
+
}
|
|
20327
|
+
const progress = new DeploymentProgress();
|
|
20328
|
+
const identity = await progress.execute(
|
|
20329
|
+
"Validating AWS credentials",
|
|
20330
|
+
async () => validateAWSCredentials()
|
|
20331
|
+
);
|
|
20332
|
+
const region = options.region || await getAWSRegion();
|
|
20333
|
+
const metadata = await loadConnectionMetadata(identity.accountId, region);
|
|
20334
|
+
if (!metadata?.services?.email?.config?.inbound?.enabled) {
|
|
20335
|
+
clack20.log.error("Inbound email infrastructure is not deployed.");
|
|
20336
|
+
console.log(`
|
|
20337
|
+
Deploy first: ${pc21.cyan("wraps email inbound init")}
|
|
20338
|
+
`);
|
|
20339
|
+
process.exit(1);
|
|
20340
|
+
}
|
|
20341
|
+
const emailConfig = metadata.services.email.config;
|
|
20342
|
+
const inboundDomains = emailConfig.inboundDomains ?? [];
|
|
20343
|
+
if (inboundDomains.length === 0) {
|
|
20344
|
+
clack20.log.warn("No inbound domains configured.");
|
|
20345
|
+
return;
|
|
20346
|
+
}
|
|
20347
|
+
let domainToRemove = options.domain;
|
|
20348
|
+
if (!domainToRemove) {
|
|
20349
|
+
if (inboundDomains.length === 1) {
|
|
20350
|
+
domainToRemove = inboundDomains[0].receivingDomain;
|
|
20351
|
+
} else {
|
|
20352
|
+
const selected = await clack20.select({
|
|
20353
|
+
message: "Which inbound domain do you want to remove?",
|
|
20354
|
+
options: inboundDomains.map((d) => ({
|
|
20355
|
+
value: d.receivingDomain,
|
|
20356
|
+
label: d.receivingDomain,
|
|
20357
|
+
hint: `added ${d.addedAt.split("T")[0]}`
|
|
20358
|
+
}))
|
|
20359
|
+
});
|
|
20360
|
+
if (clack20.isCancel(selected)) {
|
|
20361
|
+
clack20.cancel("Operation cancelled.");
|
|
20362
|
+
process.exit(0);
|
|
20363
|
+
}
|
|
20364
|
+
domainToRemove = selected;
|
|
20365
|
+
}
|
|
20366
|
+
}
|
|
20367
|
+
if (!inboundDomains.some((d) => d.receivingDomain === domainToRemove)) {
|
|
20368
|
+
clack20.log.error(
|
|
20369
|
+
`${pc21.cyan(domainToRemove)} is not in the inbound domains list.`
|
|
20370
|
+
);
|
|
20371
|
+
return;
|
|
20372
|
+
}
|
|
20373
|
+
if (inboundDomains.length === 1) {
|
|
20374
|
+
clack20.log.error(
|
|
20375
|
+
"Cannot remove the last inbound domain. Use " + pc21.cyan("wraps email inbound destroy") + " to remove all inbound infrastructure."
|
|
20376
|
+
);
|
|
20377
|
+
return;
|
|
20378
|
+
}
|
|
20379
|
+
if (!options.yes) {
|
|
20380
|
+
const confirmed = await clack20.confirm({
|
|
20381
|
+
message: `Remove inbound domain ${pc21.cyan(domainToRemove)}?`,
|
|
20382
|
+
initialValue: false
|
|
20383
|
+
});
|
|
20384
|
+
if (clack20.isCancel(confirmed) || !confirmed) {
|
|
20385
|
+
clack20.cancel("Operation cancelled.");
|
|
20386
|
+
process.exit(0);
|
|
20387
|
+
}
|
|
20388
|
+
}
|
|
20389
|
+
await progress.execute("Updating SES receipt rule", async () => {
|
|
20390
|
+
await removeDomainFromReceiptRule(region, domainToRemove);
|
|
20391
|
+
});
|
|
20392
|
+
await progress.execute("Saving configuration", async () => {
|
|
20393
|
+
removeInboundDomainFromMetadata(metadata, domainToRemove);
|
|
20394
|
+
await saveConnectionMetadata(metadata);
|
|
20395
|
+
});
|
|
20396
|
+
if (isJsonMode()) {
|
|
20397
|
+
jsonSuccess("email.inbound.remove", {
|
|
20398
|
+
removedDomain: domainToRemove,
|
|
20399
|
+
remainingDomains: (emailConfig.inboundDomains ?? []).map(
|
|
20400
|
+
(d) => d.receivingDomain
|
|
20401
|
+
),
|
|
20402
|
+
region
|
|
20403
|
+
});
|
|
20404
|
+
return;
|
|
20405
|
+
}
|
|
20406
|
+
console.log();
|
|
20407
|
+
clack20.log.success(
|
|
20408
|
+
`${pc21.bold("Removed inbound domain:")} ${pc21.cyan(domainToRemove)}`
|
|
20409
|
+
);
|
|
20410
|
+
console.log();
|
|
20411
|
+
console.log(
|
|
20412
|
+
` ${pc21.dim("Remember to remove the MX and SPF DNS records for")} ${pc21.cyan(domainToRemove)}`
|
|
20413
|
+
);
|
|
20414
|
+
console.log();
|
|
20415
|
+
}
|
|
19894
20416
|
|
|
19895
20417
|
// src/commands/email/init.ts
|
|
19896
20418
|
init_esm_shims();
|
|
@@ -20459,6 +20981,7 @@ ${pc24.yellow(pc24.bold("Configuration Warnings:"))}`);
|
|
|
20459
20981
|
domain: outputs.domain,
|
|
20460
20982
|
dkimTokens: outputs.dkimTokens,
|
|
20461
20983
|
mailFromDomain: outputs.mailFromDomain,
|
|
20984
|
+
customTrackingDomain: outputs.customTrackingDomain,
|
|
20462
20985
|
region
|
|
20463
20986
|
},
|
|
20464
20987
|
selectedCategories
|
|
@@ -20491,6 +21014,7 @@ ${pc24.yellow(pc24.bold("Configuration Warnings:"))}`);
|
|
|
20491
21014
|
domain: outputs.domain,
|
|
20492
21015
|
dkimTokens: outputs.dkimTokens,
|
|
20493
21016
|
mailFromDomain: outputs.mailFromDomain,
|
|
21017
|
+
customTrackingDomain: outputs.customTrackingDomain,
|
|
20494
21018
|
region
|
|
20495
21019
|
};
|
|
20496
21020
|
const records = buildEmailDNSRecords2(recordData);
|
|
@@ -24367,6 +24891,7 @@ ${pc30.bold("Cost Impact:")}`);
|
|
|
24367
24891
|
domain: outputs.domain,
|
|
24368
24892
|
dkimTokens: outputs.dkimTokens,
|
|
24369
24893
|
mailFromDomain,
|
|
24894
|
+
customTrackingDomain: outputs.customTrackingDomain,
|
|
24370
24895
|
region
|
|
24371
24896
|
};
|
|
24372
24897
|
try {
|
|
@@ -24417,6 +24942,7 @@ ${pc30.bold("Cost Impact:")}`);
|
|
|
24417
24942
|
domain: outputs.domain,
|
|
24418
24943
|
dkimTokens: outputs.dkimTokens,
|
|
24419
24944
|
mailFromDomain,
|
|
24945
|
+
customTrackingDomain: outputs.customTrackingDomain,
|
|
24420
24946
|
region
|
|
24421
24947
|
};
|
|
24422
24948
|
const dnsRecords = buildEmailDNSRecords(dnsData);
|
|
@@ -24461,6 +24987,13 @@ ${pc30.bold("Add these DNS records to your DNS provider:")}
|
|
|
24461
24987
|
if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {
|
|
24462
24988
|
acmValidationRecords.push(...outputs.acmCertificateValidationRecords);
|
|
24463
24989
|
}
|
|
24990
|
+
if (acmValidationRecords.length === 0 && outputs.httpsTrackingPending && outputs.customTrackingDomain) {
|
|
24991
|
+
const { getCertificateValidationRecords: getCertificateValidationRecords2 } = await Promise.resolve().then(() => (init_acm(), acm_exports));
|
|
24992
|
+
const directRecords = await getCertificateValidationRecords2(
|
|
24993
|
+
outputs.customTrackingDomain
|
|
24994
|
+
);
|
|
24995
|
+
acmValidationRecords.push(...directRecords);
|
|
24996
|
+
}
|
|
24464
24997
|
let acmDnsAutoCreated = false;
|
|
24465
24998
|
if (outputs.httpsTrackingPending && acmValidationRecords.length > 0 && outputs.customTrackingDomain) {
|
|
24466
24999
|
const trackingDnsProvider = metadata.services.email?.dnsProvider;
|
|
@@ -27140,7 +27673,7 @@ import {
|
|
|
27140
27673
|
IAMClient as IAMClient2,
|
|
27141
27674
|
PutRolePolicyCommand
|
|
27142
27675
|
} from "@aws-sdk/client-iam";
|
|
27143
|
-
import { confirm as confirm14, intro as intro32, isCancel as isCancel20, log as log32, outro as outro19, select as
|
|
27676
|
+
import { confirm as confirm14, intro as intro32, isCancel as isCancel20, log as log32, outro as outro19, select as select15 } from "@clack/prompts";
|
|
27144
27677
|
import * as pulumi21 from "@pulumi/pulumi";
|
|
27145
27678
|
import pc36 from "picocolors";
|
|
27146
27679
|
init_events();
|
|
@@ -27483,7 +28016,7 @@ async function resolveOrganization() {
|
|
|
27483
28016
|
if (orgs.length === 1) {
|
|
27484
28017
|
return orgs[0];
|
|
27485
28018
|
}
|
|
27486
|
-
const selected = await
|
|
28019
|
+
const selected = await select15({
|
|
27487
28020
|
message: "Which organization should this AWS account connect to?",
|
|
27488
28021
|
options: orgs.map((org) => ({
|
|
27489
28022
|
value: org.id,
|
|
@@ -27779,7 +28312,7 @@ Run ${pc36.cyan("wraps email init")} or ${pc36.cyan("wraps sms init")} first.
|
|
|
27779
28312
|
log32.info(
|
|
27780
28313
|
`Already connected to Wraps Platform (AWS Account: ${pc36.cyan(metadata.accountId)})`
|
|
27781
28314
|
);
|
|
27782
|
-
const action = await
|
|
28315
|
+
const action = await select15({
|
|
27783
28316
|
message: "What would you like to do?",
|
|
27784
28317
|
options: [
|
|
27785
28318
|
{
|
|
@@ -36139,6 +36672,11 @@ args.options([
|
|
|
36139
36672
|
name: "org",
|
|
36140
36673
|
description: "Organization slug",
|
|
36141
36674
|
defaultValue: void 0
|
|
36675
|
+
},
|
|
36676
|
+
{
|
|
36677
|
+
name: "subdomain",
|
|
36678
|
+
description: "Subdomain for inbound email (e.g., inbound, support)",
|
|
36679
|
+
defaultValue: void 0
|
|
36142
36680
|
}
|
|
36143
36681
|
]);
|
|
36144
36682
|
var flags = args.parse(process.argv);
|
|
@@ -36453,13 +36991,30 @@ Usage: ${pc53.cyan("wraps email verify --domain yourapp.com")}
|
|
|
36453
36991
|
json: flags.json
|
|
36454
36992
|
});
|
|
36455
36993
|
break;
|
|
36994
|
+
case "add":
|
|
36995
|
+
await inboundAdd({
|
|
36996
|
+
region: flags.region,
|
|
36997
|
+
subdomain: flags.subdomain,
|
|
36998
|
+
domain: flags.domain,
|
|
36999
|
+
yes: flags.yes,
|
|
37000
|
+
json: flags.json
|
|
37001
|
+
});
|
|
37002
|
+
break;
|
|
37003
|
+
case "remove":
|
|
37004
|
+
await inboundRemove({
|
|
37005
|
+
region: flags.region,
|
|
37006
|
+
domain: flags.domain,
|
|
37007
|
+
yes: flags.yes,
|
|
37008
|
+
json: flags.json
|
|
37009
|
+
});
|
|
37010
|
+
break;
|
|
36456
37011
|
default:
|
|
36457
37012
|
clack50.log.error(
|
|
36458
37013
|
`Unknown inbound command: ${inboundSubCommand || "(none)"}`
|
|
36459
37014
|
);
|
|
36460
37015
|
console.log(
|
|
36461
37016
|
`
|
|
36462
|
-
Available commands: ${pc53.cyan("init")}, ${pc53.cyan("destroy")}, ${pc53.cyan("status")}, ${pc53.cyan("verify")}, ${pc53.cyan("test")}
|
|
37017
|
+
Available commands: ${pc53.cyan("init")}, ${pc53.cyan("destroy")}, ${pc53.cyan("status")}, ${pc53.cyan("verify")}, ${pc53.cyan("test")}, ${pc53.cyan("add")}, ${pc53.cyan("remove")}
|
|
36463
37018
|
`
|
|
36464
37019
|
);
|
|
36465
37020
|
throw new Error(
|