@wraps.dev/cli 0.3.2 → 0.3.4
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 +481 -110
- package/dist/cli.js.map +1 -1
- package/dist/console/assets/index-DCoFg1PY.js +381 -0
- package/dist/console/index.html +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/dist/lambda/event-processor/index.mjs +1 -1
- package/package.json +5 -4
- package/dist/console/assets/index-BmOMPb6r.js +0 -381
package/dist/cli.js
CHANGED
|
@@ -9,11 +9,11 @@ var __export = (target, all3) => {
|
|
|
9
9
|
__defProp(target, name, { get: all3[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.
|
|
12
|
+
// ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
|
|
13
13
|
import path from "path";
|
|
14
14
|
import { fileURLToPath } from "url";
|
|
15
15
|
var init_esm_shims = __esm({
|
|
16
|
-
"../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.
|
|
16
|
+
"../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js"() {
|
|
17
17
|
"use strict";
|
|
18
18
|
}
|
|
19
19
|
});
|
|
@@ -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,114 @@ 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";
|
|
1539
|
+
import DOMPurify from "isomorphic-dompurify";
|
|
1378
1540
|
import { simpleParser } from "mailparser";
|
|
1379
|
-
|
|
1380
|
-
|
|
1541
|
+
function extractArchiveId(archiveArnOrId) {
|
|
1542
|
+
if (archiveArnOrId.startsWith("arn:")) {
|
|
1543
|
+
const parts = archiveArnOrId.split("/");
|
|
1544
|
+
return parts.at(-1);
|
|
1545
|
+
}
|
|
1546
|
+
return archiveArnOrId;
|
|
1547
|
+
}
|
|
1548
|
+
async function getArchivedEmail(archiveArnOrId, searchCriteria, region) {
|
|
1549
|
+
const client = new MailManagerClient2({ region });
|
|
1550
|
+
const archiveId = extractArchiveId(archiveArnOrId);
|
|
1551
|
+
const searchTime = searchCriteria.timestamp || /* @__PURE__ */ new Date();
|
|
1552
|
+
const dayBefore = new Date(searchTime.getTime() - 24 * 60 * 60 * 1e3);
|
|
1553
|
+
const dayAfter = new Date(searchTime.getTime() + 24 * 60 * 60 * 1e3);
|
|
1554
|
+
const filters = [];
|
|
1555
|
+
if (searchCriteria.from) {
|
|
1556
|
+
filters.push({
|
|
1557
|
+
StringExpression: {
|
|
1558
|
+
Evaluate: {
|
|
1559
|
+
Attribute: "FROM"
|
|
1560
|
+
},
|
|
1561
|
+
Operator: "CONTAINS",
|
|
1562
|
+
Values: [searchCriteria.from]
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
if (searchCriteria.to) {
|
|
1567
|
+
filters.push({
|
|
1568
|
+
StringExpression: {
|
|
1569
|
+
Evaluate: {
|
|
1570
|
+
Attribute: "TO"
|
|
1571
|
+
},
|
|
1572
|
+
Operator: "CONTAINS",
|
|
1573
|
+
Values: [searchCriteria.to]
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
if (searchCriteria.subject) {
|
|
1578
|
+
filters.push({
|
|
1579
|
+
StringExpression: {
|
|
1580
|
+
Evaluate: {
|
|
1581
|
+
Attribute: "SUBJECT"
|
|
1582
|
+
},
|
|
1583
|
+
Operator: "CONTAINS",
|
|
1584
|
+
Values: [searchCriteria.subject]
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
if (filters.length === 0) {
|
|
1589
|
+
throw new Error(
|
|
1590
|
+
"At least one search criterion (from, to, or subject) is required"
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
const searchCommand = new StartArchiveSearchCommand({
|
|
1594
|
+
ArchiveId: archiveId,
|
|
1595
|
+
FromTimestamp: dayBefore,
|
|
1596
|
+
ToTimestamp: dayAfter,
|
|
1597
|
+
Filters: {
|
|
1598
|
+
Include: filters
|
|
1599
|
+
},
|
|
1600
|
+
MaxResults: 10
|
|
1601
|
+
// Get a few results in case there are multiple matches
|
|
1602
|
+
});
|
|
1603
|
+
const searchResponse = await client.send(searchCommand);
|
|
1604
|
+
const searchId = searchResponse.SearchId;
|
|
1605
|
+
if (!searchId) {
|
|
1606
|
+
throw new Error("Failed to start archive search");
|
|
1607
|
+
}
|
|
1608
|
+
let archivedMessageId;
|
|
1609
|
+
let attempts = 0;
|
|
1610
|
+
const maxAttempts = 20;
|
|
1611
|
+
const pollInterval = 1e3;
|
|
1612
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1613
|
+
while (attempts < maxAttempts) {
|
|
1614
|
+
try {
|
|
1615
|
+
const resultsCommand = new GetArchiveSearchResultsCommand({
|
|
1616
|
+
SearchId: searchId
|
|
1617
|
+
});
|
|
1618
|
+
const resultsResponse = await client.send(resultsCommand);
|
|
1619
|
+
if (resultsResponse.Rows && resultsResponse.Rows.length > 0) {
|
|
1620
|
+
archivedMessageId = resultsResponse.Rows[0].ArchivedMessageId;
|
|
1621
|
+
break;
|
|
1622
|
+
}
|
|
1623
|
+
if (resultsResponse.Rows && resultsResponse.Rows.length === 0) {
|
|
1624
|
+
break;
|
|
1625
|
+
}
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
if (error instanceof Error && error.name === "ConflictException" && error.message.includes("still in progress")) {
|
|
1628
|
+
console.log(`Search still in progress, attempt ${attempts + 1}...`);
|
|
1629
|
+
} else {
|
|
1630
|
+
throw error;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1634
|
+
attempts++;
|
|
1635
|
+
}
|
|
1636
|
+
if (!archivedMessageId) {
|
|
1637
|
+
throw new Error(
|
|
1638
|
+
"Email not found in archive with the provided search criteria. It may have been sent before archiving was enabled."
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1381
1641
|
const command2 = new GetArchiveMessageCommand({
|
|
1382
|
-
ArchivedMessageId:
|
|
1642
|
+
ArchivedMessageId: archivedMessageId
|
|
1383
1643
|
});
|
|
1384
1644
|
const response = await client.send(command2);
|
|
1385
1645
|
if (!response.MessageDownloadLink) {
|
|
@@ -1418,8 +1678,7 @@ async function getArchivedEmail(_archiveId, messageId, region) {
|
|
|
1418
1678
|
return addr.text || "";
|
|
1419
1679
|
};
|
|
1420
1680
|
return {
|
|
1421
|
-
messageId,
|
|
1422
|
-
// Use the input messageId since response may not have MessageMetadata
|
|
1681
|
+
messageId: parsed.messageId || headers["message-id"]?.toString() || "",
|
|
1423
1682
|
from: getAddressText(parsed.from),
|
|
1424
1683
|
to: getAddressText(parsed.to),
|
|
1425
1684
|
subject: parsed.subject || "",
|
|
@@ -1446,14 +1705,20 @@ __export(email_archive_exports, {
|
|
|
1446
1705
|
fetchArchivedEmail: () => fetchArchivedEmail
|
|
1447
1706
|
});
|
|
1448
1707
|
async function fetchArchivedEmail(messageId, options) {
|
|
1449
|
-
const { region, archiveArn } = options;
|
|
1708
|
+
const { region, archiveArn, from, to, subject, timestamp } = options;
|
|
1450
1709
|
try {
|
|
1451
1710
|
console.log("Fetching archived email:", {
|
|
1452
1711
|
messageId,
|
|
1453
1712
|
archiveArn,
|
|
1454
1713
|
region
|
|
1455
1714
|
});
|
|
1456
|
-
const
|
|
1715
|
+
const searchCriteria = {
|
|
1716
|
+
from,
|
|
1717
|
+
to,
|
|
1718
|
+
subject,
|
|
1719
|
+
timestamp
|
|
1720
|
+
};
|
|
1721
|
+
const email = await getArchivedEmail(archiveArn, searchCriteria, region);
|
|
1457
1722
|
console.log("Archived email fetched successfully:", {
|
|
1458
1723
|
messageId: email.messageId,
|
|
1459
1724
|
hasHtml: !!email.html,
|
|
@@ -1880,11 +2145,18 @@ async function createIAMRole(config) {
|
|
|
1880
2145
|
statements.push({
|
|
1881
2146
|
Effect: "Allow",
|
|
1882
2147
|
Action: [
|
|
1883
|
-
|
|
2148
|
+
// Archive search operations
|
|
2149
|
+
"ses:StartArchiveSearch",
|
|
2150
|
+
"ses:GetArchiveSearchResults",
|
|
2151
|
+
// Archive message retrieval
|
|
1884
2152
|
"ses:GetArchiveMessage",
|
|
1885
2153
|
"ses:GetArchiveMessageContent",
|
|
1886
|
-
|
|
1887
|
-
"ses:
|
|
2154
|
+
// Archive metadata
|
|
2155
|
+
"ses:GetArchive",
|
|
2156
|
+
"ses:ListArchives",
|
|
2157
|
+
// Archive export (for future use)
|
|
2158
|
+
"ses:StartArchiveExport",
|
|
2159
|
+
"ses:GetArchiveExport"
|
|
1888
2160
|
],
|
|
1889
2161
|
Resource: "arn:aws:ses:*:*:mailmanager-archive/*"
|
|
1890
2162
|
});
|
|
@@ -2271,7 +2543,8 @@ async function deployEmailStack(config) {
|
|
|
2271
2543
|
archiveResources = await createMailManagerArchive2({
|
|
2272
2544
|
name: "email",
|
|
2273
2545
|
retention: emailConfig.emailArchiving.retention,
|
|
2274
|
-
configSetName: sesResources.configSet.configurationSetName
|
|
2546
|
+
configSetName: sesResources.configSet.configurationSetName,
|
|
2547
|
+
region: config.region
|
|
2275
2548
|
});
|
|
2276
2549
|
}
|
|
2277
2550
|
return {
|
|
@@ -2288,7 +2561,8 @@ async function deployEmailStack(config) {
|
|
|
2288
2561
|
dlqUrl: sqsResources?.dlq.url,
|
|
2289
2562
|
customTrackingDomain: sesResources?.customTrackingDomain,
|
|
2290
2563
|
mailFromDomain: sesResources?.mailFromDomain,
|
|
2291
|
-
|
|
2564
|
+
archiveId: archiveResources?.archiveId,
|
|
2565
|
+
archiveArn: archiveResources?.archiveArn,
|
|
2292
2566
|
archivingEnabled: emailConfig.emailArchiving?.enabled,
|
|
2293
2567
|
archiveRetention: emailConfig.emailArchiving?.enabled ? emailConfig.emailArchiving.retention : void 0
|
|
2294
2568
|
};
|
|
@@ -2606,11 +2880,11 @@ ${domainStrings.join("\n")}`);
|
|
|
2606
2880
|
const retentionLabel = {
|
|
2607
2881
|
"7days": "7 days",
|
|
2608
2882
|
"30days": "30 days",
|
|
2609
|
-
"
|
|
2883
|
+
"3months": "90 days",
|
|
2610
2884
|
"6months": "6 months",
|
|
2611
2885
|
"1year": "1 year",
|
|
2612
2886
|
"18months": "18 months"
|
|
2613
|
-
}[status2.resources.archiveRetention || "
|
|
2887
|
+
}[status2.resources.archiveRetention || "3months"] || "90 days";
|
|
2614
2888
|
featureLines.push(
|
|
2615
2889
|
` ${pc2.green("\u2713")} Email Archiving ${pc2.dim(`(${retentionLabel} retention)`)}`
|
|
2616
2890
|
);
|
|
@@ -3199,7 +3473,7 @@ import { Router as createRouter } from "express";
|
|
|
3199
3473
|
init_esm_shims();
|
|
3200
3474
|
init_assume_role();
|
|
3201
3475
|
import { GetSendQuotaCommand, SESClient as SESClient3 } from "@aws-sdk/client-ses";
|
|
3202
|
-
import { GetEmailIdentityCommand, SESv2Client } from "@aws-sdk/client-sesv2";
|
|
3476
|
+
import { GetEmailIdentityCommand, SESv2Client as SESv2Client2 } from "@aws-sdk/client-sesv2";
|
|
3203
3477
|
async function fetchSendQuota(roleArn, region) {
|
|
3204
3478
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3205
3479
|
const ses = new SESClient3({ region, credentials });
|
|
@@ -3212,7 +3486,7 @@ async function fetchSendQuota(roleArn, region) {
|
|
|
3212
3486
|
}
|
|
3213
3487
|
async function fetchDomainInfo(roleArn, region, domain) {
|
|
3214
3488
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3215
|
-
const sesv22 = new
|
|
3489
|
+
const sesv22 = new SESv2Client2({ region, credentials });
|
|
3216
3490
|
const response = await sesv22.send(
|
|
3217
3491
|
new GetEmailIdentityCommand({
|
|
3218
3492
|
EmailIdentity: domain
|
|
@@ -3631,11 +3905,33 @@ function createEmailsRouter(config) {
|
|
|
3631
3905
|
error: "Archive ARN not configured."
|
|
3632
3906
|
});
|
|
3633
3907
|
}
|
|
3908
|
+
if (!config.tableName) {
|
|
3909
|
+
console.log("No table name configured");
|
|
3910
|
+
return res.status(400).json({
|
|
3911
|
+
error: "Email tracking not enabled. Need email metadata to search archive."
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
console.log("Fetching email metadata from DynamoDB...");
|
|
3915
|
+
const emailDetails = await fetchEmailById(id, {
|
|
3916
|
+
region: config.region,
|
|
3917
|
+
tableName: config.tableName
|
|
3918
|
+
});
|
|
3919
|
+
if (!emailDetails) {
|
|
3920
|
+
console.log("Email metadata not found in DynamoDB for ID:", id);
|
|
3921
|
+
return res.status(404).json({
|
|
3922
|
+
error: "Email metadata not found. Cannot search archive."
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3634
3925
|
console.log("Fetching archived email from Mail Manager...");
|
|
3635
3926
|
const { fetchArchivedEmail: fetchArchivedEmail2 } = await Promise.resolve().then(() => (init_email_archive(), email_archive_exports));
|
|
3636
3927
|
const archivedEmail = await fetchArchivedEmail2(id, {
|
|
3637
3928
|
region: config.region,
|
|
3638
|
-
archiveArn: config.archiveArn
|
|
3929
|
+
archiveArn: config.archiveArn,
|
|
3930
|
+
from: emailDetails.from,
|
|
3931
|
+
to: emailDetails.to[0],
|
|
3932
|
+
// Use first recipient for search
|
|
3933
|
+
subject: emailDetails.subject,
|
|
3934
|
+
timestamp: new Date(emailDetails.sentAt)
|
|
3639
3935
|
});
|
|
3640
3936
|
if (!archivedEmail) {
|
|
3641
3937
|
console.log("Archived email not found for message ID:", id);
|
|
@@ -3882,11 +4178,11 @@ init_assume_role();
|
|
|
3882
4178
|
import {
|
|
3883
4179
|
GetConfigurationSetCommand,
|
|
3884
4180
|
GetEmailIdentityCommand as GetEmailIdentityCommand2,
|
|
3885
|
-
SESv2Client as
|
|
4181
|
+
SESv2Client as SESv2Client3
|
|
3886
4182
|
} from "@aws-sdk/client-sesv2";
|
|
3887
4183
|
async function fetchConfigurationSet(roleArn, region, configSetName) {
|
|
3888
4184
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3889
|
-
const sesv22 = new
|
|
4185
|
+
const sesv22 = new SESv2Client3({ region, credentials });
|
|
3890
4186
|
const response = await sesv22.send(
|
|
3891
4187
|
new GetConfigurationSetCommand({
|
|
3892
4188
|
ConfigurationSetName: configSetName
|
|
@@ -3916,7 +4212,7 @@ async function fetchConfigurationSet(roleArn, region, configSetName) {
|
|
|
3916
4212
|
}
|
|
3917
4213
|
async function fetchEmailIdentity(roleArn, region, identityName) {
|
|
3918
4214
|
const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
|
|
3919
|
-
const sesv22 = new
|
|
4215
|
+
const sesv22 = new SESv2Client3({ region, credentials });
|
|
3920
4216
|
const response = await sesv22.send(
|
|
3921
4217
|
new GetEmailIdentityCommand2({
|
|
3922
4218
|
EmailIdentity: identityName
|
|
@@ -4108,10 +4404,10 @@ function createSettingsRouter(config) {
|
|
|
4108
4404
|
console.log(
|
|
4109
4405
|
`[Settings] Updating sending options for ${configSetName}: ${enabled}`
|
|
4110
4406
|
);
|
|
4111
|
-
const { SESv2Client:
|
|
4407
|
+
const { SESv2Client: SESv2Client5, PutConfigurationSetSendingOptionsCommand } = await import("@aws-sdk/client-sesv2");
|
|
4112
4408
|
const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
|
|
4113
4409
|
const credentials = config.roleArn ? await assumeRole2(config.roleArn, config.region) : void 0;
|
|
4114
|
-
const sesClient = new
|
|
4410
|
+
const sesClient = new SESv2Client5({ region: config.region, credentials });
|
|
4115
4411
|
await sesClient.send(
|
|
4116
4412
|
new PutConfigurationSetSendingOptionsCommand({
|
|
4117
4413
|
ConfigurationSetName: configSetName,
|
|
@@ -4145,10 +4441,10 @@ function createSettingsRouter(config) {
|
|
|
4145
4441
|
console.log(
|
|
4146
4442
|
`[Settings] Updating reputation options for ${configSetName}: ${enabled}`
|
|
4147
4443
|
);
|
|
4148
|
-
const { SESv2Client:
|
|
4444
|
+
const { SESv2Client: SESv2Client5, PutConfigurationSetReputationOptionsCommand } = await import("@aws-sdk/client-sesv2");
|
|
4149
4445
|
const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
|
|
4150
4446
|
const credentials = config.roleArn ? await assumeRole2(config.roleArn, config.region) : void 0;
|
|
4151
|
-
const sesClient = new
|
|
4447
|
+
const sesClient = new SESv2Client5({ region: config.region, credentials });
|
|
4152
4448
|
await sesClient.send(
|
|
4153
4449
|
new PutConfigurationSetReputationOptionsCommand({
|
|
4154
4450
|
ConfigurationSetName: configSetName,
|
|
@@ -4188,10 +4484,10 @@ function createSettingsRouter(config) {
|
|
|
4188
4484
|
console.log(
|
|
4189
4485
|
`[Settings] Updating tracking domain for ${configSetName}: ${domain}`
|
|
4190
4486
|
);
|
|
4191
|
-
const { SESv2Client:
|
|
4487
|
+
const { SESv2Client: SESv2Client5, PutConfigurationSetTrackingOptionsCommand } = await import("@aws-sdk/client-sesv2");
|
|
4192
4488
|
const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
|
|
4193
4489
|
const credentials = config.roleArn ? await assumeRole2(config.roleArn, config.region) : void 0;
|
|
4194
|
-
const sesClient = new
|
|
4490
|
+
const sesClient = new SESv2Client5({
|
|
4195
4491
|
region: config.region,
|
|
4196
4492
|
credentials
|
|
4197
4493
|
});
|
|
@@ -4282,6 +4578,26 @@ async function startConsoleServer(config) {
|
|
|
4282
4578
|
const app = express();
|
|
4283
4579
|
const authToken = crypto.randomBytes(32).toString("hex");
|
|
4284
4580
|
app.use(express.json());
|
|
4581
|
+
const requestCounts = /* @__PURE__ */ new Map();
|
|
4582
|
+
const RATE_LIMIT_WINDOW = 60 * 1e3;
|
|
4583
|
+
const RATE_LIMIT_MAX_REQUESTS = 1e3;
|
|
4584
|
+
app.use((req, res, next) => {
|
|
4585
|
+
const ip = req.ip || req.socket.remoteAddress || "unknown";
|
|
4586
|
+
const now = Date.now();
|
|
4587
|
+
const record = requestCounts.get(ip);
|
|
4588
|
+
if (!record || now > record.resetTime) {
|
|
4589
|
+
requestCounts.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
|
|
4590
|
+
next();
|
|
4591
|
+
} else if (record.count < RATE_LIMIT_MAX_REQUESTS) {
|
|
4592
|
+
record.count++;
|
|
4593
|
+
next();
|
|
4594
|
+
} else {
|
|
4595
|
+
res.status(429).json({
|
|
4596
|
+
error: "Too many requests, please slow down",
|
|
4597
|
+
retryAfter: Math.ceil((record.resetTime - now) / 1e3)
|
|
4598
|
+
});
|
|
4599
|
+
}
|
|
4600
|
+
});
|
|
4285
4601
|
app.use((_req, res, next) => {
|
|
4286
4602
|
res.setHeader("X-Frame-Options", "DENY");
|
|
4287
4603
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
@@ -4819,8 +5135,8 @@ Run ${pc9.cyan("wraps init")} to deploy infrastructure.
|
|
|
4819
5135
|
process.exit(1);
|
|
4820
5136
|
}
|
|
4821
5137
|
const domains = await listSESDomains(region);
|
|
4822
|
-
const { SESv2Client:
|
|
4823
|
-
const sesv2Client = new
|
|
5138
|
+
const { SESv2Client: SESv2Client5, GetEmailIdentityCommand: GetEmailIdentityCommand4 } = await import("@aws-sdk/client-sesv2");
|
|
5139
|
+
const sesv2Client = new SESv2Client5({ region });
|
|
4824
5140
|
const domainsWithTokens = await Promise.all(
|
|
4825
5141
|
domains.map(async (d) => {
|
|
4826
5142
|
try {
|
|
@@ -4981,7 +5297,11 @@ ${pc10.bold("Current Configuration:")}
|
|
|
4981
5297
|
lambdaFunctions: result.lambdaFunctions,
|
|
4982
5298
|
domain: result.domain,
|
|
4983
5299
|
dkimTokens: result.dkimTokens,
|
|
4984
|
-
customTrackingDomain: result.customTrackingDomain
|
|
5300
|
+
customTrackingDomain: result.customTrackingDomain,
|
|
5301
|
+
mailFromDomain: result.mailFromDomain,
|
|
5302
|
+
archiveArn: result.archiveArn,
|
|
5303
|
+
archivingEnabled: result.archivingEnabled,
|
|
5304
|
+
archiveRetention: result.archiveRetention
|
|
4985
5305
|
};
|
|
4986
5306
|
}
|
|
4987
5307
|
},
|
|
@@ -5008,7 +5328,11 @@ ${pc10.bold("Current Configuration:")}
|
|
|
5008
5328
|
lambdaFunctions: pulumiOutputs.lambdaFunctions?.value,
|
|
5009
5329
|
domain: pulumiOutputs.domain?.value,
|
|
5010
5330
|
dkimTokens: pulumiOutputs.dkimTokens?.value,
|
|
5011
|
-
customTrackingDomain: pulumiOutputs.customTrackingDomain?.value
|
|
5331
|
+
customTrackingDomain: pulumiOutputs.customTrackingDomain?.value,
|
|
5332
|
+
mailFromDomain: pulumiOutputs.mailFromDomain?.value,
|
|
5333
|
+
archiveArn: pulumiOutputs.archiveArn?.value,
|
|
5334
|
+
archivingEnabled: pulumiOutputs.archivingEnabled?.value,
|
|
5335
|
+
archiveRetention: pulumiOutputs.archiveRetention?.value
|
|
5012
5336
|
};
|
|
5013
5337
|
}
|
|
5014
5338
|
);
|
|
@@ -5119,7 +5443,7 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5119
5443
|
console.log(` ${pc11.green("\u2713")} Event Tracking (EventBridge)`);
|
|
5120
5444
|
if (config.eventTracking.dynamoDBHistory) {
|
|
5121
5445
|
console.log(
|
|
5122
|
-
` ${pc11.dim("\u2514\u2500")} Email History: ${pc11.cyan(config.eventTracking.archiveRetention || "
|
|
5446
|
+
` ${pc11.dim("\u2514\u2500")} Email History: ${pc11.cyan(config.eventTracking.archiveRetention || "3months")}`
|
|
5123
5447
|
);
|
|
5124
5448
|
}
|
|
5125
5449
|
}
|
|
@@ -5128,14 +5452,23 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5128
5452
|
}
|
|
5129
5453
|
if (config.emailArchiving?.enabled) {
|
|
5130
5454
|
const retentionLabel = {
|
|
5131
|
-
"
|
|
5132
|
-
"30days": "30 days",
|
|
5133
|
-
"90days": "90 days",
|
|
5455
|
+
"3months": "3 months",
|
|
5134
5456
|
"6months": "6 months",
|
|
5457
|
+
"9months": "9 months",
|
|
5135
5458
|
"1year": "1 year",
|
|
5136
5459
|
"18months": "18 months",
|
|
5137
|
-
|
|
5138
|
-
|
|
5460
|
+
"2years": "2 years",
|
|
5461
|
+
"30months": "30 months",
|
|
5462
|
+
"3years": "3 years",
|
|
5463
|
+
"4years": "4 years",
|
|
5464
|
+
"5years": "5 years",
|
|
5465
|
+
"6years": "6 years",
|
|
5466
|
+
"7years": "7 years",
|
|
5467
|
+
"8years": "8 years",
|
|
5468
|
+
"9years": "9 years",
|
|
5469
|
+
"10years": "10 years",
|
|
5470
|
+
permanent: "permanent"
|
|
5471
|
+
}[config.emailArchiving.retention] || "3 months";
|
|
5139
5472
|
console.log(` ${pc11.green("\u2713")} Email Archiving (${retentionLabel})`);
|
|
5140
5473
|
}
|
|
5141
5474
|
const currentCostData = calculateCosts(config, 5e4);
|
|
@@ -5265,18 +5598,8 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5265
5598
|
message: "Email archive retention period:",
|
|
5266
5599
|
options: [
|
|
5267
5600
|
{
|
|
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)",
|
|
5601
|
+
value: "3months",
|
|
5602
|
+
label: "3 months (minimum)",
|
|
5280
5603
|
hint: "~$5-10/mo for 10k emails"
|
|
5281
5604
|
},
|
|
5282
5605
|
{
|
|
@@ -5286,13 +5609,38 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5286
5609
|
},
|
|
5287
5610
|
{
|
|
5288
5611
|
value: "1year",
|
|
5289
|
-
label: "1 year",
|
|
5612
|
+
label: "1 year (recommended)",
|
|
5290
5613
|
hint: "~$25-40/mo for 10k emails"
|
|
5291
5614
|
},
|
|
5292
5615
|
{
|
|
5293
|
-
value: "
|
|
5294
|
-
label: "
|
|
5295
|
-
hint: "~$
|
|
5616
|
+
value: "2years",
|
|
5617
|
+
label: "2 years",
|
|
5618
|
+
hint: "~$50-80/mo for 10k emails"
|
|
5619
|
+
},
|
|
5620
|
+
{
|
|
5621
|
+
value: "3years",
|
|
5622
|
+
label: "3 years",
|
|
5623
|
+
hint: "~$75-120/mo for 10k emails"
|
|
5624
|
+
},
|
|
5625
|
+
{
|
|
5626
|
+
value: "5years",
|
|
5627
|
+
label: "5 years",
|
|
5628
|
+
hint: "~$125-200/mo for 10k emails"
|
|
5629
|
+
},
|
|
5630
|
+
{
|
|
5631
|
+
value: "7years",
|
|
5632
|
+
label: "7 years",
|
|
5633
|
+
hint: "~$175-280/mo for 10k emails"
|
|
5634
|
+
},
|
|
5635
|
+
{
|
|
5636
|
+
value: "10years",
|
|
5637
|
+
label: "10 years",
|
|
5638
|
+
hint: "~$250-400/mo for 10k emails"
|
|
5639
|
+
},
|
|
5640
|
+
{
|
|
5641
|
+
value: "permanent",
|
|
5642
|
+
label: "Permanent",
|
|
5643
|
+
hint: "Expensive, not recommended"
|
|
5296
5644
|
}
|
|
5297
5645
|
],
|
|
5298
5646
|
initialValue: config.emailArchiving.retention
|
|
@@ -5326,18 +5674,8 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5326
5674
|
message: "Email archive retention period:",
|
|
5327
5675
|
options: [
|
|
5328
5676
|
{
|
|
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)",
|
|
5677
|
+
value: "3months",
|
|
5678
|
+
label: "3 months (minimum)",
|
|
5341
5679
|
hint: "~$5-10/mo for 10k emails"
|
|
5342
5680
|
},
|
|
5343
5681
|
{
|
|
@@ -5347,16 +5685,41 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5347
5685
|
},
|
|
5348
5686
|
{
|
|
5349
5687
|
value: "1year",
|
|
5350
|
-
label: "1 year",
|
|
5688
|
+
label: "1 year (recommended)",
|
|
5351
5689
|
hint: "~$25-40/mo for 10k emails"
|
|
5352
5690
|
},
|
|
5353
5691
|
{
|
|
5354
|
-
value: "
|
|
5355
|
-
label: "
|
|
5356
|
-
hint: "~$
|
|
5692
|
+
value: "2years",
|
|
5693
|
+
label: "2 years",
|
|
5694
|
+
hint: "~$50-80/mo for 10k emails"
|
|
5695
|
+
},
|
|
5696
|
+
{
|
|
5697
|
+
value: "3years",
|
|
5698
|
+
label: "3 years",
|
|
5699
|
+
hint: "~$75-120/mo for 10k emails"
|
|
5700
|
+
},
|
|
5701
|
+
{
|
|
5702
|
+
value: "5years",
|
|
5703
|
+
label: "5 years",
|
|
5704
|
+
hint: "~$125-200/mo for 10k emails"
|
|
5705
|
+
},
|
|
5706
|
+
{
|
|
5707
|
+
value: "7years",
|
|
5708
|
+
label: "7 years",
|
|
5709
|
+
hint: "~$175-280/mo for 10k emails"
|
|
5710
|
+
},
|
|
5711
|
+
{
|
|
5712
|
+
value: "10years",
|
|
5713
|
+
label: "10 years",
|
|
5714
|
+
hint: "~$250-400/mo for 10k emails"
|
|
5715
|
+
},
|
|
5716
|
+
{
|
|
5717
|
+
value: "permanent",
|
|
5718
|
+
label: "Permanent",
|
|
5719
|
+
hint: "Expensive, not recommended"
|
|
5357
5720
|
}
|
|
5358
5721
|
],
|
|
5359
|
-
initialValue: "
|
|
5722
|
+
initialValue: "3months"
|
|
5360
5723
|
});
|
|
5361
5724
|
if (clack11.isCancel(retention)) {
|
|
5362
5725
|
clack11.cancel("Upgrade cancelled.");
|
|
@@ -5443,11 +5806,9 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5443
5806
|
const retention = await clack11.select({
|
|
5444
5807
|
message: "Email history retention period (event data in DynamoDB):",
|
|
5445
5808
|
options: [
|
|
5446
|
-
{ value: "7days", label: "7 days", hint: "Minimal storage cost" },
|
|
5447
|
-
{ value: "30days", label: "30 days", hint: "Development/testing" },
|
|
5448
5809
|
{
|
|
5449
|
-
value: "
|
|
5450
|
-
label: "
|
|
5810
|
+
value: "3months",
|
|
5811
|
+
label: "3 months (recommended)",
|
|
5451
5812
|
hint: "Standard retention"
|
|
5452
5813
|
},
|
|
5453
5814
|
{
|
|
@@ -5460,9 +5821,19 @@ ${pc11.bold("Current Configuration:")}
|
|
|
5460
5821
|
value: "18months",
|
|
5461
5822
|
label: "18 months",
|
|
5462
5823
|
hint: "Long-term retention"
|
|
5824
|
+
},
|
|
5825
|
+
{
|
|
5826
|
+
value: "2years",
|
|
5827
|
+
label: "2 years",
|
|
5828
|
+
hint: "Extended compliance"
|
|
5829
|
+
},
|
|
5830
|
+
{
|
|
5831
|
+
value: "permanent",
|
|
5832
|
+
label: "Permanent",
|
|
5833
|
+
hint: "Not recommended (expensive)"
|
|
5463
5834
|
}
|
|
5464
5835
|
],
|
|
5465
|
-
initialValue: config.eventTracking?.archiveRetention || "
|
|
5836
|
+
initialValue: config.eventTracking?.archiveRetention || "3months"
|
|
5466
5837
|
});
|
|
5467
5838
|
if (clack11.isCancel(retention)) {
|
|
5468
5839
|
clack11.cancel("Upgrade cancelled.");
|
|
@@ -5728,14 +6099,14 @@ ${pc11.green("\u2713")} ${pc11.bold("Upgrade complete!")}
|
|
|
5728
6099
|
init_esm_shims();
|
|
5729
6100
|
init_aws();
|
|
5730
6101
|
import { Resolver } from "dns/promises";
|
|
5731
|
-
import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as
|
|
6102
|
+
import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as SESv2Client4 } from "@aws-sdk/client-sesv2";
|
|
5732
6103
|
import * as clack12 from "@clack/prompts";
|
|
5733
6104
|
import pc12 from "picocolors";
|
|
5734
6105
|
async function verify(options) {
|
|
5735
6106
|
clack12.intro(pc12.bold(`Verifying ${options.domain}`));
|
|
5736
6107
|
const progress = new DeploymentProgress();
|
|
5737
6108
|
const region = await getAWSRegion();
|
|
5738
|
-
const sesClient = new
|
|
6109
|
+
const sesClient = new SESv2Client4({ region });
|
|
5739
6110
|
let identity;
|
|
5740
6111
|
let dkimTokens = [];
|
|
5741
6112
|
let mailFromDomain;
|