@wraps.dev/cli 2.14.7 → 2.15.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/dist/cli.js +607 -78
- 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 +5 -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";
|
|
@@ -5560,19 +5621,19 @@ import { randomBytes as randomBytes2 } from "crypto";
|
|
|
5560
5621
|
import { existsSync as existsSync6, mkdirSync } from "fs";
|
|
5561
5622
|
import { builtinModules } from "module";
|
|
5562
5623
|
import { tmpdir } from "os";
|
|
5563
|
-
import { dirname as
|
|
5624
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
5564
5625
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5565
5626
|
import * as aws8 from "@pulumi/aws";
|
|
5566
5627
|
import * as pulumi11 from "@pulumi/pulumi";
|
|
5567
5628
|
import { build } from "esbuild";
|
|
5568
5629
|
function getPackageRoot() {
|
|
5569
5630
|
const currentFile = fileURLToPath3(import.meta.url);
|
|
5570
|
-
let dir =
|
|
5571
|
-
while (dir !==
|
|
5631
|
+
let dir = dirname3(currentFile);
|
|
5632
|
+
while (dir !== dirname3(dir)) {
|
|
5572
5633
|
if (existsSync6(join7(dir, "package.json"))) {
|
|
5573
5634
|
return dir;
|
|
5574
5635
|
}
|
|
5575
|
-
dir =
|
|
5636
|
+
dir = dirname3(dir);
|
|
5576
5637
|
}
|
|
5577
5638
|
throw new Error("Could not find package.json");
|
|
5578
5639
|
}
|
|
@@ -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";
|
|
@@ -8703,7 +8793,7 @@ var init_dynamodb_metrics = __esm({
|
|
|
8703
8793
|
// src/cli.ts
|
|
8704
8794
|
init_esm_shims();
|
|
8705
8795
|
import { readFileSync as readFileSync3 } from "fs";
|
|
8706
|
-
import { dirname as
|
|
8796
|
+
import { dirname as dirname4, join as join19 } from "path";
|
|
8707
8797
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
8708
8798
|
import * as clack50 from "@clack/prompts";
|
|
8709
8799
|
import args from "args";
|
|
@@ -9902,6 +9992,7 @@ import pc10 from "picocolors";
|
|
|
9902
9992
|
init_esm_shims();
|
|
9903
9993
|
init_errors();
|
|
9904
9994
|
import { exec } from "child_process";
|
|
9995
|
+
import { dirname as dirname2 } from "path";
|
|
9905
9996
|
import { promisify } from "util";
|
|
9906
9997
|
import { PulumiCommand } from "@pulumi/pulumi/automation/index.js";
|
|
9907
9998
|
var execAsync = promisify(exec);
|
|
@@ -9917,7 +10008,9 @@ async function ensurePulumiInstalled() {
|
|
|
9917
10008
|
const isInstalled = await checkPulumiInstalled();
|
|
9918
10009
|
if (!isInstalled) {
|
|
9919
10010
|
try {
|
|
9920
|
-
await PulumiCommand.install();
|
|
10011
|
+
const cmd = await PulumiCommand.install();
|
|
10012
|
+
const binDir = dirname2(cmd.command);
|
|
10013
|
+
process.env.PATH = `${binDir}:${process.env.PATH}`;
|
|
9921
10014
|
return true;
|
|
9922
10015
|
} catch (_error) {
|
|
9923
10016
|
throw errors.pulumiNotInstalled();
|
|
@@ -19095,7 +19188,8 @@ import {
|
|
|
19095
19188
|
DescribeActiveReceiptRuleSetCommand,
|
|
19096
19189
|
DescribeReceiptRuleCommand,
|
|
19097
19190
|
SESClient as SESClient3,
|
|
19098
|
-
SetActiveReceiptRuleSetCommand
|
|
19191
|
+
SetActiveReceiptRuleSetCommand,
|
|
19192
|
+
UpdateReceiptRuleCommand
|
|
19099
19193
|
} from "@aws-sdk/client-ses";
|
|
19100
19194
|
var RULE_SET_NAME = "wraps-inbound-rules";
|
|
19101
19195
|
var RULE_NAME = "wraps-inbound-catch-all";
|
|
@@ -19218,6 +19312,77 @@ async function deleteReceiptRuleSet(region) {
|
|
|
19218
19312
|
throw error;
|
|
19219
19313
|
}
|
|
19220
19314
|
}
|
|
19315
|
+
async function addDomainToReceiptRule(region, domain, s3BucketName) {
|
|
19316
|
+
const ses = createSESClient(region);
|
|
19317
|
+
try {
|
|
19318
|
+
const response = await ses.send(
|
|
19319
|
+
new DescribeReceiptRuleCommand({
|
|
19320
|
+
RuleSetName: RULE_SET_NAME,
|
|
19321
|
+
RuleName: RULE_NAME
|
|
19322
|
+
})
|
|
19323
|
+
);
|
|
19324
|
+
const existingRecipients = response.Rule?.Recipients ?? [];
|
|
19325
|
+
if (existingRecipients.includes(domain)) {
|
|
19326
|
+
return;
|
|
19327
|
+
}
|
|
19328
|
+
await ses.send(
|
|
19329
|
+
new UpdateReceiptRuleCommand({
|
|
19330
|
+
RuleSetName: RULE_SET_NAME,
|
|
19331
|
+
Rule: {
|
|
19332
|
+
Name: RULE_NAME,
|
|
19333
|
+
Enabled: response.Rule?.Enabled,
|
|
19334
|
+
ScanEnabled: response.Rule?.ScanEnabled,
|
|
19335
|
+
TlsPolicy: response.Rule?.TlsPolicy,
|
|
19336
|
+
Actions: response.Rule?.Actions,
|
|
19337
|
+
Recipients: [...existingRecipients, domain]
|
|
19338
|
+
}
|
|
19339
|
+
})
|
|
19340
|
+
);
|
|
19341
|
+
} catch (error) {
|
|
19342
|
+
if (error instanceof Error && (error.name === "RuleDoesNotExistException" || error.name === "RuleSetDoesNotExistException")) {
|
|
19343
|
+
await createReceiptRuleSet(region);
|
|
19344
|
+
await createReceiptRule(region, domain, s3BucketName);
|
|
19345
|
+
await setActiveReceiptRuleSet(region, RULE_SET_NAME);
|
|
19346
|
+
return;
|
|
19347
|
+
}
|
|
19348
|
+
throw error;
|
|
19349
|
+
}
|
|
19350
|
+
}
|
|
19351
|
+
async function removeDomainFromReceiptRule(region, domain) {
|
|
19352
|
+
const ses = createSESClient(region);
|
|
19353
|
+
try {
|
|
19354
|
+
const response = await ses.send(
|
|
19355
|
+
new DescribeReceiptRuleCommand({
|
|
19356
|
+
RuleSetName: RULE_SET_NAME,
|
|
19357
|
+
RuleName: RULE_NAME
|
|
19358
|
+
})
|
|
19359
|
+
);
|
|
19360
|
+
const existingRecipients = response.Rule?.Recipients ?? [];
|
|
19361
|
+
const updated = existingRecipients.filter((r) => r !== domain);
|
|
19362
|
+
if (updated.length === 0) {
|
|
19363
|
+
await deleteReceiptRule(region);
|
|
19364
|
+
return;
|
|
19365
|
+
}
|
|
19366
|
+
await ses.send(
|
|
19367
|
+
new UpdateReceiptRuleCommand({
|
|
19368
|
+
RuleSetName: RULE_SET_NAME,
|
|
19369
|
+
Rule: {
|
|
19370
|
+
Name: RULE_NAME,
|
|
19371
|
+
Enabled: response.Rule?.Enabled,
|
|
19372
|
+
ScanEnabled: response.Rule?.ScanEnabled,
|
|
19373
|
+
TlsPolicy: response.Rule?.TlsPolicy,
|
|
19374
|
+
Actions: response.Rule?.Actions,
|
|
19375
|
+
Recipients: updated
|
|
19376
|
+
}
|
|
19377
|
+
})
|
|
19378
|
+
);
|
|
19379
|
+
} catch (error) {
|
|
19380
|
+
if (error instanceof Error && (error.name === "RuleDoesNotExistException" || error.name === "RuleSetDoesNotExistException")) {
|
|
19381
|
+
return;
|
|
19382
|
+
}
|
|
19383
|
+
throw error;
|
|
19384
|
+
}
|
|
19385
|
+
}
|
|
19221
19386
|
|
|
19222
19387
|
// src/commands/email/inbound.ts
|
|
19223
19388
|
init_aws();
|
|
@@ -19296,7 +19461,15 @@ async function inboundInit(options) {
|
|
|
19296
19461
|
bucketName: `wraps-inbound-${identity.accountId}-${region}`,
|
|
19297
19462
|
webhookUrl,
|
|
19298
19463
|
webhookSecret
|
|
19299
|
-
}
|
|
19464
|
+
},
|
|
19465
|
+
inboundDomains: [
|
|
19466
|
+
{
|
|
19467
|
+
subdomain,
|
|
19468
|
+
receivingDomain,
|
|
19469
|
+
parentDomain: domain,
|
|
19470
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19471
|
+
}
|
|
19472
|
+
]
|
|
19300
19473
|
};
|
|
19301
19474
|
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
19302
19475
|
emailConfig: updatedEmailConfig
|
|
@@ -19537,7 +19710,8 @@ Deploy first: ${pc21.cyan("wraps email inbound init")}
|
|
|
19537
19710
|
const stackName = emailService.pulumiStackName || `wraps-${identity.accountId}-${region}`;
|
|
19538
19711
|
const updatedEmailConfig = {
|
|
19539
19712
|
...emailService.config,
|
|
19540
|
-
inbound: void 0
|
|
19713
|
+
inbound: void 0,
|
|
19714
|
+
inboundDomains: void 0
|
|
19541
19715
|
};
|
|
19542
19716
|
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
19543
19717
|
emailConfig: updatedEmailConfig
|
|
@@ -19606,13 +19780,18 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19606
19780
|
`);
|
|
19607
19781
|
return;
|
|
19608
19782
|
}
|
|
19609
|
-
const
|
|
19783
|
+
const emailConfig = metadata.services.email.config;
|
|
19784
|
+
const inbound = emailConfig.inbound;
|
|
19785
|
+
const inboundDomains = emailConfig.inboundDomains ?? [];
|
|
19610
19786
|
const activeRuleSet = await getActiveReceiptRuleSet(region);
|
|
19611
|
-
const
|
|
19787
|
+
const domainList = inboundDomains.length > 0 ? inboundDomains.map((d) => d.receivingDomain) : [
|
|
19788
|
+
inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`
|
|
19789
|
+
];
|
|
19612
19790
|
if (isJsonMode()) {
|
|
19613
19791
|
jsonSuccess("email.inbound.status", {
|
|
19614
19792
|
enabled: true,
|
|
19615
|
-
|
|
19793
|
+
receivingDomains: domainList,
|
|
19794
|
+
receivingDomain: domainList[0],
|
|
19616
19795
|
bucketName: inbound.bucketName || "",
|
|
19617
19796
|
region,
|
|
19618
19797
|
webhookUrl: inbound.webhookUrl || null,
|
|
@@ -19624,7 +19803,16 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19624
19803
|
console.log();
|
|
19625
19804
|
console.log(pc21.bold(" Inbound Email Configuration"));
|
|
19626
19805
|
console.log();
|
|
19627
|
-
|
|
19806
|
+
if (domainList.length === 1) {
|
|
19807
|
+
console.log(
|
|
19808
|
+
` ${pc21.dim("Receiving domain:")} ${pc21.cyan(domainList[0])}`
|
|
19809
|
+
);
|
|
19810
|
+
} else {
|
|
19811
|
+
console.log(` ${pc21.dim("Receiving domains:")}`);
|
|
19812
|
+
for (const d of domainList) {
|
|
19813
|
+
console.log(` ${pc21.cyan(d)}`);
|
|
19814
|
+
}
|
|
19815
|
+
}
|
|
19628
19816
|
console.log(
|
|
19629
19817
|
` ${pc21.dim("S3 bucket:")} ${pc21.cyan(inbound.bucketName || "")}`
|
|
19630
19818
|
);
|
|
@@ -19658,61 +19846,77 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19658
19846
|
`);
|
|
19659
19847
|
process.exit(1);
|
|
19660
19848
|
}
|
|
19661
|
-
const
|
|
19662
|
-
const
|
|
19849
|
+
const emailConfig = metadata.services.email.config;
|
|
19850
|
+
const inbound = emailConfig.inbound;
|
|
19851
|
+
const inboundDomains = emailConfig.inboundDomains ?? [];
|
|
19852
|
+
const domainList = inboundDomains.length > 0 ? inboundDomains.map((d) => d.receivingDomain) : [
|
|
19853
|
+
inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`
|
|
19854
|
+
];
|
|
19663
19855
|
let allPassed = true;
|
|
19856
|
+
const domainChecks = {};
|
|
19664
19857
|
console.log();
|
|
19665
|
-
const
|
|
19666
|
-
|
|
19667
|
-
|
|
19668
|
-
try {
|
|
19669
|
-
const records = await dns2.resolveMx(receivingDomain);
|
|
19670
|
-
const hasSES = records.some((r) => r.exchange.includes("inbound-smtp"));
|
|
19671
|
-
return { found: true, hasSES, records };
|
|
19672
|
-
} catch {
|
|
19673
|
-
return { found: false, hasSES: false, records: [] };
|
|
19674
|
-
}
|
|
19858
|
+
for (const receivingDomain of domainList) {
|
|
19859
|
+
if (domainList.length > 1) {
|
|
19860
|
+
clack20.log.info(pc21.bold(`Checking ${pc21.cyan(receivingDomain)}`));
|
|
19675
19861
|
}
|
|
19676
|
-
|
|
19677
|
-
|
|
19678
|
-
|
|
19679
|
-
|
|
19680
|
-
|
|
19681
|
-
|
|
19682
|
-
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19688
|
-
`MX record: ${pc21.red("not found")}. Add: ${pc21.cyan(`${receivingDomain} MX 10 inbound-smtp.${region}.amazonaws.com`)}`
|
|
19689
|
-
);
|
|
19690
|
-
allPassed = false;
|
|
19691
|
-
}
|
|
19692
|
-
const spfResult = await progress.execute(
|
|
19693
|
-
`Checking SPF record for ${receivingDomain}`,
|
|
19694
|
-
async () => {
|
|
19695
|
-
try {
|
|
19696
|
-
const records = await dns2.resolveTxt(receivingDomain);
|
|
19697
|
-
const flat = records.map((r) => r.join(""));
|
|
19698
|
-
const spf = flat.find((r) => r.startsWith("v=spf1"));
|
|
19699
|
-
const hasSES = spf?.includes("amazonses.com") ?? false;
|
|
19700
|
-
return { found: !!spf, hasSES, value: spf };
|
|
19701
|
-
} catch {
|
|
19702
|
-
return { found: false, hasSES: false, value: null };
|
|
19862
|
+
const mxResult = await progress.execute(
|
|
19863
|
+
`Checking MX record for ${receivingDomain}`,
|
|
19864
|
+
async () => {
|
|
19865
|
+
try {
|
|
19866
|
+
const records = await dns2.resolveMx(receivingDomain);
|
|
19867
|
+
const hasSES = records.some(
|
|
19868
|
+
(r) => r.exchange.includes("inbound-smtp")
|
|
19869
|
+
);
|
|
19870
|
+
return { found: true, hasSES, records };
|
|
19871
|
+
} catch {
|
|
19872
|
+
return { found: false, hasSES: false, records: [] };
|
|
19873
|
+
}
|
|
19703
19874
|
}
|
|
19875
|
+
);
|
|
19876
|
+
if (mxResult.hasSES) {
|
|
19877
|
+
clack20.log.success(
|
|
19878
|
+
`MX record: ${pc21.green("verified")} \u2192 inbound-smtp.${region}.amazonaws.com`
|
|
19879
|
+
);
|
|
19880
|
+
} else if (mxResult.found) {
|
|
19881
|
+
clack20.log.warn(
|
|
19882
|
+
`MX record found but not pointing to SES. Expected: ${pc21.cyan(`10 inbound-smtp.${region}.amazonaws.com`)}`
|
|
19883
|
+
);
|
|
19884
|
+
allPassed = false;
|
|
19885
|
+
} else {
|
|
19886
|
+
clack20.log.error(
|
|
19887
|
+
`MX record: ${pc21.red("not found")}. Add: ${pc21.cyan(`${receivingDomain} MX 10 inbound-smtp.${region}.amazonaws.com`)}`
|
|
19888
|
+
);
|
|
19889
|
+
allPassed = false;
|
|
19704
19890
|
}
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
|
|
19711
|
-
|
|
19712
|
-
|
|
19713
|
-
|
|
19891
|
+
const spfResult = await progress.execute(
|
|
19892
|
+
`Checking SPF record for ${receivingDomain}`,
|
|
19893
|
+
async () => {
|
|
19894
|
+
try {
|
|
19895
|
+
const records = await dns2.resolveTxt(receivingDomain);
|
|
19896
|
+
const flat = records.map((r) => r.join(""));
|
|
19897
|
+
const spf = flat.find((r) => r.startsWith("v=spf1"));
|
|
19898
|
+
const hasSES = spf?.includes("amazonses.com") ?? false;
|
|
19899
|
+
return { found: !!spf, hasSES, value: spf };
|
|
19900
|
+
} catch {
|
|
19901
|
+
return { found: false, hasSES: false, value: null };
|
|
19902
|
+
}
|
|
19903
|
+
}
|
|
19714
19904
|
);
|
|
19715
|
-
|
|
19905
|
+
if (spfResult.hasSES) {
|
|
19906
|
+
clack20.log.success(`SPF record: ${pc21.green("verified")}`);
|
|
19907
|
+
} else if (spfResult.found) {
|
|
19908
|
+
clack20.log.warn("SPF record exists but missing amazonses.com include");
|
|
19909
|
+
allPassed = false;
|
|
19910
|
+
} else {
|
|
19911
|
+
clack20.log.error(
|
|
19912
|
+
`SPF record: ${pc21.red("not found")}. Add TXT: ${pc21.cyan("v=spf1 include:amazonses.com ~all")}`
|
|
19913
|
+
);
|
|
19914
|
+
allPassed = false;
|
|
19915
|
+
}
|
|
19916
|
+
domainChecks[receivingDomain] = {
|
|
19917
|
+
mx: { found: mxResult.found, verified: mxResult.hasSES },
|
|
19918
|
+
spf: { found: spfResult.found, verified: spfResult.hasSES }
|
|
19919
|
+
};
|
|
19716
19920
|
}
|
|
19717
19921
|
const activeRuleSet = await getActiveReceiptRuleSet(region);
|
|
19718
19922
|
if (activeRuleSet === RULE_SET_NAME) {
|
|
@@ -19725,13 +19929,11 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19725
19929
|
}
|
|
19726
19930
|
if (isJsonMode()) {
|
|
19727
19931
|
jsonSuccess("email.inbound.verify", {
|
|
19728
|
-
|
|
19932
|
+
receivingDomains: domainList,
|
|
19933
|
+
receivingDomain: domainList[0],
|
|
19729
19934
|
allPassed,
|
|
19730
|
-
|
|
19731
|
-
|
|
19732
|
-
spf: { found: spfResult.found, verified: spfResult.hasSES },
|
|
19733
|
-
receiptRuleSet: { active: activeRuleSet === RULE_SET_NAME }
|
|
19734
|
-
}
|
|
19935
|
+
domainChecks,
|
|
19936
|
+
receiptRuleSet: { active: activeRuleSet === RULE_SET_NAME }
|
|
19735
19937
|
});
|
|
19736
19938
|
return;
|
|
19737
19939
|
}
|
|
@@ -19888,6 +20090,304 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
|
|
|
19888
20090
|
clack20.log.success(pc21.bold("Inbound email is working correctly!"));
|
|
19889
20091
|
console.log();
|
|
19890
20092
|
}
|
|
20093
|
+
async function inboundAdd(options) {
|
|
20094
|
+
if (!isJsonMode()) {
|
|
20095
|
+
clack20.intro(pc21.bold("Add Inbound Receiving Domain"));
|
|
20096
|
+
}
|
|
20097
|
+
const progress = new DeploymentProgress();
|
|
20098
|
+
const identity = await progress.execute(
|
|
20099
|
+
"Validating AWS credentials",
|
|
20100
|
+
async () => validateAWSCredentials()
|
|
20101
|
+
);
|
|
20102
|
+
const region = options.region || await getAWSRegion();
|
|
20103
|
+
if (!SES_RECEIVING_REGIONS.includes(
|
|
20104
|
+
region
|
|
20105
|
+
)) {
|
|
20106
|
+
throw errors.inboundRegionNotSupported(region);
|
|
20107
|
+
}
|
|
20108
|
+
const metadata = await loadConnectionMetadata(identity.accountId, region);
|
|
20109
|
+
if (!metadata?.services?.email?.config?.inbound?.enabled) {
|
|
20110
|
+
clack20.log.error("Inbound email infrastructure is not deployed.");
|
|
20111
|
+
console.log(
|
|
20112
|
+
`
|
|
20113
|
+
Deploy first: ${pc21.cyan("wraps email inbound init")}
|
|
20114
|
+
`
|
|
20115
|
+
);
|
|
20116
|
+
process.exit(1);
|
|
20117
|
+
}
|
|
20118
|
+
const emailConfig = metadata.services.email.config;
|
|
20119
|
+
const primaryDomain = emailConfig.domain || "";
|
|
20120
|
+
const allDomains = [primaryDomain];
|
|
20121
|
+
for (const d of emailConfig.additionalDomains ?? []) {
|
|
20122
|
+
if (!allDomains.includes(d.domain)) {
|
|
20123
|
+
allDomains.push(d.domain);
|
|
20124
|
+
}
|
|
20125
|
+
}
|
|
20126
|
+
let parentDomain = options.domain;
|
|
20127
|
+
if (!parentDomain) {
|
|
20128
|
+
if (options.yes) {
|
|
20129
|
+
parentDomain = primaryDomain;
|
|
20130
|
+
} else if (allDomains.length === 1) {
|
|
20131
|
+
parentDomain = allDomains[0];
|
|
20132
|
+
} else {
|
|
20133
|
+
const selected = await clack20.select({
|
|
20134
|
+
message: "Which domain should the inbound subdomain be under?",
|
|
20135
|
+
options: allDomains.map((d) => ({
|
|
20136
|
+
value: d,
|
|
20137
|
+
label: d,
|
|
20138
|
+
hint: d === primaryDomain ? "primary" : void 0
|
|
20139
|
+
}))
|
|
20140
|
+
});
|
|
20141
|
+
if (clack20.isCancel(selected)) {
|
|
20142
|
+
clack20.cancel("Operation cancelled.");
|
|
20143
|
+
process.exit(0);
|
|
20144
|
+
}
|
|
20145
|
+
parentDomain = selected;
|
|
20146
|
+
}
|
|
20147
|
+
}
|
|
20148
|
+
const subdomain = options.subdomain || (options.yes ? "inbound" : await promptInboundSubdomain(parentDomain));
|
|
20149
|
+
const receivingDomain = `${subdomain}.${parentDomain}`;
|
|
20150
|
+
const existingDomains = emailConfig.inboundDomains ?? [];
|
|
20151
|
+
if (existingDomains.some((d) => d.receivingDomain === receivingDomain)) {
|
|
20152
|
+
clack20.log.warn(
|
|
20153
|
+
`${pc21.cyan(receivingDomain)} is already configured as an inbound domain.`
|
|
20154
|
+
);
|
|
20155
|
+
return;
|
|
20156
|
+
}
|
|
20157
|
+
clack20.log.info(`Adding receiving domain: ${pc21.cyan(receivingDomain)}`);
|
|
20158
|
+
const bucketName = emailConfig.inbound?.bucketName || `wraps-inbound-${identity.accountId}-${region}`;
|
|
20159
|
+
await progress.execute("Updating SES receipt rule", async () => {
|
|
20160
|
+
await addDomainToReceiptRule(region, receivingDomain, bucketName);
|
|
20161
|
+
});
|
|
20162
|
+
let dnsAutoCreated = false;
|
|
20163
|
+
const {
|
|
20164
|
+
detectAvailableDNSProviders: detectAvailableDNSProviders2,
|
|
20165
|
+
getDNSCredentials: getDNSCredentials2,
|
|
20166
|
+
createInboundDNSRecordsForProvider: createInboundDNSRecordsForProvider2,
|
|
20167
|
+
getDNSProviderDisplayName: getDNSProviderDisplayName2,
|
|
20168
|
+
getDNSProviderTokenUrl: getDNSProviderTokenUrl2,
|
|
20169
|
+
buildInboundDNSRecords: buildRecords,
|
|
20170
|
+
formatDNSRecordsForDisplay: formatRecords
|
|
20171
|
+
} = await Promise.resolve().then(() => (init_dns(), dns_exports));
|
|
20172
|
+
const { promptDNSProvider: promptDNSProvider2, promptContinueManualDNS: promptContinueManualDNS2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
20173
|
+
const existingDnsProvider = metadata.services.email.dnsProvider;
|
|
20174
|
+
let dnsProvider = existingDnsProvider;
|
|
20175
|
+
if (!dnsProvider || dnsProvider === "manual") {
|
|
20176
|
+
progress.start("Detecting DNS providers");
|
|
20177
|
+
const availableProviders = await detectAvailableDNSProviders2(
|
|
20178
|
+
parentDomain,
|
|
20179
|
+
region
|
|
20180
|
+
);
|
|
20181
|
+
progress.stop();
|
|
20182
|
+
dnsProvider = options.yes ? "manual" : await promptDNSProvider2(parentDomain, availableProviders);
|
|
20183
|
+
}
|
|
20184
|
+
if (dnsProvider !== "manual") {
|
|
20185
|
+
progress.start(
|
|
20186
|
+
`Validating ${getDNSProviderDisplayName2(dnsProvider)} credentials`
|
|
20187
|
+
);
|
|
20188
|
+
const credentialResult = await getDNSCredentials2(
|
|
20189
|
+
dnsProvider,
|
|
20190
|
+
parentDomain,
|
|
20191
|
+
region
|
|
20192
|
+
);
|
|
20193
|
+
progress.stop();
|
|
20194
|
+
if (credentialResult.valid && credentialResult.credentials) {
|
|
20195
|
+
const records = buildRecords(receivingDomain, region);
|
|
20196
|
+
clack20.log.info(pc21.bold("DNS records to create:"));
|
|
20197
|
+
for (const record of records) {
|
|
20198
|
+
const value = record.priority ? `${record.priority} ${record.value}` : record.value;
|
|
20199
|
+
clack20.log.info(pc21.dim(` ${record.type} ${record.name} \u2192 ${value}`));
|
|
20200
|
+
}
|
|
20201
|
+
progress.start(
|
|
20202
|
+
`Creating DNS records in ${getDNSProviderDisplayName2(dnsProvider)}`
|
|
20203
|
+
);
|
|
20204
|
+
const result = await createInboundDNSRecordsForProvider2(
|
|
20205
|
+
credentialResult.credentials,
|
|
20206
|
+
receivingDomain,
|
|
20207
|
+
region,
|
|
20208
|
+
parentDomain
|
|
20209
|
+
);
|
|
20210
|
+
if (result.success && result.recordsCreated > 0) {
|
|
20211
|
+
progress.succeed(
|
|
20212
|
+
`Created ${result.recordsCreated} DNS records in ${getDNSProviderDisplayName2(dnsProvider)}`
|
|
20213
|
+
);
|
|
20214
|
+
dnsAutoCreated = true;
|
|
20215
|
+
} else {
|
|
20216
|
+
progress.fail("Failed to create some DNS records");
|
|
20217
|
+
if (result.errors) {
|
|
20218
|
+
for (const err of result.errors) {
|
|
20219
|
+
clack20.log.warn(err);
|
|
20220
|
+
}
|
|
20221
|
+
}
|
|
20222
|
+
}
|
|
20223
|
+
} else {
|
|
20224
|
+
clack20.log.warn(
|
|
20225
|
+
credentialResult.error || "Could not validate credentials"
|
|
20226
|
+
);
|
|
20227
|
+
if (dnsProvider === "vercel" || dnsProvider === "cloudflare") {
|
|
20228
|
+
clack20.log.info(
|
|
20229
|
+
`Set the ${dnsProvider === "vercel" ? "VERCEL_TOKEN" : "CLOUDFLARE_API_TOKEN"} environment variable to enable automatic DNS management.`
|
|
20230
|
+
);
|
|
20231
|
+
clack20.log.info(
|
|
20232
|
+
`You can create a token at: ${pc21.cyan(getDNSProviderTokenUrl2(dnsProvider))}`
|
|
20233
|
+
);
|
|
20234
|
+
}
|
|
20235
|
+
if (!options.yes) {
|
|
20236
|
+
const continueManual = await promptContinueManualDNS2();
|
|
20237
|
+
if (continueManual) {
|
|
20238
|
+
dnsProvider = "manual";
|
|
20239
|
+
}
|
|
20240
|
+
}
|
|
20241
|
+
}
|
|
20242
|
+
}
|
|
20243
|
+
if (!dnsAutoCreated) {
|
|
20244
|
+
const dnsRecords = buildRecords(receivingDomain, region);
|
|
20245
|
+
const formattedRecords = formatRecords(dnsRecords);
|
|
20246
|
+
console.log();
|
|
20247
|
+
clack20.log.info(pc21.bold("DNS Records Required"));
|
|
20248
|
+
console.log(
|
|
20249
|
+
` Add these records to your DNS provider for ${pc21.cyan(receivingDomain)}:
|
|
20250
|
+
`
|
|
20251
|
+
);
|
|
20252
|
+
for (const record of formattedRecords) {
|
|
20253
|
+
console.log(` ${pc21.dim(record.type.padEnd(6))} ${record.name}`);
|
|
20254
|
+
console.log(` ${pc21.dim("\u2192")} ${pc21.green(record.value)}
|
|
20255
|
+
`);
|
|
20256
|
+
}
|
|
20257
|
+
}
|
|
20258
|
+
await progress.execute("Saving configuration", async () => {
|
|
20259
|
+
addInboundDomainToMetadata(metadata, {
|
|
20260
|
+
subdomain,
|
|
20261
|
+
receivingDomain,
|
|
20262
|
+
parentDomain,
|
|
20263
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20264
|
+
});
|
|
20265
|
+
await saveConnectionMetadata(metadata);
|
|
20266
|
+
});
|
|
20267
|
+
if (isJsonMode()) {
|
|
20268
|
+
jsonSuccess("email.inbound.add", {
|
|
20269
|
+
receivingDomain,
|
|
20270
|
+
subdomain,
|
|
20271
|
+
parentDomain,
|
|
20272
|
+
dnsAutoCreated,
|
|
20273
|
+
region
|
|
20274
|
+
});
|
|
20275
|
+
return;
|
|
20276
|
+
}
|
|
20277
|
+
console.log();
|
|
20278
|
+
clack20.log.success(
|
|
20279
|
+
`${pc21.bold("Added inbound domain:")} ${pc21.cyan(receivingDomain)}`
|
|
20280
|
+
);
|
|
20281
|
+
console.log();
|
|
20282
|
+
if (!dnsAutoCreated) {
|
|
20283
|
+
console.log(
|
|
20284
|
+
` ${pc21.dim("1.")} Add DNS records above to your DNS provider`
|
|
20285
|
+
);
|
|
20286
|
+
console.log(
|
|
20287
|
+
` ${pc21.dim("2.")} Verify: ${pc21.cyan("wraps email inbound verify")}`
|
|
20288
|
+
);
|
|
20289
|
+
} else {
|
|
20290
|
+
console.log(
|
|
20291
|
+
` Verify: ${pc21.cyan("wraps email inbound verify")}`
|
|
20292
|
+
);
|
|
20293
|
+
}
|
|
20294
|
+
console.log();
|
|
20295
|
+
}
|
|
20296
|
+
async function inboundRemove(options) {
|
|
20297
|
+
if (!isJsonMode()) {
|
|
20298
|
+
clack20.intro(pc21.bold("Remove Inbound Receiving Domain"));
|
|
20299
|
+
}
|
|
20300
|
+
const progress = new DeploymentProgress();
|
|
20301
|
+
const identity = await progress.execute(
|
|
20302
|
+
"Validating AWS credentials",
|
|
20303
|
+
async () => validateAWSCredentials()
|
|
20304
|
+
);
|
|
20305
|
+
const region = options.region || await getAWSRegion();
|
|
20306
|
+
const metadata = await loadConnectionMetadata(identity.accountId, region);
|
|
20307
|
+
if (!metadata?.services?.email?.config?.inbound?.enabled) {
|
|
20308
|
+
clack20.log.error("Inbound email infrastructure is not deployed.");
|
|
20309
|
+
console.log(
|
|
20310
|
+
`
|
|
20311
|
+
Deploy first: ${pc21.cyan("wraps email inbound init")}
|
|
20312
|
+
`
|
|
20313
|
+
);
|
|
20314
|
+
process.exit(1);
|
|
20315
|
+
}
|
|
20316
|
+
const emailConfig = metadata.services.email.config;
|
|
20317
|
+
const inboundDomains = emailConfig.inboundDomains ?? [];
|
|
20318
|
+
if (inboundDomains.length === 0) {
|
|
20319
|
+
clack20.log.warn("No inbound domains configured.");
|
|
20320
|
+
return;
|
|
20321
|
+
}
|
|
20322
|
+
let domainToRemove = options.domain;
|
|
20323
|
+
if (!domainToRemove) {
|
|
20324
|
+
if (inboundDomains.length === 1) {
|
|
20325
|
+
domainToRemove = inboundDomains[0].receivingDomain;
|
|
20326
|
+
} else {
|
|
20327
|
+
const selected = await clack20.select({
|
|
20328
|
+
message: "Which inbound domain do you want to remove?",
|
|
20329
|
+
options: inboundDomains.map((d) => ({
|
|
20330
|
+
value: d.receivingDomain,
|
|
20331
|
+
label: d.receivingDomain,
|
|
20332
|
+
hint: `added ${d.addedAt.split("T")[0]}`
|
|
20333
|
+
}))
|
|
20334
|
+
});
|
|
20335
|
+
if (clack20.isCancel(selected)) {
|
|
20336
|
+
clack20.cancel("Operation cancelled.");
|
|
20337
|
+
process.exit(0);
|
|
20338
|
+
}
|
|
20339
|
+
domainToRemove = selected;
|
|
20340
|
+
}
|
|
20341
|
+
}
|
|
20342
|
+
if (!inboundDomains.some((d) => d.receivingDomain === domainToRemove)) {
|
|
20343
|
+
clack20.log.error(
|
|
20344
|
+
`${pc21.cyan(domainToRemove)} is not in the inbound domains list.`
|
|
20345
|
+
);
|
|
20346
|
+
return;
|
|
20347
|
+
}
|
|
20348
|
+
if (inboundDomains.length === 1) {
|
|
20349
|
+
clack20.log.error(
|
|
20350
|
+
"Cannot remove the last inbound domain. Use " + pc21.cyan("wraps email inbound destroy") + " to remove all inbound infrastructure."
|
|
20351
|
+
);
|
|
20352
|
+
return;
|
|
20353
|
+
}
|
|
20354
|
+
if (!options.yes) {
|
|
20355
|
+
const confirmed = await clack20.confirm({
|
|
20356
|
+
message: `Remove inbound domain ${pc21.cyan(domainToRemove)}?`,
|
|
20357
|
+
initialValue: false
|
|
20358
|
+
});
|
|
20359
|
+
if (clack20.isCancel(confirmed) || !confirmed) {
|
|
20360
|
+
clack20.cancel("Operation cancelled.");
|
|
20361
|
+
process.exit(0);
|
|
20362
|
+
}
|
|
20363
|
+
}
|
|
20364
|
+
await progress.execute("Updating SES receipt rule", async () => {
|
|
20365
|
+
await removeDomainFromReceiptRule(region, domainToRemove);
|
|
20366
|
+
});
|
|
20367
|
+
await progress.execute("Saving configuration", async () => {
|
|
20368
|
+
removeInboundDomainFromMetadata(metadata, domainToRemove);
|
|
20369
|
+
await saveConnectionMetadata(metadata);
|
|
20370
|
+
});
|
|
20371
|
+
if (isJsonMode()) {
|
|
20372
|
+
jsonSuccess("email.inbound.remove", {
|
|
20373
|
+
removedDomain: domainToRemove,
|
|
20374
|
+
remainingDomains: (emailConfig.inboundDomains ?? []).map(
|
|
20375
|
+
(d) => d.receivingDomain
|
|
20376
|
+
),
|
|
20377
|
+
region
|
|
20378
|
+
});
|
|
20379
|
+
return;
|
|
20380
|
+
}
|
|
20381
|
+
console.log();
|
|
20382
|
+
clack20.log.success(
|
|
20383
|
+
`${pc21.bold("Removed inbound domain:")} ${pc21.cyan(domainToRemove)}`
|
|
20384
|
+
);
|
|
20385
|
+
console.log();
|
|
20386
|
+
console.log(
|
|
20387
|
+
` ${pc21.dim("Remember to remove the MX and SPF DNS records for")} ${pc21.cyan(domainToRemove)}`
|
|
20388
|
+
);
|
|
20389
|
+
console.log();
|
|
20390
|
+
}
|
|
19891
20391
|
|
|
19892
20392
|
// src/commands/email/init.ts
|
|
19893
20393
|
init_esm_shims();
|
|
@@ -21870,7 +22370,7 @@ async function findCliNodeModules() {
|
|
|
21870
22370
|
const paths = [];
|
|
21871
22371
|
try {
|
|
21872
22372
|
const { createRequire } = await import("module");
|
|
21873
|
-
const { dirname:
|
|
22373
|
+
const { dirname: dirname5 } = await import("path");
|
|
21874
22374
|
for (const base of [
|
|
21875
22375
|
// The current file's location (works when running from source)
|
|
21876
22376
|
import.meta.url,
|
|
@@ -21880,7 +22380,7 @@ async function findCliNodeModules() {
|
|
|
21880
22380
|
try {
|
|
21881
22381
|
const req = createRequire(base);
|
|
21882
22382
|
const reactPkg = req.resolve("react/package.json");
|
|
21883
|
-
const reactNodeModules = join10(
|
|
22383
|
+
const reactNodeModules = join10(dirname5(reactPkg), "..");
|
|
21884
22384
|
if (existsSync9(join10(reactNodeModules, "react"))) {
|
|
21885
22385
|
paths.push(reactNodeModules);
|
|
21886
22386
|
break;
|
|
@@ -24458,6 +24958,13 @@ ${pc30.bold("Add these DNS records to your DNS provider:")}
|
|
|
24458
24958
|
if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {
|
|
24459
24959
|
acmValidationRecords.push(...outputs.acmCertificateValidationRecords);
|
|
24460
24960
|
}
|
|
24961
|
+
if (acmValidationRecords.length === 0 && outputs.httpsTrackingPending && outputs.customTrackingDomain) {
|
|
24962
|
+
const { getCertificateValidationRecords: getCertificateValidationRecords2 } = await Promise.resolve().then(() => (init_acm(), acm_exports));
|
|
24963
|
+
const directRecords = await getCertificateValidationRecords2(
|
|
24964
|
+
outputs.customTrackingDomain
|
|
24965
|
+
);
|
|
24966
|
+
acmValidationRecords.push(...directRecords);
|
|
24967
|
+
}
|
|
24461
24968
|
let acmDnsAutoCreated = false;
|
|
24462
24969
|
if (outputs.httpsTrackingPending && acmValidationRecords.length > 0 && outputs.customTrackingDomain) {
|
|
24463
24970
|
const trackingDnsProvider = metadata.services.email?.dnsProvider;
|
|
@@ -27137,7 +27644,7 @@ import {
|
|
|
27137
27644
|
IAMClient as IAMClient2,
|
|
27138
27645
|
PutRolePolicyCommand
|
|
27139
27646
|
} from "@aws-sdk/client-iam";
|
|
27140
|
-
import { confirm as confirm14, intro as intro32, isCancel as isCancel20, log as log32, outro as outro19, select as
|
|
27647
|
+
import { confirm as confirm14, intro as intro32, isCancel as isCancel20, log as log32, outro as outro19, select as select15 } from "@clack/prompts";
|
|
27141
27648
|
import * as pulumi21 from "@pulumi/pulumi";
|
|
27142
27649
|
import pc36 from "picocolors";
|
|
27143
27650
|
init_events();
|
|
@@ -27480,7 +27987,7 @@ async function resolveOrganization() {
|
|
|
27480
27987
|
if (orgs.length === 1) {
|
|
27481
27988
|
return orgs[0];
|
|
27482
27989
|
}
|
|
27483
|
-
const selected = await
|
|
27990
|
+
const selected = await select15({
|
|
27484
27991
|
message: "Which organization should this AWS account connect to?",
|
|
27485
27992
|
options: orgs.map((org) => ({
|
|
27486
27993
|
value: org.id,
|
|
@@ -27776,7 +28283,7 @@ Run ${pc36.cyan("wraps email init")} or ${pc36.cyan("wraps sms init")} first.
|
|
|
27776
28283
|
log32.info(
|
|
27777
28284
|
`Already connected to Wraps Platform (AWS Account: ${pc36.cyan(metadata.accountId)})`
|
|
27778
28285
|
);
|
|
27779
|
-
const action = await
|
|
28286
|
+
const action = await select15({
|
|
27780
28287
|
message: "What would you like to do?",
|
|
27781
28288
|
options: [
|
|
27782
28289
|
{
|
|
@@ -35746,7 +36253,7 @@ if (nodeMajorVersion < 20) {
|
|
|
35746
36253
|
process.exit(1);
|
|
35747
36254
|
}
|
|
35748
36255
|
var __filename2 = fileURLToPath5(import.meta.url);
|
|
35749
|
-
var __dirname3 =
|
|
36256
|
+
var __dirname3 = dirname4(__filename2);
|
|
35750
36257
|
var packageJson = JSON.parse(
|
|
35751
36258
|
readFileSync3(join19(__dirname3, "../package.json"), "utf-8")
|
|
35752
36259
|
);
|
|
@@ -36136,6 +36643,11 @@ args.options([
|
|
|
36136
36643
|
name: "org",
|
|
36137
36644
|
description: "Organization slug",
|
|
36138
36645
|
defaultValue: void 0
|
|
36646
|
+
},
|
|
36647
|
+
{
|
|
36648
|
+
name: "subdomain",
|
|
36649
|
+
description: "Subdomain for inbound email (e.g., inbound, support)",
|
|
36650
|
+
defaultValue: void 0
|
|
36139
36651
|
}
|
|
36140
36652
|
]);
|
|
36141
36653
|
var flags = args.parse(process.argv);
|
|
@@ -36450,13 +36962,30 @@ Usage: ${pc53.cyan("wraps email verify --domain yourapp.com")}
|
|
|
36450
36962
|
json: flags.json
|
|
36451
36963
|
});
|
|
36452
36964
|
break;
|
|
36965
|
+
case "add":
|
|
36966
|
+
await inboundAdd({
|
|
36967
|
+
region: flags.region,
|
|
36968
|
+
subdomain: flags.subdomain,
|
|
36969
|
+
domain: flags.domain,
|
|
36970
|
+
yes: flags.yes,
|
|
36971
|
+
json: flags.json
|
|
36972
|
+
});
|
|
36973
|
+
break;
|
|
36974
|
+
case "remove":
|
|
36975
|
+
await inboundRemove({
|
|
36976
|
+
region: flags.region,
|
|
36977
|
+
domain: flags.domain,
|
|
36978
|
+
yes: flags.yes,
|
|
36979
|
+
json: flags.json
|
|
36980
|
+
});
|
|
36981
|
+
break;
|
|
36453
36982
|
default:
|
|
36454
36983
|
clack50.log.error(
|
|
36455
36984
|
`Unknown inbound command: ${inboundSubCommand || "(none)"}`
|
|
36456
36985
|
);
|
|
36457
36986
|
console.log(
|
|
36458
36987
|
`
|
|
36459
|
-
Available commands: ${pc53.cyan("init")}, ${pc53.cyan("destroy")}, ${pc53.cyan("status")}, ${pc53.cyan("verify")}, ${pc53.cyan("test")}
|
|
36988
|
+
Available commands: ${pc53.cyan("init")}, ${pc53.cyan("destroy")}, ${pc53.cyan("status")}, ${pc53.cyan("verify")}, ${pc53.cyan("test")}, ${pc53.cyan("add")}, ${pc53.cyan("remove")}
|
|
36460
36989
|
`
|
|
36461
36990
|
);
|
|
36462
36991
|
throw new Error(
|