@wraps.dev/cli 2.11.2 → 2.11.3
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
CHANGED
|
@@ -4880,9 +4880,36 @@ var init_lambda = __esm({
|
|
|
4880
4880
|
// src/infrastructure/resources/acm.ts
|
|
4881
4881
|
var acm_exports = {};
|
|
4882
4882
|
__export(acm_exports, {
|
|
4883
|
+
checkCertificateValidation: () => checkCertificateValidation,
|
|
4883
4884
|
createACMCertificate: () => createACMCertificate
|
|
4884
4885
|
});
|
|
4886
|
+
import { ACMClient as ACMClient2, DescribeCertificateCommand as DescribeCertificateCommand2 } from "@aws-sdk/client-acm";
|
|
4885
4887
|
import * as aws12 from "@pulumi/aws";
|
|
4888
|
+
async function checkCertificateValidation(domain) {
|
|
4889
|
+
try {
|
|
4890
|
+
const acm3 = new ACMClient2({ region: "us-east-1" });
|
|
4891
|
+
const { ListCertificatesCommand } = await import("@aws-sdk/client-acm");
|
|
4892
|
+
const listResponse = await acm3.send(
|
|
4893
|
+
new ListCertificatesCommand({
|
|
4894
|
+
CertificateStatuses: ["ISSUED"]
|
|
4895
|
+
})
|
|
4896
|
+
);
|
|
4897
|
+
const cert = listResponse.CertificateSummaryList?.find(
|
|
4898
|
+
(c) => c.DomainName === domain
|
|
4899
|
+
);
|
|
4900
|
+
if (cert?.CertificateArn) {
|
|
4901
|
+
const describeResponse = await acm3.send(
|
|
4902
|
+
new DescribeCertificateCommand2({
|
|
4903
|
+
CertificateArn: cert.CertificateArn
|
|
4904
|
+
})
|
|
4905
|
+
);
|
|
4906
|
+
return describeResponse.Certificate?.Status === "ISSUED";
|
|
4907
|
+
}
|
|
4908
|
+
return false;
|
|
4909
|
+
} catch (error) {
|
|
4910
|
+
return false;
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4886
4913
|
async function createACMCertificate(config2) {
|
|
4887
4914
|
const usEast1Provider = new aws12.Provider("acm-us-east-1", {
|
|
4888
4915
|
region: "us-east-1"
|
|
@@ -5618,6 +5645,10 @@ var init_eventbridge_inbound = __esm({
|
|
|
5618
5645
|
});
|
|
5619
5646
|
|
|
5620
5647
|
// src/utils/dns/cloudflare.ts
|
|
5648
|
+
var cloudflare_exports = {};
|
|
5649
|
+
__export(cloudflare_exports, {
|
|
5650
|
+
CloudflareDNSClient: () => CloudflareDNSClient
|
|
5651
|
+
});
|
|
5621
5652
|
var CLOUDFLARE_API_BASE, CloudflareDNSClient;
|
|
5622
5653
|
var init_cloudflare = __esm({
|
|
5623
5654
|
"src/utils/dns/cloudflare.ts"() {
|
|
@@ -5844,6 +5875,92 @@ var init_cloudflare = __esm({
|
|
|
5844
5875
|
};
|
|
5845
5876
|
}
|
|
5846
5877
|
}
|
|
5878
|
+
/**
|
|
5879
|
+
* Get the zone name (domain) for this zone ID
|
|
5880
|
+
*/
|
|
5881
|
+
async getZoneName() {
|
|
5882
|
+
try {
|
|
5883
|
+
const response = await fetch(
|
|
5884
|
+
`${CLOUDFLARE_API_BASE}/zones/${this.zoneId}`,
|
|
5885
|
+
{
|
|
5886
|
+
headers: {
|
|
5887
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
5888
|
+
"Content-Type": "application/json"
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
);
|
|
5892
|
+
const data = await response.json();
|
|
5893
|
+
return data.success ? data.result.name : null;
|
|
5894
|
+
} catch {
|
|
5895
|
+
return null;
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
/**
|
|
5899
|
+
* Get all CAA records for the zone
|
|
5900
|
+
*/
|
|
5901
|
+
async getCAARecords() {
|
|
5902
|
+
const zoneName = await this.getZoneName();
|
|
5903
|
+
if (!zoneName) return [];
|
|
5904
|
+
const result = await this.request(
|
|
5905
|
+
"/dns_records?type=CAA"
|
|
5906
|
+
);
|
|
5907
|
+
if (!(result.success && result.result)) {
|
|
5908
|
+
return [];
|
|
5909
|
+
}
|
|
5910
|
+
return result.result.filter((r) => r.type === "CAA").map((r) => {
|
|
5911
|
+
const match = r.content.match(/^(\d+)\s+(\w+)\s+"?([^"]+)"?$/);
|
|
5912
|
+
if (match) {
|
|
5913
|
+
return {
|
|
5914
|
+
flags: Number.parseInt(match[1], 10),
|
|
5915
|
+
tag: match[2],
|
|
5916
|
+
value: match[3]
|
|
5917
|
+
};
|
|
5918
|
+
}
|
|
5919
|
+
return null;
|
|
5920
|
+
}).filter(
|
|
5921
|
+
(r) => r !== null
|
|
5922
|
+
);
|
|
5923
|
+
}
|
|
5924
|
+
/**
|
|
5925
|
+
* Check if Amazon is allowed to issue certificates based on CAA records
|
|
5926
|
+
*/
|
|
5927
|
+
async isAmazonCAAAllowed() {
|
|
5928
|
+
const caaRecords = await this.getCAARecords();
|
|
5929
|
+
const issueRecords = caaRecords.filter(
|
|
5930
|
+
(r) => r.tag === "issue" || r.tag === "issuewild"
|
|
5931
|
+
);
|
|
5932
|
+
if (issueRecords.length === 0) {
|
|
5933
|
+
return { allowed: true, hasCAA: false, existingCAs: [] };
|
|
5934
|
+
}
|
|
5935
|
+
const existingCAs = issueRecords.map((r) => r.value);
|
|
5936
|
+
const amazonAllowed = existingCAs.some(
|
|
5937
|
+
(ca) => ca.includes("amazon.com") || ca.includes("amazontrust.com")
|
|
5938
|
+
);
|
|
5939
|
+
return { allowed: amazonAllowed, hasCAA: true, existingCAs };
|
|
5940
|
+
}
|
|
5941
|
+
/**
|
|
5942
|
+
* Add a CAA record to allow Amazon to issue certificates
|
|
5943
|
+
*/
|
|
5944
|
+
async addAmazonCAARecord() {
|
|
5945
|
+
const zoneName = await this.getZoneName();
|
|
5946
|
+
if (!zoneName) return false;
|
|
5947
|
+
const body = {
|
|
5948
|
+
name: zoneName,
|
|
5949
|
+
type: "CAA",
|
|
5950
|
+
data: {
|
|
5951
|
+
flags: 0,
|
|
5952
|
+
tag: "issue",
|
|
5953
|
+
value: "amazon.com"
|
|
5954
|
+
},
|
|
5955
|
+
ttl: 1800
|
|
5956
|
+
};
|
|
5957
|
+
const result = await this.request(
|
|
5958
|
+
"/dns_records",
|
|
5959
|
+
"POST",
|
|
5960
|
+
body
|
|
5961
|
+
);
|
|
5962
|
+
return result.success;
|
|
5963
|
+
}
|
|
5847
5964
|
async verifyRecords(data) {
|
|
5848
5965
|
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
5849
5966
|
const missing = [];
|
|
@@ -5891,6 +6008,10 @@ var init_cloudflare = __esm({
|
|
|
5891
6008
|
});
|
|
5892
6009
|
|
|
5893
6010
|
// src/utils/dns/vercel.ts
|
|
6011
|
+
var vercel_exports = {};
|
|
6012
|
+
__export(vercel_exports, {
|
|
6013
|
+
VercelDNSClient: () => VercelDNSClient
|
|
6014
|
+
});
|
|
5894
6015
|
var VERCEL_API_BASE, VercelDNSClient;
|
|
5895
6016
|
var init_vercel = __esm({
|
|
5896
6017
|
"src/utils/dns/vercel.ts"() {
|
|
@@ -6122,6 +6243,68 @@ var init_vercel = __esm({
|
|
|
6122
6243
|
};
|
|
6123
6244
|
}
|
|
6124
6245
|
}
|
|
6246
|
+
/**
|
|
6247
|
+
* Get all CAA records for the domain
|
|
6248
|
+
*/
|
|
6249
|
+
async getCAARecords() {
|
|
6250
|
+
const result = await this.request(
|
|
6251
|
+
`/v4/domains/${this.domain}/records`
|
|
6252
|
+
);
|
|
6253
|
+
if (result.error || !result.records) {
|
|
6254
|
+
return [];
|
|
6255
|
+
}
|
|
6256
|
+
return result.records.filter((r) => r.type === "CAA").map((r) => {
|
|
6257
|
+
const match = r.value.match(/^(\d+)\s+(\w+)\s+"?([^"]+)"?$/);
|
|
6258
|
+
if (match) {
|
|
6259
|
+
return {
|
|
6260
|
+
flags: Number.parseInt(match[1], 10),
|
|
6261
|
+
tag: match[2],
|
|
6262
|
+
value: match[3]
|
|
6263
|
+
};
|
|
6264
|
+
}
|
|
6265
|
+
return null;
|
|
6266
|
+
}).filter(
|
|
6267
|
+
(r) => r !== null
|
|
6268
|
+
);
|
|
6269
|
+
}
|
|
6270
|
+
/**
|
|
6271
|
+
* Check if Amazon is allowed to issue certificates based on CAA records
|
|
6272
|
+
* Returns true if:
|
|
6273
|
+
* - No CAA records exist (any CA can issue)
|
|
6274
|
+
* - CAA records exist and include amazon.com or amazontrust.com
|
|
6275
|
+
*/
|
|
6276
|
+
async isAmazonCAAAllowed() {
|
|
6277
|
+
const caaRecords = await this.getCAARecords();
|
|
6278
|
+
const issueRecords = caaRecords.filter(
|
|
6279
|
+
(r) => r.tag === "issue" || r.tag === "issuewild"
|
|
6280
|
+
);
|
|
6281
|
+
if (issueRecords.length === 0) {
|
|
6282
|
+
return { allowed: true, hasCAA: false, existingCAs: [] };
|
|
6283
|
+
}
|
|
6284
|
+
const existingCAs = issueRecords.map((r) => r.value);
|
|
6285
|
+
const amazonAllowed = existingCAs.some(
|
|
6286
|
+
(ca) => ca.includes("amazon.com") || ca.includes("amazontrust.com")
|
|
6287
|
+
);
|
|
6288
|
+
return { allowed: amazonAllowed, hasCAA: true, existingCAs };
|
|
6289
|
+
}
|
|
6290
|
+
/**
|
|
6291
|
+
* Add a CAA record to allow Amazon to issue certificates
|
|
6292
|
+
*/
|
|
6293
|
+
async addAmazonCAARecord() {
|
|
6294
|
+
const body = {
|
|
6295
|
+
name: "@",
|
|
6296
|
+
// Root domain
|
|
6297
|
+
type: "CAA",
|
|
6298
|
+
value: '0 issue "amazon.com"',
|
|
6299
|
+
ttl: 1800
|
|
6300
|
+
};
|
|
6301
|
+
const result = await this.request(
|
|
6302
|
+
`/v2/domains/${this.domain}/records`,
|
|
6303
|
+
"POST",
|
|
6304
|
+
body
|
|
6305
|
+
);
|
|
6306
|
+
return !result.error;
|
|
6307
|
+
}
|
|
6125
6308
|
async verifyRecords(data) {
|
|
6126
6309
|
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
6127
6310
|
const missing = [];
|
|
@@ -6687,6 +6870,76 @@ var init_dns = __esm({
|
|
|
6687
6870
|
}
|
|
6688
6871
|
});
|
|
6689
6872
|
|
|
6873
|
+
// src/utils/dns/caa.ts
|
|
6874
|
+
var caa_exports = {};
|
|
6875
|
+
__export(caa_exports, {
|
|
6876
|
+
ensureAmazonCAAAllowed: () => ensureAmazonCAAAllowed
|
|
6877
|
+
});
|
|
6878
|
+
async function ensureAmazonCAAAllowed(credentials, domain) {
|
|
6879
|
+
if (credentials.provider === "manual" || credentials.provider === "route53") {
|
|
6880
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6881
|
+
}
|
|
6882
|
+
try {
|
|
6883
|
+
if (credentials.provider === "vercel") {
|
|
6884
|
+
const { VercelDNSClient: VercelDNSClient2 } = await Promise.resolve().then(() => (init_vercel(), vercel_exports));
|
|
6885
|
+
const client = new VercelDNSClient2(
|
|
6886
|
+
domain,
|
|
6887
|
+
credentials.token,
|
|
6888
|
+
credentials.teamId
|
|
6889
|
+
);
|
|
6890
|
+
const caaStatus = await client.isAmazonCAAAllowed();
|
|
6891
|
+
if (caaStatus.hasCAA && caaStatus.allowed) {
|
|
6892
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6893
|
+
}
|
|
6894
|
+
const added = await client.addAmazonCAARecord();
|
|
6895
|
+
if (added) {
|
|
6896
|
+
return { success: true, wasAlreadyAllowed: false, recordCreated: true };
|
|
6897
|
+
}
|
|
6898
|
+
return {
|
|
6899
|
+
success: false,
|
|
6900
|
+
wasAlreadyAllowed: false,
|
|
6901
|
+
recordCreated: false,
|
|
6902
|
+
error: "Failed to create CAA record in Vercel DNS"
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
if (credentials.provider === "cloudflare") {
|
|
6906
|
+
const { CloudflareDNSClient: CloudflareDNSClient2 } = await Promise.resolve().then(() => (init_cloudflare(), cloudflare_exports));
|
|
6907
|
+
const client = new CloudflareDNSClient2(
|
|
6908
|
+
credentials.zoneId,
|
|
6909
|
+
credentials.token
|
|
6910
|
+
);
|
|
6911
|
+
const caaStatus = await client.isAmazonCAAAllowed();
|
|
6912
|
+
if (caaStatus.allowed) {
|
|
6913
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6914
|
+
}
|
|
6915
|
+
const added = await client.addAmazonCAARecord();
|
|
6916
|
+
if (added) {
|
|
6917
|
+
return { success: true, wasAlreadyAllowed: false, recordCreated: true };
|
|
6918
|
+
}
|
|
6919
|
+
return {
|
|
6920
|
+
success: false,
|
|
6921
|
+
wasAlreadyAllowed: false,
|
|
6922
|
+
recordCreated: false,
|
|
6923
|
+
error: "Failed to create CAA record in Cloudflare DNS"
|
|
6924
|
+
};
|
|
6925
|
+
}
|
|
6926
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6927
|
+
} catch (error) {
|
|
6928
|
+
return {
|
|
6929
|
+
success: false,
|
|
6930
|
+
wasAlreadyAllowed: false,
|
|
6931
|
+
recordCreated: false,
|
|
6932
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6936
|
+
var init_caa = __esm({
|
|
6937
|
+
"src/utils/dns/caa.ts"() {
|
|
6938
|
+
"use strict";
|
|
6939
|
+
init_esm_shims();
|
|
6940
|
+
}
|
|
6941
|
+
});
|
|
6942
|
+
|
|
6690
6943
|
// src/utils/shared/assume-role.ts
|
|
6691
6944
|
var assume_role_exports = {};
|
|
6692
6945
|
__export(assume_role_exports, {
|
|
@@ -10753,10 +11006,10 @@ async function cdnSync(options) {
|
|
|
10753
11006
|
});
|
|
10754
11007
|
const stackOutputs = await checkStack.outputs();
|
|
10755
11008
|
if (stackOutputs.acmCertificateArn?.value) {
|
|
10756
|
-
const { ACMClient:
|
|
10757
|
-
const acmClient = new
|
|
11009
|
+
const { ACMClient: ACMClient3, DescribeCertificateCommand: DescribeCertificateCommand3 } = await import("@aws-sdk/client-acm");
|
|
11010
|
+
const acmClient = new ACMClient3({ region: "us-east-1" });
|
|
10758
11011
|
const certResponse = await acmClient.send(
|
|
10759
|
-
new
|
|
11012
|
+
new DescribeCertificateCommand3({
|
|
10760
11013
|
CertificateArn: stackOutputs.acmCertificateArn.value
|
|
10761
11014
|
})
|
|
10762
11015
|
);
|
|
@@ -10941,12 +11194,12 @@ Current configuration:
|
|
|
10941
11194
|
process.exit(0);
|
|
10942
11195
|
}
|
|
10943
11196
|
progress.start("Checking certificate validation status");
|
|
10944
|
-
const { ACMClient:
|
|
10945
|
-
const acmClient = new
|
|
11197
|
+
const { ACMClient: ACMClient3, DescribeCertificateCommand: DescribeCertificateCommand3 } = await import("@aws-sdk/client-acm");
|
|
11198
|
+
const acmClient = new ACMClient3({ region: "us-east-1" });
|
|
10946
11199
|
let certStatus;
|
|
10947
11200
|
try {
|
|
10948
11201
|
const certResponse = await acmClient.send(
|
|
10949
|
-
new
|
|
11202
|
+
new DescribeCertificateCommand3({
|
|
10950
11203
|
CertificateArn: stackOutputs.acmCertificateArn.value
|
|
10951
11204
|
})
|
|
10952
11205
|
);
|
|
@@ -11142,10 +11395,10 @@ async function checkDNSRecord(hostname, expectedValue) {
|
|
|
11142
11395
|
}
|
|
11143
11396
|
async function checkCertificateStatus(certificateArn) {
|
|
11144
11397
|
try {
|
|
11145
|
-
const { ACMClient:
|
|
11146
|
-
const acm3 = new
|
|
11398
|
+
const { ACMClient: ACMClient3, DescribeCertificateCommand: DescribeCertificateCommand3 } = await import("@aws-sdk/client-acm");
|
|
11399
|
+
const acm3 = new ACMClient3({ region: "us-east-1" });
|
|
11147
11400
|
const result = await acm3.send(
|
|
11148
|
-
new
|
|
11401
|
+
new DescribeCertificateCommand3({ CertificateArn: certificateArn })
|
|
11149
11402
|
);
|
|
11150
11403
|
const cert = result.Certificate;
|
|
11151
11404
|
const validationStatus = cert?.DomainValidationOptions?.[0]?.ValidationStatus;
|
|
@@ -15296,6 +15549,7 @@ async function deployEmailStack(config2) {
|
|
|
15296
15549
|
});
|
|
15297
15550
|
let cloudFrontResources;
|
|
15298
15551
|
let acmResources;
|
|
15552
|
+
let skipCloudFront = false;
|
|
15299
15553
|
if (emailConfig.tracking?.enabled && emailConfig.tracking.customRedirectDomain && emailConfig.tracking.httpsEnabled) {
|
|
15300
15554
|
const { findHostedZone: findHostedZone2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
|
|
15301
15555
|
const hostedZone = await findHostedZone2(
|
|
@@ -15307,16 +15561,27 @@ async function deployEmailStack(config2) {
|
|
|
15307
15561
|
domain: emailConfig.tracking.customRedirectDomain,
|
|
15308
15562
|
hostedZoneId: hostedZone?.id
|
|
15309
15563
|
});
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15564
|
+
if (!hostedZone) {
|
|
15565
|
+
const { checkCertificateValidation: checkCertificateValidation2 } = await Promise.resolve().then(() => (init_acm(), acm_exports));
|
|
15566
|
+
const isValidated = await checkCertificateValidation2(
|
|
15567
|
+
emailConfig.tracking.customRedirectDomain
|
|
15568
|
+
);
|
|
15569
|
+
if (!isValidated) {
|
|
15570
|
+
skipCloudFront = true;
|
|
15571
|
+
}
|
|
15572
|
+
}
|
|
15573
|
+
if (!skipCloudFront) {
|
|
15574
|
+
const { createCloudFrontTracking: createCloudFrontTracking2 } = await Promise.resolve().then(() => (init_cloudfront(), cloudfront_exports));
|
|
15575
|
+
const certificateArn = acmResources.certificateValidation ? acmResources.certificateValidation.certificateArn : acmResources.certificate.arn;
|
|
15576
|
+
cloudFrontResources = await createCloudFrontTracking2({
|
|
15577
|
+
customTrackingDomain: emailConfig.tracking.customRedirectDomain,
|
|
15578
|
+
region: config2.region,
|
|
15579
|
+
certificateArn,
|
|
15580
|
+
hostedZoneId: hostedZone?.id,
|
|
15581
|
+
// Pass hosted zone ID for automatic DNS record creation
|
|
15582
|
+
wafEnabled: emailConfig.tracking.wafEnabled
|
|
15583
|
+
});
|
|
15584
|
+
}
|
|
15320
15585
|
}
|
|
15321
15586
|
let sesResources;
|
|
15322
15587
|
if (emailConfig.tracking?.enabled || emailConfig.eventTracking?.enabled) {
|
|
@@ -15329,11 +15594,19 @@ async function deployEmailStack(config2) {
|
|
|
15329
15594
|
if (!mailFromDomain && emailConfig.mailFromSubdomain && emailConfig.domain) {
|
|
15330
15595
|
mailFromDomain = `${emailConfig.mailFromSubdomain}.${emailConfig.domain}`;
|
|
15331
15596
|
}
|
|
15597
|
+
const effectiveTrackingConfig = skipCloudFront && emailConfig.tracking ? {
|
|
15598
|
+
enabled: emailConfig.tracking.enabled,
|
|
15599
|
+
opens: emailConfig.tracking.opens,
|
|
15600
|
+
clicks: emailConfig.tracking.clicks,
|
|
15601
|
+
customRedirectDomain: emailConfig.tracking.customRedirectDomain,
|
|
15602
|
+
httpsEnabled: false
|
|
15603
|
+
// Use OPTIONAL until CloudFront is ready
|
|
15604
|
+
} : emailConfig.tracking;
|
|
15332
15605
|
sesResources = await createSESResources({
|
|
15333
15606
|
domain: emailConfig.domain,
|
|
15334
15607
|
mailFromDomain,
|
|
15335
15608
|
region: config2.region,
|
|
15336
|
-
trackingConfig:
|
|
15609
|
+
trackingConfig: effectiveTrackingConfig,
|
|
15337
15610
|
eventTypes: emailConfig.eventTracking?.events,
|
|
15338
15611
|
eventTrackingEnabled: emailConfig.eventTracking?.enabled,
|
|
15339
15612
|
// Pass flag to create EventBridge destination
|
|
@@ -15461,6 +15734,8 @@ async function deployEmailStack(config2) {
|
|
|
15461
15734
|
dlqUrl: sqsResources?.dlq.url,
|
|
15462
15735
|
customTrackingDomain: sesResources?.customTrackingDomain,
|
|
15463
15736
|
httpsTrackingEnabled: emailConfig.tracking?.httpsEnabled,
|
|
15737
|
+
httpsTrackingPending: skipCloudFront,
|
|
15738
|
+
// True if HTTPS requested but cert not validated yet
|
|
15464
15739
|
cloudFrontDomain: cloudFrontResources?.domainName,
|
|
15465
15740
|
acmCertificateValidationRecords: acmResources?.validationRecords,
|
|
15466
15741
|
mailFromDomain: sesResources?.mailFromDomain,
|
|
@@ -21408,6 +21683,56 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21408
21683
|
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
21409
21684
|
emailConfig: updatedConfig
|
|
21410
21685
|
});
|
|
21686
|
+
if (updatedConfig.tracking?.httpsEnabled && updatedConfig.tracking.customRedirectDomain) {
|
|
21687
|
+
const trackingDomainParts = updatedConfig.tracking.customRedirectDomain.split(".");
|
|
21688
|
+
const parentDomain = trackingDomainParts.length > 2 ? trackingDomainParts.slice(-2).join(".") : updatedConfig.tracking.customRedirectDomain;
|
|
21689
|
+
let dnsProvider = metadata.services.email?.dnsProvider;
|
|
21690
|
+
if (!dnsProvider) {
|
|
21691
|
+
const availableProviders = await progress.execute(
|
|
21692
|
+
"Detecting DNS provider for CAA check",
|
|
21693
|
+
async () => await detectAvailableDNSProviders(parentDomain, region)
|
|
21694
|
+
);
|
|
21695
|
+
const detectedProvider = availableProviders.find(
|
|
21696
|
+
(p) => p.detected && p.provider !== "manual"
|
|
21697
|
+
);
|
|
21698
|
+
if (detectedProvider) {
|
|
21699
|
+
dnsProvider = detectedProvider.provider;
|
|
21700
|
+
if (metadata.services.email) {
|
|
21701
|
+
metadata.services.email.dnsProvider = dnsProvider;
|
|
21702
|
+
}
|
|
21703
|
+
}
|
|
21704
|
+
}
|
|
21705
|
+
if (dnsProvider && dnsProvider !== "manual" && dnsProvider !== "route53") {
|
|
21706
|
+
const credResult = await getDNSCredentials(
|
|
21707
|
+
dnsProvider,
|
|
21708
|
+
parentDomain,
|
|
21709
|
+
region
|
|
21710
|
+
);
|
|
21711
|
+
if (credResult.valid && credResult.credentials) {
|
|
21712
|
+
const { ensureAmazonCAAAllowed: ensureAmazonCAAAllowed2 } = await Promise.resolve().then(() => (init_caa(), caa_exports));
|
|
21713
|
+
const caaResult = await progress.execute(
|
|
21714
|
+
"Checking CAA records for certificate issuance",
|
|
21715
|
+
async () => await ensureAmazonCAAAllowed2(credResult.credentials, parentDomain)
|
|
21716
|
+
);
|
|
21717
|
+
if (caaResult.recordCreated) {
|
|
21718
|
+
progress.info(
|
|
21719
|
+
`Added CAA record to allow Amazon certificate issuance for ${pc28.cyan(parentDomain)}`
|
|
21720
|
+
);
|
|
21721
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
21722
|
+
} else if (!caaResult.success) {
|
|
21723
|
+
clack27.log.warn(
|
|
21724
|
+
`Could not verify CAA records: ${caaResult.error || "Unknown error"}`
|
|
21725
|
+
);
|
|
21726
|
+
clack27.log.info(
|
|
21727
|
+
pc28.dim(
|
|
21728
|
+
"If certificate issuance fails, you may need to add a CAA record manually:"
|
|
21729
|
+
)
|
|
21730
|
+
);
|
|
21731
|
+
clack27.log.info(pc28.dim(` ${parentDomain} CAA 0 issue "amazon.com"`));
|
|
21732
|
+
}
|
|
21733
|
+
}
|
|
21734
|
+
}
|
|
21735
|
+
}
|
|
21411
21736
|
if (options.preview) {
|
|
21412
21737
|
try {
|
|
21413
21738
|
const previewResult = await progress.execute(
|
|
@@ -21508,6 +21833,7 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21508
21833
|
dkimTokens: result.dkimTokens,
|
|
21509
21834
|
customTrackingDomain: result.customTrackingDomain,
|
|
21510
21835
|
httpsTrackingEnabled: result.httpsTrackingEnabled,
|
|
21836
|
+
httpsTrackingPending: result.httpsTrackingPending,
|
|
21511
21837
|
cloudFrontDomain: result.cloudFrontDomain,
|
|
21512
21838
|
acmCertificateValidationRecords: result.acmCertificateValidationRecords,
|
|
21513
21839
|
archiveArn: result.archiveArn,
|
|
@@ -21549,6 +21875,7 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21549
21875
|
dkimTokens: pulumiOutputs.dkimTokens?.value,
|
|
21550
21876
|
customTrackingDomain: pulumiOutputs.customTrackingDomain?.value,
|
|
21551
21877
|
httpsTrackingEnabled: pulumiOutputs.httpsTrackingEnabled?.value,
|
|
21878
|
+
httpsTrackingPending: pulumiOutputs.httpsTrackingPending?.value,
|
|
21552
21879
|
cloudFrontDomain: pulumiOutputs.cloudFrontDomain?.value,
|
|
21553
21880
|
acmCertificateValidationRecords: pulumiOutputs.acmCertificateValidationRecords?.value,
|
|
21554
21881
|
archiveArn: pulumiOutputs.archiveArn?.value,
|
|
@@ -21703,14 +22030,92 @@ ${pc28.bold("Add these DNS records to your DNS provider:")}
|
|
|
21703
22030
|
if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {
|
|
21704
22031
|
acmValidationRecords.push(...outputs.acmCertificateValidationRecords);
|
|
21705
22032
|
}
|
|
21706
|
-
|
|
22033
|
+
let acmDnsAutoCreated = false;
|
|
22034
|
+
if (outputs.httpsTrackingPending && acmValidationRecords.length > 0 && outputs.customTrackingDomain) {
|
|
22035
|
+
const trackingDnsProvider = metadata.services.email?.dnsProvider;
|
|
22036
|
+
if (trackingDnsProvider && trackingDnsProvider !== "manual") {
|
|
22037
|
+
const trackingDomainParts = outputs.customTrackingDomain.split(".");
|
|
22038
|
+
const parentDomain = trackingDomainParts.length > 2 ? trackingDomainParts.slice(-2).join(".") : outputs.customTrackingDomain;
|
|
22039
|
+
const credResult = await progress.execute(
|
|
22040
|
+
`Validating ${getDNSProviderDisplayName(trackingDnsProvider)} credentials for ACM validation`,
|
|
22041
|
+
async () => await getDNSCredentials(trackingDnsProvider, parentDomain, region)
|
|
22042
|
+
);
|
|
22043
|
+
if (credResult.valid && credResult.credentials) {
|
|
22044
|
+
try {
|
|
22045
|
+
progress.start(
|
|
22046
|
+
`Creating ACM validation DNS record in ${getDNSProviderDisplayName(trackingDnsProvider)}`
|
|
22047
|
+
);
|
|
22048
|
+
if (credResult.credentials.provider === "vercel") {
|
|
22049
|
+
const { VercelDNSClient: VercelDNSClient2 } = await Promise.resolve().then(() => (init_vercel(), vercel_exports));
|
|
22050
|
+
const client = new VercelDNSClient2(
|
|
22051
|
+
parentDomain,
|
|
22052
|
+
credResult.credentials.token,
|
|
22053
|
+
credResult.credentials.teamId
|
|
22054
|
+
);
|
|
22055
|
+
const result = await client.createRecords(
|
|
22056
|
+
acmValidationRecords.map((r) => ({
|
|
22057
|
+
name: r.name,
|
|
22058
|
+
type: r.type,
|
|
22059
|
+
value: r.value
|
|
22060
|
+
}))
|
|
22061
|
+
);
|
|
22062
|
+
if (result.success) {
|
|
22063
|
+
progress.succeed(
|
|
22064
|
+
`Created ACM validation DNS record in ${getDNSProviderDisplayName(trackingDnsProvider)}`
|
|
22065
|
+
);
|
|
22066
|
+
acmDnsAutoCreated = true;
|
|
22067
|
+
progress.info(
|
|
22068
|
+
"Certificate validation usually takes 5-30 minutes. Run this command again after validation completes."
|
|
22069
|
+
);
|
|
22070
|
+
} else {
|
|
22071
|
+
progress.fail(
|
|
22072
|
+
`Failed to create ACM validation record: ${result.errors?.join(", ")}`
|
|
22073
|
+
);
|
|
22074
|
+
}
|
|
22075
|
+
} else if (credResult.credentials.provider === "cloudflare") {
|
|
22076
|
+
const { CloudflareDNSClient: CloudflareDNSClient2 } = await Promise.resolve().then(() => (init_cloudflare(), cloudflare_exports));
|
|
22077
|
+
const client = new CloudflareDNSClient2(
|
|
22078
|
+
credResult.credentials.zoneId,
|
|
22079
|
+
credResult.credentials.token
|
|
22080
|
+
);
|
|
22081
|
+
const result = await client.createRecords(
|
|
22082
|
+
acmValidationRecords.map((r) => ({
|
|
22083
|
+
name: r.name,
|
|
22084
|
+
type: r.type,
|
|
22085
|
+
value: r.value
|
|
22086
|
+
}))
|
|
22087
|
+
);
|
|
22088
|
+
if (result.success) {
|
|
22089
|
+
progress.succeed(
|
|
22090
|
+
`Created ACM validation DNS record in ${getDNSProviderDisplayName(trackingDnsProvider)}`
|
|
22091
|
+
);
|
|
22092
|
+
acmDnsAutoCreated = true;
|
|
22093
|
+
progress.info(
|
|
22094
|
+
"Certificate validation usually takes 5-30 minutes. Run this command again after validation completes."
|
|
22095
|
+
);
|
|
22096
|
+
} else {
|
|
22097
|
+
progress.fail(
|
|
22098
|
+
`Failed to create ACM validation record: ${result.errors?.join(", ")}`
|
|
22099
|
+
);
|
|
22100
|
+
}
|
|
22101
|
+
}
|
|
22102
|
+
} catch (error) {
|
|
22103
|
+
progress.fail(
|
|
22104
|
+
`Failed to create ACM validation record: ${error.message}`
|
|
22105
|
+
);
|
|
22106
|
+
}
|
|
22107
|
+
}
|
|
22108
|
+
}
|
|
22109
|
+
}
|
|
22110
|
+
const needsCertificateValidation = outputs.httpsTrackingPending || outputs.httpsTrackingEnabled && acmValidationRecords.length > 0 && !outputs.cloudFrontDomain;
|
|
21707
22111
|
displaySuccess({
|
|
21708
22112
|
roleArn: outputs.roleArn,
|
|
21709
22113
|
configSetName: outputs.configSetName,
|
|
21710
22114
|
region: outputs.region,
|
|
21711
22115
|
tableName: outputs.tableName,
|
|
21712
22116
|
trackingDomainDnsRecords: trackingDomainDnsRecords.length > 0 ? trackingDomainDnsRecords : void 0,
|
|
21713
|
-
|
|
22117
|
+
// Only show ACM validation records if they weren't auto-created
|
|
22118
|
+
acmValidationRecords: acmValidationRecords.length > 0 && !acmDnsAutoCreated ? acmValidationRecords : void 0,
|
|
21714
22119
|
customTrackingDomain: outputs.customTrackingDomain,
|
|
21715
22120
|
httpsTrackingEnabled: outputs.httpsTrackingEnabled
|
|
21716
22121
|
});
|
|
@@ -21730,16 +22135,27 @@ ${pc28.green("\u2713")} ${pc28.bold("Upgrade complete!")}
|
|
|
21730
22135
|
}
|
|
21731
22136
|
if (needsCertificateValidation) {
|
|
21732
22137
|
console.log(pc28.bold("\u26A0\uFE0F HTTPS Tracking - Next Steps:\n"));
|
|
21733
|
-
|
|
21734
|
-
|
|
21735
|
-
|
|
21736
|
-
|
|
21737
|
-
" 2. Wait for
|
|
21738
|
-
|
|
21739
|
-
|
|
21740
|
-
` 3. Run ${pc28.cyan("wraps email upgrade")} again to complete CloudFront setup
|
|
22138
|
+
if (acmDnsAutoCreated) {
|
|
22139
|
+
console.log(
|
|
22140
|
+
` 1. ${pc28.green("\u2713")} ACM validation DNS record created automatically`
|
|
22141
|
+
);
|
|
22142
|
+
console.log(" 2. Wait for certificate validation (5-30 minutes)");
|
|
22143
|
+
console.log(
|
|
22144
|
+
` 3. Run ${pc28.cyan("wraps email upgrade")} again to complete CloudFront setup
|
|
21741
22145
|
`
|
|
21742
|
-
|
|
22146
|
+
);
|
|
22147
|
+
} else {
|
|
22148
|
+
console.log(
|
|
22149
|
+
" 1. Add the SSL certificate validation DNS record shown above to your DNS provider"
|
|
22150
|
+
);
|
|
22151
|
+
console.log(
|
|
22152
|
+
" 2. Wait for DNS propagation and certificate validation (5-30 minutes)"
|
|
22153
|
+
);
|
|
22154
|
+
console.log(
|
|
22155
|
+
` 3. Run ${pc28.cyan("wraps email upgrade")} again to complete CloudFront setup
|
|
22156
|
+
`
|
|
22157
|
+
);
|
|
22158
|
+
}
|
|
21743
22159
|
console.log(
|
|
21744
22160
|
pc28.dim(
|
|
21745
22161
|
" Note: CloudFront distribution will be created once the certificate is validated.\n"
|
|
@@ -24496,6 +24912,7 @@ async function platform() {
|
|
|
24496
24912
|
|
|
24497
24913
|
// src/commands/platform/update-role.ts
|
|
24498
24914
|
init_esm_shims();
|
|
24915
|
+
init_events();
|
|
24499
24916
|
init_aws();
|
|
24500
24917
|
init_metadata();
|
|
24501
24918
|
import {
|
|
@@ -24506,6 +24923,7 @@ import {
|
|
|
24506
24923
|
import { confirm as confirm13, intro as intro32, isCancel as isCancel19, log as log31, outro as outro19 } from "@clack/prompts";
|
|
24507
24924
|
import pc35 from "picocolors";
|
|
24508
24925
|
async function updateRole(options) {
|
|
24926
|
+
const startTime = Date.now();
|
|
24509
24927
|
intro32(pc35.bold("Update Platform Access Role"));
|
|
24510
24928
|
const progress = new DeploymentProgress();
|
|
24511
24929
|
const identity = await progress.execute(
|
|
@@ -24633,6 +25051,11 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
24633
25051
|
}
|
|
24634
25052
|
progress.stop();
|
|
24635
25053
|
const actionVerb = roleExists4 ? "updated" : "created";
|
|
25054
|
+
trackCommand("platform:update-role", {
|
|
25055
|
+
success: true,
|
|
25056
|
+
duration_ms: Date.now() - startTime,
|
|
25057
|
+
action: actionVerb
|
|
25058
|
+
});
|
|
24636
25059
|
outro19(pc35.green(`\u2713 Platform access role ${actionVerb} successfully`));
|
|
24637
25060
|
console.log(`
|
|
24638
25061
|
${pc35.bold("Permissions:")}`);
|