@wraps.dev/cli 0.3.2 → 0.3.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 +458 -108
- package/dist/cli.js.map +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23,11 +23,152 @@ var mail_manager_exports = {};
|
|
|
23
23
|
__export(mail_manager_exports, {
|
|
24
24
|
createMailManagerArchive: () => createMailManagerArchive
|
|
25
25
|
});
|
|
26
|
+
import {
|
|
27
|
+
CreateArchiveCommand,
|
|
28
|
+
GetArchiveCommand,
|
|
29
|
+
ListArchivesCommand,
|
|
30
|
+
MailManagerClient
|
|
31
|
+
} from "@aws-sdk/client-mailmanager";
|
|
32
|
+
import {
|
|
33
|
+
PutConfigurationSetArchivingOptionsCommand,
|
|
34
|
+
SESv2Client
|
|
35
|
+
} from "@aws-sdk/client-sesv2";
|
|
36
|
+
function retentionToAWSPeriod(retention) {
|
|
37
|
+
switch (retention) {
|
|
38
|
+
case "3months":
|
|
39
|
+
return "THREE_MONTHS";
|
|
40
|
+
case "6months":
|
|
41
|
+
return "SIX_MONTHS";
|
|
42
|
+
case "9months":
|
|
43
|
+
return "NINE_MONTHS";
|
|
44
|
+
case "1year":
|
|
45
|
+
return "ONE_YEAR";
|
|
46
|
+
case "18months":
|
|
47
|
+
return "EIGHTEEN_MONTHS";
|
|
48
|
+
case "2years":
|
|
49
|
+
return "TWO_YEARS";
|
|
50
|
+
case "30months":
|
|
51
|
+
return "THIRTY_MONTHS";
|
|
52
|
+
case "3years":
|
|
53
|
+
return "THREE_YEARS";
|
|
54
|
+
case "4years":
|
|
55
|
+
return "FOUR_YEARS";
|
|
56
|
+
case "5years":
|
|
57
|
+
return "FIVE_YEARS";
|
|
58
|
+
case "6years":
|
|
59
|
+
return "SIX_YEARS";
|
|
60
|
+
case "7years":
|
|
61
|
+
return "SEVEN_YEARS";
|
|
62
|
+
case "8years":
|
|
63
|
+
return "EIGHT_YEARS";
|
|
64
|
+
case "9years":
|
|
65
|
+
return "NINE_YEARS";
|
|
66
|
+
case "10years":
|
|
67
|
+
return "TEN_YEARS";
|
|
68
|
+
case "permanent":
|
|
69
|
+
return "PERMANENT";
|
|
70
|
+
default:
|
|
71
|
+
return "THREE_MONTHS";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
26
74
|
async function createMailManagerArchive(config) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
);
|
|
75
|
+
const region = config.region || process.env.AWS_REGION || "us-east-1";
|
|
76
|
+
const archiveName = `wraps-${config.name}-archive`;
|
|
77
|
+
const mailManagerClient = new MailManagerClient({ region });
|
|
78
|
+
const sesClient = new SESv2Client({ region });
|
|
79
|
+
const kmsKeyArn = config.kmsKeyArn;
|
|
80
|
+
if (!kmsKeyArn) {
|
|
81
|
+
}
|
|
82
|
+
const awsRetention = retentionToAWSPeriod(config.retention);
|
|
83
|
+
let archiveId;
|
|
84
|
+
let archiveArn;
|
|
85
|
+
try {
|
|
86
|
+
const listCommand = new ListArchivesCommand({});
|
|
87
|
+
const listResult = await mailManagerClient.send(listCommand);
|
|
88
|
+
const existingArchive = listResult.Archives?.find(
|
|
89
|
+
(archive) => archive.ArchiveName === archiveName
|
|
90
|
+
);
|
|
91
|
+
if (existingArchive?.ArchiveId) {
|
|
92
|
+
console.log(`Using existing Mail Manager archive: ${archiveName}`);
|
|
93
|
+
archiveId = existingArchive.ArchiveId;
|
|
94
|
+
const getCommand = new GetArchiveCommand({ ArchiveId: archiveId });
|
|
95
|
+
const getResult = await mailManagerClient.send(getCommand);
|
|
96
|
+
archiveArn = getResult.ArchiveArn;
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.log("Error checking for existing archive:", error);
|
|
100
|
+
}
|
|
101
|
+
if (!archiveId) {
|
|
102
|
+
try {
|
|
103
|
+
const createArchiveCommand = new CreateArchiveCommand({
|
|
104
|
+
ArchiveName: archiveName,
|
|
105
|
+
Retention: {
|
|
106
|
+
RetentionPeriod: awsRetention
|
|
107
|
+
},
|
|
108
|
+
...kmsKeyArn && { KmsKeyArn: kmsKeyArn },
|
|
109
|
+
Tags: [
|
|
110
|
+
{ Key: "ManagedBy", Value: "wraps-cli" },
|
|
111
|
+
{ Key: "Name", Value: archiveName },
|
|
112
|
+
{ Key: "Retention", Value: config.retention }
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
const archiveResult = await mailManagerClient.send(createArchiveCommand);
|
|
116
|
+
archiveId = archiveResult.ArchiveId;
|
|
117
|
+
if (!archiveId) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"Failed to create Mail Manager Archive: No ArchiveId returned"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
console.log(`Created new Mail Manager archive: ${archiveName}`);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (error instanceof Error && error.name === "ConflictException" && error.message.includes("Archive already exists")) {
|
|
125
|
+
console.log(
|
|
126
|
+
"Archive was created concurrently, fetching existing archive..."
|
|
127
|
+
);
|
|
128
|
+
const listCommand = new ListArchivesCommand({});
|
|
129
|
+
const listResult = await mailManagerClient.send(listCommand);
|
|
130
|
+
const existingArchive = listResult.Archives?.find(
|
|
131
|
+
(archive) => archive.ArchiveName === archiveName
|
|
132
|
+
);
|
|
133
|
+
if (!existingArchive?.ArchiveId) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Archive exists but couldn't find it: ${archiveName}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
archiveId = existingArchive.ArchiveId;
|
|
139
|
+
const getCommand = new GetArchiveCommand({ ArchiveId: archiveId });
|
|
140
|
+
const getResult = await mailManagerClient.send(getCommand);
|
|
141
|
+
archiveArn = getResult.ArchiveArn;
|
|
142
|
+
} else {
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!archiveArn) {
|
|
148
|
+
const identity = await import("@aws-sdk/client-sts").then(
|
|
149
|
+
(m) => new m.STSClient({ region }).send(new m.GetCallerIdentityCommand({}))
|
|
150
|
+
);
|
|
151
|
+
const accountId = identity.Account;
|
|
152
|
+
archiveArn = `arn:aws:ses:${region}:${accountId}:mailmanager-archive/${archiveId}`;
|
|
153
|
+
}
|
|
154
|
+
const configSetName = await new Promise((resolve) => {
|
|
155
|
+
config.configSetName.apply((name) => {
|
|
156
|
+
resolve(name);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
const putArchivingOptionsCommand = new PutConfigurationSetArchivingOptionsCommand({
|
|
160
|
+
ConfigurationSetName: configSetName,
|
|
161
|
+
ArchiveArn: archiveArn
|
|
162
|
+
});
|
|
163
|
+
await sesClient.send(putArchivingOptionsCommand);
|
|
164
|
+
if (!(archiveId && archiveArn)) {
|
|
165
|
+
throw new Error("Failed to get archive ID or ARN");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
archiveId,
|
|
169
|
+
archiveArn,
|
|
170
|
+
kmsKeyArn
|
|
171
|
+
};
|
|
31
172
|
}
|
|
32
173
|
var init_mail_manager = __esm({
|
|
33
174
|
"src/infrastructure/resources/mail-manager.ts"() {
|
|
@@ -239,13 +380,22 @@ var init_aws = __esm({
|
|
|
239
380
|
function estimateStorageSize(emailsPerMonth, retention, numEventTypes = 8) {
|
|
240
381
|
const avgRecordSizeKB = 2;
|
|
241
382
|
const retentionMonths = {
|
|
242
|
-
"
|
|
243
|
-
"30days": 1,
|
|
244
|
-
"90days": 3,
|
|
383
|
+
"3months": 3,
|
|
245
384
|
"6months": 6,
|
|
385
|
+
"9months": 9,
|
|
246
386
|
"1year": 12,
|
|
247
387
|
"18months": 18,
|
|
248
|
-
|
|
388
|
+
"2years": 24,
|
|
389
|
+
"30months": 30,
|
|
390
|
+
"3years": 36,
|
|
391
|
+
"4years": 48,
|
|
392
|
+
"5years": 60,
|
|
393
|
+
"6years": 72,
|
|
394
|
+
"7years": 84,
|
|
395
|
+
"8years": 96,
|
|
396
|
+
"9years": 108,
|
|
397
|
+
"10years": 120,
|
|
398
|
+
permanent: 120
|
|
249
399
|
// Assume 10 years for cost estimation
|
|
250
400
|
}[retention];
|
|
251
401
|
const totalKB = emailsPerMonth * numEventTypes * (retentionMonths ?? 12) * avgRecordSizeKB;
|
|
@@ -254,13 +404,22 @@ function estimateStorageSize(emailsPerMonth, retention, numEventTypes = 8) {
|
|
|
254
404
|
function estimateArchiveStorageSize(emailsPerMonth, retention) {
|
|
255
405
|
const avgEmailSizeKB = 50;
|
|
256
406
|
const retentionMonths = {
|
|
257
|
-
"
|
|
258
|
-
"30days": 1,
|
|
259
|
-
"90days": 3,
|
|
407
|
+
"3months": 3,
|
|
260
408
|
"6months": 6,
|
|
409
|
+
"9months": 9,
|
|
261
410
|
"1year": 12,
|
|
262
411
|
"18months": 18,
|
|
263
|
-
|
|
412
|
+
"2years": 24,
|
|
413
|
+
"30months": 30,
|
|
414
|
+
"3years": 36,
|
|
415
|
+
"4years": 48,
|
|
416
|
+
"5years": 60,
|
|
417
|
+
"6years": 72,
|
|
418
|
+
"7years": 84,
|
|
419
|
+
"8years": 96,
|
|
420
|
+
"9years": 108,
|
|
421
|
+
"10years": 120,
|
|
422
|
+
permanent: 120
|
|
264
423
|
// Assume 10 years for cost estimation
|
|
265
424
|
}[retention];
|
|
266
425
|
const totalKB = emailsPerMonth * (retentionMonths ?? 12) * avgEmailSizeKB;
|
|
@@ -300,7 +459,7 @@ function calculateDynamoDBCost(config, emailsPerMonth) {
|
|
|
300
459
|
if (!config.eventTracking?.dynamoDBHistory) {
|
|
301
460
|
return;
|
|
302
461
|
}
|
|
303
|
-
const retention = config.eventTracking.archiveRetention || "
|
|
462
|
+
const retention = config.eventTracking.archiveRetention || "3months";
|
|
304
463
|
const numEventTypes = config.eventTracking.events?.length || 8;
|
|
305
464
|
const totalEvents = emailsPerMonth * numEventTypes;
|
|
306
465
|
const writeCost = Math.max(0, totalEvents - FREE_TIER.DYNAMODB_WRITES) / 1e6 * AWS_PRICING.DYNAMODB_WRITE_PER_MILLION;
|
|
@@ -561,7 +720,7 @@ function getPresetInfo(preset) {
|
|
|
561
720
|
"Everything in Starter",
|
|
562
721
|
"Reputation metrics dashboard",
|
|
563
722
|
"Real-time event tracking (EventBridge)",
|
|
564
|
-
"
|
|
723
|
+
"3-month email history storage",
|
|
565
724
|
"Optional: Email archiving with rendered viewer",
|
|
566
725
|
"Complete event visibility"
|
|
567
726
|
]
|
|
@@ -630,9 +789,9 @@ function validateConfig(config) {
|
|
|
630
789
|
"\u{1F4A1} Event tracking is enabled but history storage is disabled. Events will only be available in real-time."
|
|
631
790
|
);
|
|
632
791
|
}
|
|
633
|
-
if (config.eventTracking?.archiveRetention === "
|
|
792
|
+
if (config.eventTracking?.archiveRetention === "permanent") {
|
|
634
793
|
warnings.push(
|
|
635
|
-
"\u26A0\uFE0F
|
|
794
|
+
"\u26A0\uFE0F Permanent retention can become expensive. Consider 3-month or 1-year retention."
|
|
636
795
|
);
|
|
637
796
|
}
|
|
638
797
|
return warnings;
|
|
@@ -661,7 +820,7 @@ var init_presets = __esm({
|
|
|
661
820
|
// Email archiving disabled by default
|
|
662
821
|
emailArchiving: {
|
|
663
822
|
enabled: false,
|
|
664
|
-
retention: "
|
|
823
|
+
retention: "3months"
|
|
665
824
|
},
|
|
666
825
|
sendingEnabled: true
|
|
667
826
|
};
|
|
@@ -691,13 +850,13 @@ var init_presets = __esm({
|
|
|
691
850
|
"RENDERING_FAILURE"
|
|
692
851
|
],
|
|
693
852
|
dynamoDBHistory: true,
|
|
694
|
-
archiveRetention: "
|
|
853
|
+
archiveRetention: "3months"
|
|
695
854
|
},
|
|
696
|
-
// Email archiving with
|
|
855
|
+
// Email archiving with 3-month retention
|
|
697
856
|
emailArchiving: {
|
|
698
857
|
enabled: false,
|
|
699
858
|
// User can opt-in
|
|
700
|
-
retention: "
|
|
859
|
+
retention: "3months"
|
|
701
860
|
},
|
|
702
861
|
sendingEnabled: true
|
|
703
862
|
};
|
|
@@ -1125,7 +1284,7 @@ async function promptEmailArchiving() {
|
|
|
1125
1284
|
process.exit(0);
|
|
1126
1285
|
}
|
|
1127
1286
|
if (!enabled) {
|
|
1128
|
-
return { enabled: false, retention: "
|
|
1287
|
+
return { enabled: false, retention: "3months" };
|
|
1129
1288
|
}
|
|
1130
1289
|
const retention = await clack3.select({
|
|
1131
1290
|
message: "Email archive retention period:",
|
|
@@ -1133,7 +1292,7 @@ async function promptEmailArchiving() {
|
|
|
1133
1292
|
{ value: "7days", label: "7 days", hint: "~$1-2/mo for 10k emails" },
|
|
1134
1293
|
{ value: "30days", label: "30 days", hint: "~$2-4/mo for 10k emails" },
|
|
1135
1294
|
{
|
|
1136
|
-
value: "
|
|
1295
|
+
value: "3months",
|
|
1137
1296
|
label: "90 days (recommended)",
|
|
1138
1297
|
hint: "~$5-10/mo for 10k emails"
|
|
1139
1298
|
},
|
|
@@ -1149,7 +1308,7 @@ async function promptEmailArchiving() {
|
|
|
1149
1308
|
hint: "~$35-60/mo for 10k emails"
|
|
1150
1309
|
}
|
|
1151
1310
|
],
|
|
1152
|
-
initialValue: "
|
|
1311
|
+
initialValue: "3months"
|
|
1153
1312
|
});
|
|
1154
1313
|
if (clack3.isCancel(retention)) {
|
|
1155
1314
|
clack3.cancel("Operation cancelled.");
|
|
@@ -1188,7 +1347,7 @@ async function promptCustomConfig() {
|
|
|
1188
1347
|
process.exit(0);
|
|
1189
1348
|
}
|
|
1190
1349
|
let dynamoDBHistory = false;
|
|
1191
|
-
let archiveRetention = "
|
|
1350
|
+
let archiveRetention = "3months";
|
|
1192
1351
|
if (eventTrackingEnabled) {
|
|
1193
1352
|
dynamoDBHistory = await clack3.confirm({
|
|
1194
1353
|
message: "Store email history in DynamoDB?",
|
|
@@ -1205,7 +1364,7 @@ async function promptCustomConfig() {
|
|
|
1205
1364
|
{ value: "7days", label: "7 days", hint: "Minimal storage cost" },
|
|
1206
1365
|
{ value: "30days", label: "30 days", hint: "Development/testing" },
|
|
1207
1366
|
{
|
|
1208
|
-
value: "
|
|
1367
|
+
value: "3months",
|
|
1209
1368
|
label: "90 days (recommended)",
|
|
1210
1369
|
hint: "Standard retention"
|
|
1211
1370
|
},
|
|
@@ -1255,7 +1414,7 @@ async function promptCustomConfig() {
|
|
|
1255
1414
|
clack3.cancel("Operation cancelled.");
|
|
1256
1415
|
process.exit(0);
|
|
1257
1416
|
}
|
|
1258
|
-
let emailArchiveRetention = "
|
|
1417
|
+
let emailArchiveRetention = "3months";
|
|
1259
1418
|
if (emailArchivingEnabled) {
|
|
1260
1419
|
emailArchiveRetention = await clack3.select({
|
|
1261
1420
|
message: "Email archive retention period:",
|
|
@@ -1263,7 +1422,7 @@ async function promptCustomConfig() {
|
|
|
1263
1422
|
{ value: "7days", label: "7 days", hint: "~$1-2/mo for 10k emails" },
|
|
1264
1423
|
{ value: "30days", label: "30 days", hint: "~$2-4/mo for 10k emails" },
|
|
1265
1424
|
{
|
|
1266
|
-
value: "
|
|
1425
|
+
value: "3months",
|
|
1267
1426
|
label: "90 days (recommended)",
|
|
1268
1427
|
hint: "~$5-10/mo for 10k emails"
|
|
1269
1428
|
},
|
|
@@ -1279,7 +1438,7 @@ async function promptCustomConfig() {
|
|
|
1279
1438
|
hint: "~$35-60/mo for 10k emails"
|
|
1280
1439
|
}
|
|
1281
1440
|
],
|
|
1282
|
-
initialValue: "
|
|
1441
|
+
initialValue: "3months"
|
|
1283
1442
|
});
|
|
1284
1443
|
if (clack3.isCancel(emailArchiveRetention)) {
|
|
1285
1444
|
clack3.cancel("Operation cancelled.");
|
|
@@ -1320,12 +1479,12 @@ async function promptCustomConfig() {
|
|
|
1320
1479
|
"RENDERING_FAILURE"
|
|
1321
1480
|
],
|
|
1322
1481
|
dynamoDBHistory: Boolean(dynamoDBHistory),
|
|
1323
|
-
archiveRetention: typeof archiveRetention === "string" ? archiveRetention : "
|
|
1482
|
+
archiveRetention: typeof archiveRetention === "string" ? archiveRetention : "3months"
|
|
1324
1483
|
} : { enabled: false },
|
|
1325
1484
|
emailArchiving: emailArchivingEnabled ? {
|
|
1326
1485
|
enabled: true,
|
|
1327
|
-
retention: typeof emailArchiveRetention === "string" ? emailArchiveRetention : "
|
|
1328
|
-
} : { enabled: false, retention: "
|
|
1486
|
+
retention: typeof emailArchiveRetention === "string" ? emailArchiveRetention : "3months"
|
|
1487
|
+
} : { enabled: false, retention: "3months" },
|
|
1329
1488
|
dedicatedIp,
|
|
1330
1489
|
sendingEnabled: true
|
|
1331
1490
|
};
|
|
@@ -1373,13 +1532,113 @@ var init_assume_role = __esm({
|
|
|
1373
1532
|
// src/utils/archive.ts
|
|
1374
1533
|
import {
|
|
1375
1534
|
GetArchiveMessageCommand,
|
|
1376
|
-
|
|
1535
|
+
GetArchiveSearchResultsCommand,
|
|
1536
|
+
MailManagerClient as MailManagerClient2,
|
|
1537
|
+
StartArchiveSearchCommand
|
|
1377
1538
|
} from "@aws-sdk/client-mailmanager";
|
|
1378
1539
|
import { simpleParser } from "mailparser";
|
|
1379
|
-
|
|
1380
|
-
|
|
1540
|
+
function extractArchiveId(archiveArnOrId) {
|
|
1541
|
+
if (archiveArnOrId.startsWith("arn:")) {
|
|
1542
|
+
const parts = archiveArnOrId.split("/");
|
|
1543
|
+
return parts.at(-1);
|
|
1544
|
+
}
|
|
1545
|
+
return archiveArnOrId;
|
|
1546
|
+
}
|
|
1547
|
+
async function getArchivedEmail(archiveArnOrId, searchCriteria, region) {
|
|
1548
|
+
const client = new MailManagerClient2({ region });
|
|
1549
|
+
const archiveId = extractArchiveId(archiveArnOrId);
|
|
1550
|
+
const searchTime = searchCriteria.timestamp || /* @__PURE__ */ new Date();
|
|
1551
|
+
const dayBefore = new Date(searchTime.getTime() - 24 * 60 * 60 * 1e3);
|
|
1552
|
+
const dayAfter = new Date(searchTime.getTime() + 24 * 60 * 60 * 1e3);
|
|
1553
|
+
const filters = [];
|
|
1554
|
+
if (searchCriteria.from) {
|
|
1555
|
+
filters.push({
|
|
1556
|
+
StringExpression: {
|
|
1557
|
+
Evaluate: {
|
|
1558
|
+
Attribute: "FROM"
|
|
1559
|
+
},
|
|
1560
|
+
Operator: "CONTAINS",
|
|
1561
|
+
Values: [searchCriteria.from]
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
if (searchCriteria.to) {
|
|
1566
|
+
filters.push({
|
|
1567
|
+
StringExpression: {
|
|
1568
|
+
Evaluate: {
|
|
1569
|
+
Attribute: "TO"
|
|
1570
|
+
},
|
|
1571
|
+
Operator: "CONTAINS",
|
|
1572
|
+
Values: [searchCriteria.to]
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
if (searchCriteria.subject) {
|
|
1577
|
+
filters.push({
|
|
1578
|
+
StringExpression: {
|
|
1579
|
+
Evaluate: {
|
|
1580
|
+
Attribute: "SUBJECT"
|
|
1581
|
+
},
|
|
1582
|
+
Operator: "CONTAINS",
|
|
1583
|
+
Values: [searchCriteria.subject]
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
if (filters.length === 0) {
|
|
1588
|
+
throw new Error(
|
|
1589
|
+
"At least one search criterion (from, to, or subject) is required"
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
const searchCommand = new StartArchiveSearchCommand({
|
|
1593
|
+
ArchiveId: archiveId,
|
|
1594
|
+
FromTimestamp: dayBefore,
|
|
1595
|
+
ToTimestamp: dayAfter,
|
|
1596
|
+
Filters: {
|
|
1597
|
+
Include: filters
|
|
1598
|
+
},
|
|
1599
|
+
MaxResults: 10
|
|
1600
|
+
// Get a few results in case there are multiple matches
|
|
1601
|
+
});
|
|
1602
|
+
const searchResponse = await client.send(searchCommand);
|
|
1603
|
+
const searchId = searchResponse.SearchId;
|
|
1604
|
+
if (!searchId) {
|
|
1605
|
+
throw new Error("Failed to start archive search");
|
|
1606
|
+
}
|
|
1607
|
+
let archivedMessageId;
|
|
1608
|
+
let attempts = 0;
|
|
1609
|
+
const maxAttempts = 20;
|
|
1610
|
+
const pollInterval = 1e3;
|
|
1611
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1612
|
+
while (attempts < maxAttempts) {
|
|
1613
|
+
try {
|
|
1614
|
+
const resultsCommand = new GetArchiveSearchResultsCommand({
|
|
1615
|
+
SearchId: searchId
|
|
1616
|
+
});
|
|
1617
|
+
const resultsResponse = await client.send(resultsCommand);
|
|
1618
|
+
if (resultsResponse.Rows && resultsResponse.Rows.length > 0) {
|
|
1619
|
+
archivedMessageId = resultsResponse.Rows[0].ArchivedMessageId;
|
|
1620
|
+
break;
|
|
1621
|
+
}
|
|
1622
|
+
if (resultsResponse.Rows && resultsResponse.Rows.length === 0) {
|
|
1623
|
+
break;
|
|
1624
|
+
}
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
if (error instanceof Error && error.name === "ConflictException" && error.message.includes("still in progress")) {
|
|
1627
|
+
console.log(`Search still in progress, attempt ${attempts + 1}...`);
|
|
1628
|
+
} else {
|
|
1629
|
+
throw error;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1633
|
+
attempts++;
|
|
1634
|
+
}
|
|
1635
|
+
if (!archivedMessageId) {
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
"Email not found in archive with the provided search criteria. It may have been sent before archiving was enabled."
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1381
1640
|
const command2 = new GetArchiveMessageCommand({
|
|
1382
|
-
ArchivedMessageId:
|
|
1641
|
+
ArchivedMessageId: archivedMessageId
|
|
1383
1642
|
});
|
|
1384
1643
|
const response = await client.send(command2);
|
|
1385
1644
|
if (!response.MessageDownloadLink) {
|
|
@@ -1418,8 +1677,7 @@ async function getArchivedEmail(_archiveId, messageId, region) {
|
|
|
1418
1677
|
return addr.text || "";
|
|
1419
1678
|
};
|
|
1420
1679
|
return {
|
|
1421
|
-
messageId,
|
|
1422
|
-
// Use the input messageId since response may not have MessageMetadata
|
|
1680
|
+
messageId: parsed.messageId || headers["message-id"]?.toString() || "",
|
|
1423
1681
|
from: getAddressText(parsed.from),
|
|
1424
1682
|
to: getAddressText(parsed.to),
|
|
1425
1683
|
subject: parsed.subject || "",
|
|
@@ -1446,14 +1704,20 @@ __export(email_archive_exports, {
|
|
|
1446
1704
|
fetchArchivedEmail: () => fetchArchivedEmail
|
|
1447
1705
|
});
|
|
1448
1706
|
async function fetchArchivedEmail(messageId, options) {
|
|
1449
|
-
const { region, archiveArn } = options;
|
|
1707
|
+
const { region, archiveArn, from, to, subject, timestamp } = options;
|
|
1450
1708
|
try {
|
|
1451
1709
|
console.log("Fetching archived email:", {
|
|
1452
1710
|
messageId,
|
|
1453
1711
|
archiveArn,
|
|
1454
1712
|
region
|
|
1455
1713
|
});
|
|
1456
|
-
const
|
|
1714
|
+
const searchCriteria = {
|
|
1715
|
+
from,
|
|
1716
|
+
to,
|
|
1717
|
+
subject,
|
|
1718
|
+
timestamp
|
|
1719
|
+
};
|
|
1720
|
+
const email = await getArchivedEmail(archiveArn, searchCriteria, region);
|
|
1457
1721
|
console.log("Archived email fetched successfully:", {
|
|
1458
1722
|
messageId: email.messageId,
|
|
1459
1723
|
hasHtml: !!email.html,
|
|
@@ -1880,11 +2144,18 @@ async function createIAMRole(config) {
|
|
|
1880
2144
|
statements.push({
|
|
1881
2145
|
Effect: "Allow",
|
|
1882
2146
|
Action: [
|
|
1883
|
-
|
|
2147
|
+
// Archive search operations
|
|
2148
|
+
"ses:StartArchiveSearch",
|
|
2149
|
+
"ses:GetArchiveSearchResults",
|
|
2150
|
+
// Archive message retrieval
|
|
1884
2151
|
"ses:GetArchiveMessage",
|
|
1885
2152
|
"ses:GetArchiveMessageContent",
|
|
1886
|
-
|
|
1887
|
-
"ses:
|
|
2153
|
+
// Archive metadata
|
|
2154
|
+
"ses:GetArchive",
|
|
2155
|
+
"ses:ListArchives",
|
|
2156
|
+
// Archive export (for future use)
|
|
2157
|
+
"ses:StartArchiveExport",
|
|
2158
|
+
"ses:GetArchiveExport"
|
|
1888
2159
|
],
|
|
1889
2160
|
Resource: "arn:aws:ses:*:*:mailmanager-archive/*"
|
|
1890
2161
|
});
|
|
@@ -2271,7 +2542,8 @@ async function deployEmailStack(config) {
|
|
|
2271
2542
|
archiveResources = await createMailManagerArchive2({
|
|
2272
2543
|
name: "email",
|
|
2273
2544
|
retention: emailConfig.emailArchiving.retention,
|
|
2274
|
-
configSetName: sesResources.configSet.configurationSetName
|
|
2545
|
+
configSetName: sesResources.configSet.configurationSetName,
|
|
2546
|
+
region: config.region
|
|
2275
2547
|
});
|
|
2276
2548
|
}
|
|
2277
2549
|
return {
|
|
@@ -2288,7 +2560,8 @@ async function deployEmailStack(config) {
|
|
|
2288
2560
|
dlqUrl: sqsResources?.dlq.url,
|
|
2289
2561
|
customTrackingDomain: sesResources?.customTrackingDomain,
|
|
2290
2562
|
mailFromDomain: sesResources?.mailFromDomain,
|
|
2291
|
-
|
|
2563
|
+
archiveId: archiveResources?.archiveId,
|
|
2564
|
+
archiveArn: archiveResources?.archiveArn,
|
|
2292
2565
|
archivingEnabled: emailConfig.emailArchiving?.enabled,
|
|
2293
2566
|
archiveRetention: emailConfig.emailArchiving?.enabled ? emailConfig.emailArchiving.retention : void 0
|
|
2294
2567
|
};
|
|
@@ -2606,11 +2879,11 @@ ${domainStrings.join("\n")}`);
|
|
|
2606
2879
|
const retentionLabel = {
|
|
2607
2880
|
"7days": "7 days",
|
|
2608
2881
|
"30days": "30 days",
|
|
2609
|
-
"
|
|
2882
|
+
"3months": "90 days",
|
|
2610
2883
|
"6months": "6 months",
|
|
2611
2884
|
"1year": "1 year",
|
|
2612
2885
|
"18months": "18 months"
|
|
2613
|
-
}[status2.resources.archiveRetention || "
|
|
2886
|
+
}[status2.resources.archiveRetention || "3months"] || "90 days";
|
|
2614
2887
|
featureLines.push(
|
|
2615
2888
|
` ${pc2.green("\u2713")} Email Archiving ${pc2.dim(`(${retentionLabel} retention)`)}`
|
|
2616
2889
|
);
|
|
@@ -3199,7 +3472,7 @@ import { Router as createRouter } from "express";
|
|
|
3199
3472
|
init_esm_shims();
|
|
3200
3473
|
init_assume_role();
|
|
3201
3474
|
import { GetSendQuotaCommand, SESClient as SESClient3 } from "@aws-sdk/client-ses";
|
|
3202
|
-
import { GetEmailIdentityCommand, SESv2Client } from "@aws-sdk/client-sesv2";
|
|
3475
|
+
import { GetEmailIdentityCommand, SESv2Client as SESv2Client2 } from "@aws-sdk/client-sesv2";
|
|
3203
3476
|
async function fetchSendQuota(roleArn, region) {
|
|
3204
3477
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3205
3478
|
const ses = new SESClient3({ region, credentials });
|
|
@@ -3212,7 +3485,7 @@ async function fetchSendQuota(roleArn, region) {
|
|
|
3212
3485
|
}
|
|
3213
3486
|
async function fetchDomainInfo(roleArn, region, domain) {
|
|
3214
3487
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3215
|
-
const sesv22 = new
|
|
3488
|
+
const sesv22 = new SESv2Client2({ region, credentials });
|
|
3216
3489
|
const response = await sesv22.send(
|
|
3217
3490
|
new GetEmailIdentityCommand({
|
|
3218
3491
|
EmailIdentity: domain
|
|
@@ -3631,11 +3904,33 @@ function createEmailsRouter(config) {
|
|
|
3631
3904
|
error: "Archive ARN not configured."
|
|
3632
3905
|
});
|
|
3633
3906
|
}
|
|
3907
|
+
if (!config.tableName) {
|
|
3908
|
+
console.log("No table name configured");
|
|
3909
|
+
return res.status(400).json({
|
|
3910
|
+
error: "Email tracking not enabled. Need email metadata to search archive."
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3913
|
+
console.log("Fetching email metadata from DynamoDB...");
|
|
3914
|
+
const emailDetails = await fetchEmailById(id, {
|
|
3915
|
+
region: config.region,
|
|
3916
|
+
tableName: config.tableName
|
|
3917
|
+
});
|
|
3918
|
+
if (!emailDetails) {
|
|
3919
|
+
console.log("Email metadata not found in DynamoDB for ID:", id);
|
|
3920
|
+
return res.status(404).json({
|
|
3921
|
+
error: "Email metadata not found. Cannot search archive."
|
|
3922
|
+
});
|
|
3923
|
+
}
|
|
3634
3924
|
console.log("Fetching archived email from Mail Manager...");
|
|
3635
3925
|
const { fetchArchivedEmail: fetchArchivedEmail2 } = await Promise.resolve().then(() => (init_email_archive(), email_archive_exports));
|
|
3636
3926
|
const archivedEmail = await fetchArchivedEmail2(id, {
|
|
3637
3927
|
region: config.region,
|
|
3638
|
-
archiveArn: config.archiveArn
|
|
3928
|
+
archiveArn: config.archiveArn,
|
|
3929
|
+
from: emailDetails.from,
|
|
3930
|
+
to: emailDetails.to[0],
|
|
3931
|
+
// Use first recipient for search
|
|
3932
|
+
subject: emailDetails.subject,
|
|
3933
|
+
timestamp: new Date(emailDetails.sentAt)
|
|
3639
3934
|
});
|
|
3640
3935
|
if (!archivedEmail) {
|
|
3641
3936
|
console.log("Archived email not found for message ID:", id);
|
|
@@ -3882,11 +4177,11 @@ init_assume_role();
|
|
|
3882
4177
|
import {
|
|
3883
4178
|
GetConfigurationSetCommand,
|
|
3884
4179
|
GetEmailIdentityCommand as GetEmailIdentityCommand2,
|
|
3885
|
-
SESv2Client as
|
|
4180
|
+
SESv2Client as SESv2Client3
|
|
3886
4181
|
} from "@aws-sdk/client-sesv2";
|
|
3887
4182
|
async function fetchConfigurationSet(roleArn, region, configSetName) {
|
|
3888
4183
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3889
|
-
const sesv22 = new
|
|
4184
|
+
const sesv22 = new SESv2Client3({ region, credentials });
|
|
3890
4185
|
const response = await sesv22.send(
|
|
3891
4186
|
new GetConfigurationSetCommand({
|
|
3892
4187
|
ConfigurationSetName: configSetName
|
|
@@ -3916,7 +4211,7 @@ async function fetchConfigurationSet(roleArn, region, configSetName) {
|
|
|
3916
4211
|
}
|
|
3917
4212
|
async function fetchEmailIdentity(roleArn, region, identityName) {
|
|
3918
4213
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3919
|
-
const sesv22 = new
|
|
4214
|
+
const sesv22 = new SESv2Client3({ region, credentials });
|
|
3920
4215
|
const response = await sesv22.send(
|
|
3921
4216
|
new GetEmailIdentityCommand2({
|
|
3922
4217
|
EmailIdentity: identityName
|
|
@@ -4108,10 +4403,10 @@ function createSettingsRouter(config) {
|
|
|
4108
4403
|
console.log(
|
|
4109
4404
|
`[Settings] Updating sending options for ${configSetName}: ${enabled}`
|
|
4110
4405
|
);
|
|
4111
|
-
const { SESv2Client:
|
|
4406
|
+
const { SESv2Client: SESv2Client5, PutConfigurationSetSendingOptionsCommand } = await import("@aws-sdk/client-sesv2");
|
|
4112
4407
|
const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
|
|
4113
4408
|
const credentials = config.roleArn ? await assumeRole2(config.roleArn, config.region) : void 0;
|
|
4114
|
-
const sesClient = new
|
|
4409
|
+
const sesClient = new SESv2Client5({ region: config.region, credentials });
|
|
4115
4410
|
await sesClient.send(
|
|
4116
4411
|
new PutConfigurationSetSendingOptionsCommand({
|
|
4117
4412
|
ConfigurationSetName: configSetName,
|
|
@@ -4145,10 +4440,10 @@ function createSettingsRouter(config) {
|
|
|
4145
4440
|
console.log(
|
|
4146
4441
|
`[Settings] Updating reputation options for ${configSetName}: ${enabled}`
|
|
4147
4442
|
);
|
|
4148
|
-
const { SESv2Client:
|
|
4443
|
+
const { SESv2Client: SESv2Client5, PutConfigurationSetReputationOptionsCommand } = await import("@aws-sdk/client-sesv2");
|
|
4149
4444
|
const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
|
|
4150
4445
|
const credentials = config.roleArn ? await assumeRole2(config.roleArn, config.region) : void 0;
|
|
4151
|
-
const sesClient = new
|
|
4446
|
+
const sesClient = new SESv2Client5({ region: config.region, credentials });
|
|
4152
4447
|
await sesClient.send(
|
|
4153
4448
|
new PutConfigurationSetReputationOptionsCommand({
|
|
4154
4449
|
ConfigurationSetName: configSetName,
|
|
@@ -4188,10 +4483,10 @@ function createSettingsRouter(config) {
|
|
|
4188
4483
|
console.log(
|
|
4189
4484
|
`[Settings] Updating tracking domain for ${configSetName}: ${domain}`
|
|
4190
4485
|
);
|
|
4191
|
-
const { SESv2Client:
|
|
4486
|
+
const { SESv2Client: SESv2Client5, PutConfigurationSetTrackingOptionsCommand } = await import("@aws-sdk/client-sesv2");
|
|
4192
4487
|
const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
|
|
4193
4488
|
const credentials = config.roleArn ? await assumeRole2(config.roleArn, config.region) : void 0;
|
|
4194
|
-
const sesClient = new
|
|
4489
|
+
const sesClient = new SESv2Client5({
|
|
4195
4490
|
region: config.region,
|
|
4196
4491
|
credentials
|
|
4197
4492
|
});
|
|
@@ -4819,8 +5114,8 @@ Run ${pc9.cyan("wraps init")} to deploy infrastructure.
|
|
|
4819
5114
|
process.exit(1);
|
|
4820
5115
|
}
|
|
4821
5116
|
const domains = await listSESDomains(region);
|
|
4822
|
-
const { SESv2Client:
|
|
4823
|
-
const sesv2Client = new
|
|
5117
|
+
const { SESv2Client: SESv2Client5, GetEmailIdentityCommand: GetEmailIdentityCommand4 } = await import("@aws-sdk/client-sesv2");
|
|
5118
|
+
const sesv2Client = new SESv2Client5({ region });
|
|
4824
5119
|
const domainsWithTokens = await Promise.all(
|
|
4825
5120
|
domains.map(async (d) => {
|
|
4826
5121
|
try {
|
|
@@ -4981,7 +5276,11 @@ ${pc10.bold("Current Configuration:")}
|
|
|
4981
5276
|
lambdaFunctions: result.lambdaFunctions,
|
|
4982
5277
|
domain: result.domain,
|
|
4983
5278
|
dkimTokens: result.dkimTokens,
|
|
4984
|
-
customTrackingDomain: result.customTrackingDomain
|
|
5279
|
+
customTrackingDomain: result.customTrackingDomain,
|
|
5280
|
+
mailFromDomain: result.mailFromDomain,
|
|
5281
|
+
archiveArn: result.archiveArn,
|
|
5282
|
+
archivingEnabled: result.archivingEnabled,
|
|
5283
|
+
archiveRetention: result.archiveRetention
|
|
4985
5284
|
};
|
|
4986
5285
|
}
|
|
4987
5286
|
},
|
|
@@ -5008,7 +5307,11 @@ ${pc10.bold("Current Configuration:")}
|
|
|
5008
5307
|
lambdaFunctions: pulumiOutputs.lambdaFunctions?.value,
|
|
5009
5308
|
domain: pulumiOutputs.domain?.value,
|
|
5010
5309
|
dkimTokens: pulumiOutputs.dkimTokens?.value,
|
|
5011
|
-
customTrackingDomain: pulumiOutputs.customTrackingDomain?.value
|
|
5310
|
+
customTrackingDomain: pulumiOutputs.customTrackingDomain?.value,
|
|
5311
|
+
mailFromDomain: pulumiOutputs.mailFromDomain?.value,
|
|
5312
|
+
archiveArn: pulumiOutputs.archiveArn?.value,
|
|
5313
|
+
archivingEnabled: pulumiOutputs.archivingEnabled?.value,
|
|
5314
|
+
archiveRetention: pulumiOutputs.archiveRetention?.value
|
|
5012
5315
|
};
|
|
5013
5316
|
}
|
|
5014
5317
|
);
|
|
@@ -5119,7 +5422,7 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5119
5422
|
console.log(` ${pc11.green("\u2713")} Event Tracking (EventBridge)`);
|
|
5120
5423
|
if (config.eventTracking.dynamoDBHistory) {
|
|
5121
5424
|
console.log(
|
|
5122
|
-
` ${pc11.dim("\u2514\u2500")} Email History: ${pc11.cyan(config.eventTracking.archiveRetention || "
|
|
5425
|
+
` ${pc11.dim("\u2514\u2500")} Email History: ${pc11.cyan(config.eventTracking.archiveRetention || "3months")}`
|
|
5123
5426
|
);
|
|
5124
5427
|
}
|
|
5125
5428
|
}
|
|
@@ -5128,14 +5431,23 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5128
5431
|
}
|
|
5129
5432
|
if (config.emailArchiving?.enabled) {
|
|
5130
5433
|
const retentionLabel = {
|
|
5131
|
-
"
|
|
5132
|
-
"30days": "30 days",
|
|
5133
|
-
"90days": "90 days",
|
|
5434
|
+
"3months": "3 months",
|
|
5134
5435
|
"6months": "6 months",
|
|
5436
|
+
"9months": "9 months",
|
|
5135
5437
|
"1year": "1 year",
|
|
5136
5438
|
"18months": "18 months",
|
|
5137
|
-
|
|
5138
|
-
|
|
5439
|
+
"2years": "2 years",
|
|
5440
|
+
"30months": "30 months",
|
|
5441
|
+
"3years": "3 years",
|
|
5442
|
+
"4years": "4 years",
|
|
5443
|
+
"5years": "5 years",
|
|
5444
|
+
"6years": "6 years",
|
|
5445
|
+
"7years": "7 years",
|
|
5446
|
+
"8years": "8 years",
|
|
5447
|
+
"9years": "9 years",
|
|
5448
|
+
"10years": "10 years",
|
|
5449
|
+
permanent: "permanent"
|
|
5450
|
+
}[config.emailArchiving.retention] || "3 months";
|
|
5139
5451
|
console.log(` ${pc11.green("\u2713")} Email Archiving (${retentionLabel})`);
|
|
5140
5452
|
}
|
|
5141
5453
|
const currentCostData = calculateCosts(config, 5e4);
|
|
@@ -5265,18 +5577,8 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5265
5577
|
message: "Email archive retention period:",
|
|
5266
5578
|
options: [
|
|
5267
5579
|
{
|
|
5268
|
-
value: "
|
|
5269
|
-
label: "
|
|
5270
|
-
hint: "~$1-2/mo for 10k emails"
|
|
5271
|
-
},
|
|
5272
|
-
{
|
|
5273
|
-
value: "30days",
|
|
5274
|
-
label: "30 days",
|
|
5275
|
-
hint: "~$2-4/mo for 10k emails"
|
|
5276
|
-
},
|
|
5277
|
-
{
|
|
5278
|
-
value: "90days",
|
|
5279
|
-
label: "90 days (recommended)",
|
|
5580
|
+
value: "3months",
|
|
5581
|
+
label: "3 months (minimum)",
|
|
5280
5582
|
hint: "~$5-10/mo for 10k emails"
|
|
5281
5583
|
},
|
|
5282
5584
|
{
|
|
@@ -5286,13 +5588,38 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5286
5588
|
},
|
|
5287
5589
|
{
|
|
5288
5590
|
value: "1year",
|
|
5289
|
-
label: "1 year",
|
|
5591
|
+
label: "1 year (recommended)",
|
|
5290
5592
|
hint: "~$25-40/mo for 10k emails"
|
|
5291
5593
|
},
|
|
5292
5594
|
{
|
|
5293
|
-
value: "
|
|
5294
|
-
label: "
|
|
5295
|
-
hint: "~$
|
|
5595
|
+
value: "2years",
|
|
5596
|
+
label: "2 years",
|
|
5597
|
+
hint: "~$50-80/mo for 10k emails"
|
|
5598
|
+
},
|
|
5599
|
+
{
|
|
5600
|
+
value: "3years",
|
|
5601
|
+
label: "3 years",
|
|
5602
|
+
hint: "~$75-120/mo for 10k emails"
|
|
5603
|
+
},
|
|
5604
|
+
{
|
|
5605
|
+
value: "5years",
|
|
5606
|
+
label: "5 years",
|
|
5607
|
+
hint: "~$125-200/mo for 10k emails"
|
|
5608
|
+
},
|
|
5609
|
+
{
|
|
5610
|
+
value: "7years",
|
|
5611
|
+
label: "7 years",
|
|
5612
|
+
hint: "~$175-280/mo for 10k emails"
|
|
5613
|
+
},
|
|
5614
|
+
{
|
|
5615
|
+
value: "10years",
|
|
5616
|
+
label: "10 years",
|
|
5617
|
+
hint: "~$250-400/mo for 10k emails"
|
|
5618
|
+
},
|
|
5619
|
+
{
|
|
5620
|
+
value: "permanent",
|
|
5621
|
+
label: "Permanent",
|
|
5622
|
+
hint: "Expensive, not recommended"
|
|
5296
5623
|
}
|
|
5297
5624
|
],
|
|
5298
5625
|
initialValue: config.emailArchiving.retention
|
|
@@ -5326,18 +5653,8 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5326
5653
|
message: "Email archive retention period:",
|
|
5327
5654
|
options: [
|
|
5328
5655
|
{
|
|
5329
|
-
value: "
|
|
5330
|
-
label: "
|
|
5331
|
-
hint: "~$1-2/mo for 10k emails"
|
|
5332
|
-
},
|
|
5333
|
-
{
|
|
5334
|
-
value: "30days",
|
|
5335
|
-
label: "30 days",
|
|
5336
|
-
hint: "~$2-4/mo for 10k emails"
|
|
5337
|
-
},
|
|
5338
|
-
{
|
|
5339
|
-
value: "90days",
|
|
5340
|
-
label: "90 days (recommended)",
|
|
5656
|
+
value: "3months",
|
|
5657
|
+
label: "3 months (minimum)",
|
|
5341
5658
|
hint: "~$5-10/mo for 10k emails"
|
|
5342
5659
|
},
|
|
5343
5660
|
{
|
|
@@ -5347,16 +5664,41 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5347
5664
|
},
|
|
5348
5665
|
{
|
|
5349
5666
|
value: "1year",
|
|
5350
|
-
label: "1 year",
|
|
5667
|
+
label: "1 year (recommended)",
|
|
5351
5668
|
hint: "~$25-40/mo for 10k emails"
|
|
5352
5669
|
},
|
|
5353
5670
|
{
|
|
5354
|
-
value: "
|
|
5355
|
-
label: "
|
|
5356
|
-
hint: "~$
|
|
5671
|
+
value: "2years",
|
|
5672
|
+
label: "2 years",
|
|
5673
|
+
hint: "~$50-80/mo for 10k emails"
|
|
5674
|
+
},
|
|
5675
|
+
{
|
|
5676
|
+
value: "3years",
|
|
5677
|
+
label: "3 years",
|
|
5678
|
+
hint: "~$75-120/mo for 10k emails"
|
|
5679
|
+
},
|
|
5680
|
+
{
|
|
5681
|
+
value: "5years",
|
|
5682
|
+
label: "5 years",
|
|
5683
|
+
hint: "~$125-200/mo for 10k emails"
|
|
5684
|
+
},
|
|
5685
|
+
{
|
|
5686
|
+
value: "7years",
|
|
5687
|
+
label: "7 years",
|
|
5688
|
+
hint: "~$175-280/mo for 10k emails"
|
|
5689
|
+
},
|
|
5690
|
+
{
|
|
5691
|
+
value: "10years",
|
|
5692
|
+
label: "10 years",
|
|
5693
|
+
hint: "~$250-400/mo for 10k emails"
|
|
5694
|
+
},
|
|
5695
|
+
{
|
|
5696
|
+
value: "permanent",
|
|
5697
|
+
label: "Permanent",
|
|
5698
|
+
hint: "Expensive, not recommended"
|
|
5357
5699
|
}
|
|
5358
5700
|
],
|
|
5359
|
-
initialValue: "
|
|
5701
|
+
initialValue: "3months"
|
|
5360
5702
|
});
|
|
5361
5703
|
if (clack11.isCancel(retention)) {
|
|
5362
5704
|
clack11.cancel("Upgrade cancelled.");
|
|
@@ -5443,11 +5785,9 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5443
5785
|
const retention = await clack11.select({
|
|
5444
5786
|
message: "Email history retention period (event data in DynamoDB):",
|
|
5445
5787
|
options: [
|
|
5446
|
-
{ value: "7days", label: "7 days", hint: "Minimal storage cost" },
|
|
5447
|
-
{ value: "30days", label: "30 days", hint: "Development/testing" },
|
|
5448
5788
|
{
|
|
5449
|
-
value: "
|
|
5450
|
-
label: "
|
|
5789
|
+
value: "3months",
|
|
5790
|
+
label: "3 months (recommended)",
|
|
5451
5791
|
hint: "Standard retention"
|
|
5452
5792
|
},
|
|
5453
5793
|
{
|
|
@@ -5460,9 +5800,19 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5460
5800
|
value: "18months",
|
|
5461
5801
|
label: "18 months",
|
|
5462
5802
|
hint: "Long-term retention"
|
|
5803
|
+
},
|
|
5804
|
+
{
|
|
5805
|
+
value: "2years",
|
|
5806
|
+
label: "2 years",
|
|
5807
|
+
hint: "Extended compliance"
|
|
5808
|
+
},
|
|
5809
|
+
{
|
|
5810
|
+
value: "permanent",
|
|
5811
|
+
label: "Permanent",
|
|
5812
|
+
hint: "Not recommended (expensive)"
|
|
5463
5813
|
}
|
|
5464
5814
|
],
|
|
5465
|
-
initialValue: config.eventTracking?.archiveRetention || "
|
|
5815
|
+
initialValue: config.eventTracking?.archiveRetention || "3months"
|
|
5466
5816
|
});
|
|
5467
5817
|
if (clack11.isCancel(retention)) {
|
|
5468
5818
|
clack11.cancel("Upgrade cancelled.");
|
|
@@ -5728,14 +6078,14 @@ ${pc11.green("\u2713")} ${pc11.bold("Upgrade complete!")}
|
|
|
5728
6078
|
init_esm_shims();
|
|
5729
6079
|
init_aws();
|
|
5730
6080
|
import { Resolver } from "dns/promises";
|
|
5731
|
-
import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as
|
|
6081
|
+
import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as SESv2Client4 } from "@aws-sdk/client-sesv2";
|
|
5732
6082
|
import * as clack12 from "@clack/prompts";
|
|
5733
6083
|
import pc12 from "picocolors";
|
|
5734
6084
|
async function verify(options) {
|
|
5735
6085
|
clack12.intro(pc12.bold(`Verifying ${options.domain}`));
|
|
5736
6086
|
const progress = new DeploymentProgress();
|
|
5737
6087
|
const region = await getAWSRegion();
|
|
5738
|
-
const sesClient = new
|
|
6088
|
+
const sesClient = new SESv2Client4({ region });
|
|
5739
6089
|
let identity;
|
|
5740
6090
|
let dkimTokens = [];
|
|
5741
6091
|
let mailFromDomain;
|