@wraps.dev/cli 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/cli.js +344 -81
- package/dist/cli.js.map +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/dist/lambda/sms-event-processor/.bundled +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -147,7 +147,7 @@ var require_package = __commonJS({
|
|
|
147
147
|
"package.json"(exports, module) {
|
|
148
148
|
module.exports = {
|
|
149
149
|
name: "@wraps.dev/cli",
|
|
150
|
-
version: "2.
|
|
150
|
+
version: "2.2.0",
|
|
151
151
|
description: "CLI for deploying Wraps email infrastructure to your AWS account",
|
|
152
152
|
type: "module",
|
|
153
153
|
main: "./dist/cli.js",
|
|
@@ -647,6 +647,19 @@ To remove: wraps destroy --stack ${stackName}`,
|
|
|
647
647
|
"SMS_SIMULATOR_LIMIT",
|
|
648
648
|
"Upgrade to a toll-free number for production use:\n wraps sms upgrade --phone-type toll-free",
|
|
649
649
|
"https://wraps.dev/docs/cli-reference"
|
|
650
|
+
),
|
|
651
|
+
// SMTP-specific errors
|
|
652
|
+
smtpRequiresSending: () => new WrapsError(
|
|
653
|
+
"SMTP credentials require email sending to be enabled",
|
|
654
|
+
"SMTP_REQUIRES_SENDING",
|
|
655
|
+
"Enable sending first:\n wraps email upgrade\nAnd select 'Custom configuration' to enable sending.",
|
|
656
|
+
"https://wraps.dev/docs/cli-reference"
|
|
657
|
+
),
|
|
658
|
+
smtpCredentialsNotFound: () => new WrapsError(
|
|
659
|
+
"SMTP credentials not found",
|
|
660
|
+
"SMTP_CREDENTIALS_NOT_FOUND",
|
|
661
|
+
"Enable SMTP credentials:\n wraps email upgrade\nAnd select 'Enable SMTP credentials'",
|
|
662
|
+
"https://wraps.dev/docs/cli-reference"
|
|
650
663
|
)
|
|
651
664
|
};
|
|
652
665
|
}
|
|
@@ -953,6 +966,15 @@ function calculateEmailArchivingCost(config2, emailsPerMonth) {
|
|
|
953
966
|
description: `Email archiving (${retention}, ~${storageGB.toFixed(2)} GB at steady-state)`
|
|
954
967
|
};
|
|
955
968
|
}
|
|
969
|
+
function calculateSMTPCredentialsCost(config2) {
|
|
970
|
+
if (!config2.smtpCredentials?.enabled) {
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
monthly: 0,
|
|
975
|
+
description: "SMTP credentials (no additional cost)"
|
|
976
|
+
};
|
|
977
|
+
}
|
|
956
978
|
function calculateCosts(config2, emailsPerMonth = 1e4) {
|
|
957
979
|
const tracking = calculateTrackingCost(config2);
|
|
958
980
|
const reputationMetrics = calculateReputationMetricsCost(config2);
|
|
@@ -961,8 +983,9 @@ function calculateCosts(config2, emailsPerMonth = 1e4) {
|
|
|
961
983
|
const emailArchiving = calculateEmailArchivingCost(config2, emailsPerMonth);
|
|
962
984
|
const dedicatedIp = calculateDedicatedIpCost(config2);
|
|
963
985
|
const waf = calculateWafCost(config2, emailsPerMonth);
|
|
986
|
+
const smtpCredentials = calculateSMTPCredentialsCost(config2);
|
|
964
987
|
const sesEmailCost = Math.max(0, emailsPerMonth - FREE_TIER.SES_EMAILS) * AWS_PRICING.SES_PER_EMAIL;
|
|
965
|
-
const totalMonthlyCost = sesEmailCost + (tracking?.monthly || 0) + (reputationMetrics?.monthly || 0) + (eventTracking?.monthly || 0) + (dynamoDBHistory?.monthly || 0) + (emailArchiving?.monthly || 0) + (dedicatedIp?.monthly || 0) + (waf?.monthly || 0);
|
|
988
|
+
const totalMonthlyCost = sesEmailCost + (tracking?.monthly || 0) + (reputationMetrics?.monthly || 0) + (eventTracking?.monthly || 0) + (dynamoDBHistory?.monthly || 0) + (emailArchiving?.monthly || 0) + (dedicatedIp?.monthly || 0) + (waf?.monthly || 0) + (smtpCredentials?.monthly || 0);
|
|
966
989
|
return {
|
|
967
990
|
tracking,
|
|
968
991
|
reputationMetrics,
|
|
@@ -971,6 +994,7 @@ function calculateCosts(config2, emailsPerMonth = 1e4) {
|
|
|
971
994
|
emailArchiving,
|
|
972
995
|
dedicatedIp,
|
|
973
996
|
waf,
|
|
997
|
+
smtpCredentials,
|
|
974
998
|
total: {
|
|
975
999
|
monthly: totalMonthlyCost,
|
|
976
1000
|
perEmail: AWS_PRICING.SES_PER_EMAIL,
|
|
@@ -1031,6 +1055,11 @@ function getCostSummary(config2, emailsPerMonth = 1e4) {
|
|
|
1031
1055
|
` - ${costs.waf.description}: ${formatCost(costs.waf.monthly)}`
|
|
1032
1056
|
);
|
|
1033
1057
|
}
|
|
1058
|
+
if (costs.smtpCredentials) {
|
|
1059
|
+
lines.push(
|
|
1060
|
+
` - ${costs.smtpCredentials.description}: ${formatCost(costs.smtpCredentials.monthly)}`
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1034
1063
|
return lines.join("\n");
|
|
1035
1064
|
}
|
|
1036
1065
|
var AWS_PRICING, FREE_TIER;
|
|
@@ -2317,6 +2346,11 @@ function applyConfigUpdates(existingConfig, updates) {
|
|
|
2317
2346
|
...result.emailArchiving,
|
|
2318
2347
|
...value
|
|
2319
2348
|
};
|
|
2349
|
+
} else if (key === "smtpCredentials" && typeof value === "object") {
|
|
2350
|
+
result.smtpCredentials = {
|
|
2351
|
+
...result.smtpCredentials,
|
|
2352
|
+
...value
|
|
2353
|
+
};
|
|
2320
2354
|
} else {
|
|
2321
2355
|
result[key] = value;
|
|
2322
2356
|
}
|
|
@@ -3230,12 +3264,12 @@ var acm_exports = {};
|
|
|
3230
3264
|
__export(acm_exports, {
|
|
3231
3265
|
createACMCertificate: () => createACMCertificate
|
|
3232
3266
|
});
|
|
3233
|
-
import * as
|
|
3267
|
+
import * as aws9 from "@pulumi/aws";
|
|
3234
3268
|
async function createACMCertificate(config2) {
|
|
3235
|
-
const usEast1Provider = new
|
|
3269
|
+
const usEast1Provider = new aws9.Provider("acm-us-east-1", {
|
|
3236
3270
|
region: "us-east-1"
|
|
3237
3271
|
});
|
|
3238
|
-
const certificate = new
|
|
3272
|
+
const certificate = new aws9.acm.Certificate(
|
|
3239
3273
|
"wraps-email-tracking-cert",
|
|
3240
3274
|
{
|
|
3241
3275
|
domainName: config2.domain,
|
|
@@ -3258,7 +3292,7 @@ async function createACMCertificate(config2) {
|
|
|
3258
3292
|
);
|
|
3259
3293
|
let certificateValidation;
|
|
3260
3294
|
if (config2.hostedZoneId) {
|
|
3261
|
-
const validationRecord = new
|
|
3295
|
+
const validationRecord = new aws9.route53.Record(
|
|
3262
3296
|
"wraps-email-tracking-cert-validation",
|
|
3263
3297
|
{
|
|
3264
3298
|
zoneId: config2.hostedZoneId,
|
|
@@ -3268,7 +3302,7 @@ async function createACMCertificate(config2) {
|
|
|
3268
3302
|
ttl: 60
|
|
3269
3303
|
}
|
|
3270
3304
|
);
|
|
3271
|
-
certificateValidation = new
|
|
3305
|
+
certificateValidation = new aws9.acm.CertificateValidation(
|
|
3272
3306
|
"wraps-email-tracking-cert-validation-waiter",
|
|
3273
3307
|
{
|
|
3274
3308
|
certificateArn: certificate.arn,
|
|
@@ -3297,7 +3331,7 @@ var cloudfront_exports = {};
|
|
|
3297
3331
|
__export(cloudfront_exports, {
|
|
3298
3332
|
createCloudFrontTracking: () => createCloudFrontTracking
|
|
3299
3333
|
});
|
|
3300
|
-
import * as
|
|
3334
|
+
import * as aws10 from "@pulumi/aws";
|
|
3301
3335
|
async function findDistributionByAlias(alias) {
|
|
3302
3336
|
try {
|
|
3303
3337
|
const { CloudFrontClient, ListDistributionsCommand } = await import("@aws-sdk/client-cloudfront");
|
|
@@ -3313,10 +3347,10 @@ async function findDistributionByAlias(alias) {
|
|
|
3313
3347
|
}
|
|
3314
3348
|
}
|
|
3315
3349
|
async function createWAFWebACL() {
|
|
3316
|
-
const usEast1Provider = new
|
|
3350
|
+
const usEast1Provider = new aws10.Provider("waf-us-east-1", {
|
|
3317
3351
|
region: "us-east-1"
|
|
3318
3352
|
});
|
|
3319
|
-
const webAcl = new
|
|
3353
|
+
const webAcl = new aws10.wafv2.WebAcl(
|
|
3320
3354
|
"wraps-email-tracking-waf",
|
|
3321
3355
|
{
|
|
3322
3356
|
scope: "CLOUDFRONT",
|
|
@@ -3431,14 +3465,14 @@ async function createCloudFrontTracking(config2) {
|
|
|
3431
3465
|
Description: "Wraps email tracking CloudFront distribution"
|
|
3432
3466
|
}
|
|
3433
3467
|
};
|
|
3434
|
-
const distribution = existingDistributionId ? new
|
|
3468
|
+
const distribution = existingDistributionId ? new aws10.cloudfront.Distribution(
|
|
3435
3469
|
"wraps-email-tracking-cdn",
|
|
3436
3470
|
distributionConfig,
|
|
3437
3471
|
{
|
|
3438
3472
|
import: existingDistributionId
|
|
3439
3473
|
// Import existing distribution
|
|
3440
3474
|
}
|
|
3441
|
-
) : new
|
|
3475
|
+
) : new aws10.cloudfront.Distribution(
|
|
3442
3476
|
"wraps-email-tracking-cdn",
|
|
3443
3477
|
distributionConfig
|
|
3444
3478
|
);
|
|
@@ -8143,7 +8177,7 @@ import pc8 from "picocolors";
|
|
|
8143
8177
|
|
|
8144
8178
|
// src/infrastructure/email-stack.ts
|
|
8145
8179
|
init_esm_shims();
|
|
8146
|
-
import * as
|
|
8180
|
+
import * as aws11 from "@pulumi/aws";
|
|
8147
8181
|
import * as pulumi4 from "@pulumi/pulumi";
|
|
8148
8182
|
|
|
8149
8183
|
// src/infrastructure/resources/dynamodb.ts
|
|
@@ -8366,10 +8400,10 @@ import * as pulumi2 from "@pulumi/pulumi";
|
|
|
8366
8400
|
async function roleExists(roleName) {
|
|
8367
8401
|
try {
|
|
8368
8402
|
const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
|
|
8369
|
-
const
|
|
8403
|
+
const iam8 = new IAMClient3({
|
|
8370
8404
|
region: process.env.AWS_REGION || "us-east-1"
|
|
8371
8405
|
});
|
|
8372
|
-
await
|
|
8406
|
+
await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
|
|
8373
8407
|
return true;
|
|
8374
8408
|
} catch (error) {
|
|
8375
8409
|
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
|
|
@@ -8728,11 +8762,93 @@ async function createSESResources(config2) {
|
|
|
8728
8762
|
};
|
|
8729
8763
|
}
|
|
8730
8764
|
|
|
8731
|
-
// src/infrastructure/resources/
|
|
8765
|
+
// src/infrastructure/resources/smtp-credentials.ts
|
|
8732
8766
|
init_esm_shims();
|
|
8733
8767
|
import * as aws6 from "@pulumi/aws";
|
|
8768
|
+
import { createHmac } from "crypto";
|
|
8769
|
+
function convertToSMTPPassword(secretAccessKey, region) {
|
|
8770
|
+
const DATE = "11111111";
|
|
8771
|
+
const SERVICE = "ses";
|
|
8772
|
+
const MESSAGE = "SendRawEmail";
|
|
8773
|
+
const TERMINAL = "aws4_request";
|
|
8774
|
+
const VERSION2 = 4;
|
|
8775
|
+
const kDate = createHmac("sha256", `AWS4${secretAccessKey}`).update(DATE).digest();
|
|
8776
|
+
const kRegion = createHmac("sha256", kDate).update(region).digest();
|
|
8777
|
+
const kService = createHmac("sha256", kRegion).update(SERVICE).digest();
|
|
8778
|
+
const kTerminal = createHmac("sha256", kService).update(TERMINAL).digest();
|
|
8779
|
+
const kMessage = createHmac("sha256", kTerminal).update(MESSAGE).digest();
|
|
8780
|
+
const signatureWithVersion = Buffer.concat([Buffer.from([VERSION2]), kMessage]);
|
|
8781
|
+
return signatureWithVersion.toString("base64");
|
|
8782
|
+
}
|
|
8783
|
+
async function userExists(userName) {
|
|
8784
|
+
try {
|
|
8785
|
+
const { IAMClient: IAMClient3, GetUserCommand } = await import("@aws-sdk/client-iam");
|
|
8786
|
+
const iam8 = new IAMClient3({ region: process.env.AWS_REGION || "us-east-1" });
|
|
8787
|
+
await iam8.send(new GetUserCommand({ UserName: userName }));
|
|
8788
|
+
return true;
|
|
8789
|
+
} catch (error) {
|
|
8790
|
+
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity") {
|
|
8791
|
+
return false;
|
|
8792
|
+
}
|
|
8793
|
+
return false;
|
|
8794
|
+
}
|
|
8795
|
+
}
|
|
8796
|
+
async function createSMTPCredentials(config2) {
|
|
8797
|
+
const userName = "wraps-email-smtp-user";
|
|
8798
|
+
const userAlreadyExists = await userExists(userName);
|
|
8799
|
+
const iamUser = userAlreadyExists ? new aws6.iam.User(
|
|
8800
|
+
userName,
|
|
8801
|
+
{
|
|
8802
|
+
name: userName,
|
|
8803
|
+
tags: {
|
|
8804
|
+
ManagedBy: "wraps-cli",
|
|
8805
|
+
Purpose: "SES SMTP Authentication"
|
|
8806
|
+
}
|
|
8807
|
+
},
|
|
8808
|
+
{ import: userName }
|
|
8809
|
+
) : new aws6.iam.User(userName, {
|
|
8810
|
+
name: userName,
|
|
8811
|
+
tags: {
|
|
8812
|
+
ManagedBy: "wraps-cli",
|
|
8813
|
+
Purpose: "SES SMTP Authentication"
|
|
8814
|
+
}
|
|
8815
|
+
});
|
|
8816
|
+
new aws6.iam.UserPolicy("wraps-email-smtp-policy", {
|
|
8817
|
+
user: iamUser.name,
|
|
8818
|
+
policy: JSON.stringify({
|
|
8819
|
+
Version: "2012-10-17",
|
|
8820
|
+
Statement: [
|
|
8821
|
+
{
|
|
8822
|
+
Effect: "Allow",
|
|
8823
|
+
Action: "ses:SendRawEmail",
|
|
8824
|
+
Resource: "*",
|
|
8825
|
+
Condition: {
|
|
8826
|
+
StringEquals: {
|
|
8827
|
+
"ses:ConfigurationSetName": config2.configSetName
|
|
8828
|
+
}
|
|
8829
|
+
}
|
|
8830
|
+
}
|
|
8831
|
+
]
|
|
8832
|
+
})
|
|
8833
|
+
});
|
|
8834
|
+
const accessKey = new aws6.iam.AccessKey("wraps-email-smtp-key", {
|
|
8835
|
+
user: iamUser.name
|
|
8836
|
+
});
|
|
8837
|
+
const smtpPassword = accessKey.secret.apply(
|
|
8838
|
+
(secret) => convertToSMTPPassword(secret, config2.region)
|
|
8839
|
+
);
|
|
8840
|
+
return {
|
|
8841
|
+
iamUser,
|
|
8842
|
+
accessKey,
|
|
8843
|
+
smtpPassword
|
|
8844
|
+
};
|
|
8845
|
+
}
|
|
8846
|
+
|
|
8847
|
+
// src/infrastructure/resources/sqs.ts
|
|
8848
|
+
init_esm_shims();
|
|
8849
|
+
import * as aws7 from "@pulumi/aws";
|
|
8734
8850
|
async function createSQSResources() {
|
|
8735
|
-
const dlq = new
|
|
8851
|
+
const dlq = new aws7.sqs.Queue("wraps-email-events-dlq", {
|
|
8736
8852
|
name: "wraps-email-events-dlq",
|
|
8737
8853
|
messageRetentionSeconds: 1209600,
|
|
8738
8854
|
// 14 days
|
|
@@ -8741,7 +8857,7 @@ async function createSQSResources() {
|
|
|
8741
8857
|
Description: "Dead letter queue for failed SES event processing"
|
|
8742
8858
|
}
|
|
8743
8859
|
});
|
|
8744
|
-
const queue = new
|
|
8860
|
+
const queue = new aws7.sqs.Queue("wraps-email-events", {
|
|
8745
8861
|
name: "wraps-email-events",
|
|
8746
8862
|
visibilityTimeoutSeconds: 300,
|
|
8747
8863
|
// 5 minutes (Lambda timeout)
|
|
@@ -8769,14 +8885,14 @@ async function createSQSResources() {
|
|
|
8769
8885
|
|
|
8770
8886
|
// src/infrastructure/vercel-oidc.ts
|
|
8771
8887
|
init_esm_shims();
|
|
8772
|
-
import * as
|
|
8888
|
+
import * as aws8 from "@pulumi/aws";
|
|
8773
8889
|
async function getExistingOIDCProviderArn(url) {
|
|
8774
8890
|
try {
|
|
8775
8891
|
const { IAMClient: IAMClient3, ListOpenIDConnectProvidersCommand } = await import("@aws-sdk/client-iam");
|
|
8776
|
-
const
|
|
8892
|
+
const iam8 = new IAMClient3({
|
|
8777
8893
|
region: process.env.AWS_REGION || "us-east-1"
|
|
8778
8894
|
});
|
|
8779
|
-
const response = await
|
|
8895
|
+
const response = await iam8.send(new ListOpenIDConnectProvidersCommand({}));
|
|
8780
8896
|
const expectedArnSuffix = url.replace("https://", "");
|
|
8781
8897
|
const provider = response.OpenIDConnectProviderList?.find(
|
|
8782
8898
|
(p) => p.Arn?.endsWith(expectedArnSuffix)
|
|
@@ -8791,7 +8907,7 @@ async function createVercelOIDC(config2) {
|
|
|
8791
8907
|
const url = `https://oidc.vercel.com/${config2.teamSlug}`;
|
|
8792
8908
|
const existingArn = await getExistingOIDCProviderArn(url);
|
|
8793
8909
|
if (existingArn) {
|
|
8794
|
-
return new
|
|
8910
|
+
return new aws8.iam.OpenIdConnectProvider(
|
|
8795
8911
|
"wraps-vercel-oidc",
|
|
8796
8912
|
{
|
|
8797
8913
|
url,
|
|
@@ -8812,7 +8928,7 @@ async function createVercelOIDC(config2) {
|
|
|
8812
8928
|
}
|
|
8813
8929
|
);
|
|
8814
8930
|
}
|
|
8815
|
-
return new
|
|
8931
|
+
return new aws8.iam.OpenIdConnectProvider("wraps-vercel-oidc", {
|
|
8816
8932
|
url,
|
|
8817
8933
|
clientIdLists: [`https://vercel.com/${config2.teamSlug}`],
|
|
8818
8934
|
thumbprintLists: [
|
|
@@ -8829,7 +8945,7 @@ async function createVercelOIDC(config2) {
|
|
|
8829
8945
|
|
|
8830
8946
|
// src/infrastructure/email-stack.ts
|
|
8831
8947
|
async function deployEmailStack(config2) {
|
|
8832
|
-
const identity = await
|
|
8948
|
+
const identity = await aws11.getCallerIdentity();
|
|
8833
8949
|
const accountId = identity.accountId;
|
|
8834
8950
|
let oidcProvider;
|
|
8835
8951
|
if (config2.provider === "vercel" && config2.vercel) {
|
|
@@ -8934,6 +9050,13 @@ async function deployEmailStack(config2) {
|
|
|
8934
9050
|
region: config2.region
|
|
8935
9051
|
});
|
|
8936
9052
|
}
|
|
9053
|
+
let smtpResources;
|
|
9054
|
+
if (emailConfig.smtpCredentials?.enabled && sesResources) {
|
|
9055
|
+
smtpResources = await createSMTPCredentials({
|
|
9056
|
+
configSetName: "wraps-email-tracking",
|
|
9057
|
+
region: config2.region
|
|
9058
|
+
});
|
|
9059
|
+
}
|
|
8937
9060
|
return {
|
|
8938
9061
|
roleArn: role.arn,
|
|
8939
9062
|
configSetName: sesResources?.configSet.configurationSetName,
|
|
@@ -8953,7 +9076,12 @@ async function deployEmailStack(config2) {
|
|
|
8953
9076
|
mailFromDomain: sesResources?.mailFromDomain,
|
|
8954
9077
|
archiveArn: archiveResources?.archiveArn,
|
|
8955
9078
|
archivingEnabled: emailConfig.emailArchiving?.enabled,
|
|
8956
|
-
archiveRetention: emailConfig.emailArchiving?.enabled ? emailConfig.emailArchiving.retention : void 0
|
|
9079
|
+
archiveRetention: emailConfig.emailArchiving?.enabled ? emailConfig.emailArchiving.retention : void 0,
|
|
9080
|
+
// SMTP credentials (shown once, not stored)
|
|
9081
|
+
smtpUserArn: smtpResources?.iamUser.arn,
|
|
9082
|
+
smtpUsername: smtpResources?.accessKey.id,
|
|
9083
|
+
smtpPassword: smtpResources?.smtpPassword,
|
|
9084
|
+
smtpEndpoint: smtpResources ? `email-smtp.${config2.region}.amazonaws.com` : void 0
|
|
8957
9085
|
};
|
|
8958
9086
|
}
|
|
8959
9087
|
|
|
@@ -10000,13 +10128,13 @@ async function scanLambdaFunctions(region) {
|
|
|
10000
10128
|
}
|
|
10001
10129
|
}
|
|
10002
10130
|
async function scanIAMRoles(region) {
|
|
10003
|
-
const
|
|
10131
|
+
const iam8 = new IAMClient({ region });
|
|
10004
10132
|
const roles = [];
|
|
10005
10133
|
try {
|
|
10006
10134
|
let marker;
|
|
10007
10135
|
let hasMore = true;
|
|
10008
10136
|
while (hasMore) {
|
|
10009
|
-
const listResponse = await
|
|
10137
|
+
const listResponse = await iam8.send(
|
|
10010
10138
|
new ListRolesCommand({
|
|
10011
10139
|
Marker: marker,
|
|
10012
10140
|
MaxItems: 100
|
|
@@ -11901,6 +12029,11 @@ ${pc15.bold("Current Configuration:")}
|
|
|
11901
12029
|
value: "wraps-dashboard",
|
|
11902
12030
|
label: metadata.services.email?.webhookSecret ? "Manage Wraps Dashboard connection" : "Connect to Wraps Dashboard",
|
|
11903
12031
|
hint: metadata.services.email?.webhookSecret ? "Regenerate secret or disconnect" : "Send events to dashboard for analytics"
|
|
12032
|
+
},
|
|
12033
|
+
{
|
|
12034
|
+
value: "smtp-credentials",
|
|
12035
|
+
label: metadata.services.email?.smtpCredentials?.enabled ? "Manage SMTP credentials" : "Enable SMTP credentials",
|
|
12036
|
+
hint: metadata.services.email?.smtpCredentials?.enabled ? "Rotate or disable credentials" : "Generate credentials for PHP, WordPress, etc."
|
|
11904
12037
|
}
|
|
11905
12038
|
]
|
|
11906
12039
|
});
|
|
@@ -12492,6 +12625,98 @@ ${pc15.bold("Webhook Configuration:")}`);
|
|
|
12492
12625
|
newPreset = void 0;
|
|
12493
12626
|
break;
|
|
12494
12627
|
}
|
|
12628
|
+
case "smtp-credentials": {
|
|
12629
|
+
if (metadata.services.email?.smtpCredentials?.enabled) {
|
|
12630
|
+
clack14.log.info(
|
|
12631
|
+
`SMTP credentials are currently ${pc15.green("enabled")} (created ${metadata.services.email.smtpCredentials.createdAt})`
|
|
12632
|
+
);
|
|
12633
|
+
const smtpAction = await clack14.select({
|
|
12634
|
+
message: "What would you like to do?",
|
|
12635
|
+
options: [
|
|
12636
|
+
{
|
|
12637
|
+
value: "rotate",
|
|
12638
|
+
label: "Rotate credentials",
|
|
12639
|
+
hint: "Generate new credentials (invalidates old ones)"
|
|
12640
|
+
},
|
|
12641
|
+
{
|
|
12642
|
+
value: "disable",
|
|
12643
|
+
label: "Disable SMTP credentials",
|
|
12644
|
+
hint: "Delete IAM user and credentials"
|
|
12645
|
+
},
|
|
12646
|
+
{
|
|
12647
|
+
value: "cancel",
|
|
12648
|
+
label: "Cancel",
|
|
12649
|
+
hint: "Keep current credentials"
|
|
12650
|
+
}
|
|
12651
|
+
]
|
|
12652
|
+
});
|
|
12653
|
+
if (clack14.isCancel(smtpAction) || smtpAction === "cancel") {
|
|
12654
|
+
clack14.log.info("No changes made.");
|
|
12655
|
+
process.exit(0);
|
|
12656
|
+
}
|
|
12657
|
+
if (smtpAction === "disable") {
|
|
12658
|
+
const confirmDisable = await clack14.confirm({
|
|
12659
|
+
message: "Are you sure? Any systems using these credentials will stop working immediately.",
|
|
12660
|
+
initialValue: false
|
|
12661
|
+
});
|
|
12662
|
+
if (clack14.isCancel(confirmDisable) || !confirmDisable) {
|
|
12663
|
+
clack14.log.info("SMTP credentials not disabled.");
|
|
12664
|
+
process.exit(0);
|
|
12665
|
+
}
|
|
12666
|
+
updatedConfig = {
|
|
12667
|
+
...config2,
|
|
12668
|
+
smtpCredentials: { enabled: false }
|
|
12669
|
+
};
|
|
12670
|
+
if (metadata.services.email) {
|
|
12671
|
+
metadata.services.email.smtpCredentials = void 0;
|
|
12672
|
+
}
|
|
12673
|
+
newPreset = void 0;
|
|
12674
|
+
break;
|
|
12675
|
+
}
|
|
12676
|
+
clack14.log.info(
|
|
12677
|
+
"\nRotating credentials will invalidate your current SMTP password."
|
|
12678
|
+
);
|
|
12679
|
+
clack14.log.warn("You will need to update all systems using the old credentials.");
|
|
12680
|
+
const confirmRotate = await clack14.confirm({
|
|
12681
|
+
message: "Generate new SMTP credentials?",
|
|
12682
|
+
initialValue: false
|
|
12683
|
+
});
|
|
12684
|
+
if (clack14.isCancel(confirmRotate) || !confirmRotate) {
|
|
12685
|
+
clack14.log.info("Credential rotation cancelled.");
|
|
12686
|
+
process.exit(0);
|
|
12687
|
+
}
|
|
12688
|
+
}
|
|
12689
|
+
clack14.log.info(`
|
|
12690
|
+
${pc15.bold("SMTP Credentials for Legacy Systems")}
|
|
12691
|
+
`);
|
|
12692
|
+
clack14.log.info(pc15.dim("Generate SMTP username/password that works with:"));
|
|
12693
|
+
clack14.log.info(pc15.dim(" - PHP mail() and PHPMailer"));
|
|
12694
|
+
clack14.log.info(pc15.dim(" - WordPress (WP Mail SMTP plugin)"));
|
|
12695
|
+
clack14.log.info(pc15.dim(" - Nodemailer and other SMTP libraries"));
|
|
12696
|
+
clack14.log.info(pc15.dim(" - Any SMTP-compatible email client"));
|
|
12697
|
+
console.log("");
|
|
12698
|
+
clack14.log.warn(
|
|
12699
|
+
"Credentials will be shown ONCE after deployment - save them immediately!"
|
|
12700
|
+
);
|
|
12701
|
+
console.log("");
|
|
12702
|
+
const confirmCreate = await clack14.confirm({
|
|
12703
|
+
message: "Create SMTP credentials?",
|
|
12704
|
+
initialValue: true
|
|
12705
|
+
});
|
|
12706
|
+
if (clack14.isCancel(confirmCreate) || !confirmCreate) {
|
|
12707
|
+
clack14.log.info("SMTP credentials not created.");
|
|
12708
|
+
process.exit(0);
|
|
12709
|
+
}
|
|
12710
|
+
updatedConfig = {
|
|
12711
|
+
...config2,
|
|
12712
|
+
smtpCredentials: {
|
|
12713
|
+
enabled: true,
|
|
12714
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12715
|
+
}
|
|
12716
|
+
};
|
|
12717
|
+
newPreset = void 0;
|
|
12718
|
+
break;
|
|
12719
|
+
}
|
|
12495
12720
|
}
|
|
12496
12721
|
const newCostData = calculateCosts(updatedConfig, 5e4);
|
|
12497
12722
|
const costDiff = newCostData.total.monthly - currentCostData.total.monthly;
|
|
@@ -12643,7 +12868,12 @@ ${pc15.bold("Cost Impact:")}`);
|
|
|
12643
12868
|
acmCertificateValidationRecords: result.acmCertificateValidationRecords,
|
|
12644
12869
|
archiveArn: result.archiveArn,
|
|
12645
12870
|
archivingEnabled: result.archivingEnabled,
|
|
12646
|
-
archiveRetention: result.archiveRetention
|
|
12871
|
+
archiveRetention: result.archiveRetention,
|
|
12872
|
+
// SMTP credentials (shown once)
|
|
12873
|
+
smtpUserArn: result.smtpUserArn,
|
|
12874
|
+
smtpUsername: result.smtpUsername,
|
|
12875
|
+
smtpPassword: result.smtpPassword,
|
|
12876
|
+
smtpEndpoint: result.smtpEndpoint
|
|
12647
12877
|
};
|
|
12648
12878
|
}
|
|
12649
12879
|
},
|
|
@@ -12679,7 +12909,12 @@ ${pc15.bold("Cost Impact:")}`);
|
|
|
12679
12909
|
acmCertificateValidationRecords: pulumiOutputs.acmCertificateValidationRecords?.value,
|
|
12680
12910
|
archiveArn: pulumiOutputs.archiveArn?.value,
|
|
12681
12911
|
archivingEnabled: pulumiOutputs.archivingEnabled?.value,
|
|
12682
|
-
archiveRetention: pulumiOutputs.archiveRetention?.value
|
|
12912
|
+
archiveRetention: pulumiOutputs.archiveRetention?.value,
|
|
12913
|
+
// SMTP credentials (shown once)
|
|
12914
|
+
smtpUserArn: pulumiOutputs.smtpUserArn?.value,
|
|
12915
|
+
smtpUsername: pulumiOutputs.smtpUsername?.value,
|
|
12916
|
+
smtpPassword: pulumiOutputs.smtpPassword?.value,
|
|
12917
|
+
smtpEndpoint: pulumiOutputs.smtpEndpoint?.value
|
|
12683
12918
|
};
|
|
12684
12919
|
}
|
|
12685
12920
|
);
|
|
@@ -12823,6 +13058,31 @@ ${pc15.green("\u2713")} ${pc15.bold("Upgrade complete!")}
|
|
|
12823
13058
|
)
|
|
12824
13059
|
);
|
|
12825
13060
|
}
|
|
13061
|
+
if (upgradeAction === "smtp-credentials" && outputs.smtpUsername && outputs.smtpPassword) {
|
|
13062
|
+
console.log(pc15.bold("\n\u{1F4E7} SMTP Connection Details\n"));
|
|
13063
|
+
console.log(` ${pc15.cyan("Server:")} ${outputs.smtpEndpoint}`);
|
|
13064
|
+
console.log(` ${pc15.cyan("Port:")} 587 (STARTTLS) or 465 (TLS)`);
|
|
13065
|
+
console.log(` ${pc15.cyan("Username:")} ${outputs.smtpUsername}`);
|
|
13066
|
+
console.log(` ${pc15.cyan("Password:")} ${outputs.smtpPassword}`);
|
|
13067
|
+
console.log(` ${pc15.cyan("Encryption:")} TLS/STARTTLS required
|
|
13068
|
+
`);
|
|
13069
|
+
console.log(pc15.yellow("\u26A0\uFE0F IMPORTANT: Save these credentials NOW!"));
|
|
13070
|
+
console.log(pc15.yellow(" They cannot be retrieved later.\n"));
|
|
13071
|
+
console.log(pc15.bold(" Environment Variables:\n"));
|
|
13072
|
+
console.log(pc15.dim(` SMTP_HOST=${outputs.smtpEndpoint}`));
|
|
13073
|
+
console.log(pc15.dim(` SMTP_PORT=587`));
|
|
13074
|
+
console.log(pc15.dim(` SMTP_USER=${outputs.smtpUsername}`));
|
|
13075
|
+
console.log(pc15.dim(` SMTP_PASS=${outputs.smtpPassword}
|
|
13076
|
+
`));
|
|
13077
|
+
if (metadata.services.email && outputs.smtpUserArn) {
|
|
13078
|
+
metadata.services.email.smtpCredentials = {
|
|
13079
|
+
enabled: true,
|
|
13080
|
+
iamUserArn: outputs.smtpUserArn,
|
|
13081
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13082
|
+
};
|
|
13083
|
+
await saveConnectionMetadata(metadata);
|
|
13084
|
+
}
|
|
13085
|
+
}
|
|
12826
13086
|
const enabledFeatures = [];
|
|
12827
13087
|
if (updatedConfig.tracking?.enabled) {
|
|
12828
13088
|
enabledFeatures.push("tracking");
|
|
@@ -12842,6 +13102,9 @@ ${pc15.green("\u2713")} ${pc15.bold("Upgrade complete!")}
|
|
|
12842
13102
|
if (updatedConfig.emailArchiving?.enabled) {
|
|
12843
13103
|
enabledFeatures.push("email_archiving");
|
|
12844
13104
|
}
|
|
13105
|
+
if (updatedConfig.smtpCredentials?.enabled) {
|
|
13106
|
+
enabledFeatures.push("smtp_credentials");
|
|
13107
|
+
}
|
|
12845
13108
|
trackServiceUpgrade("email", {
|
|
12846
13109
|
from_preset: metadata.services.email?.preset,
|
|
12847
13110
|
to_preset: newPreset,
|
|
@@ -12945,10 +13208,10 @@ Run ${pc18.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
12945
13208
|
process.exit(1);
|
|
12946
13209
|
}
|
|
12947
13210
|
const roleName = "wraps-console-access-role";
|
|
12948
|
-
const
|
|
13211
|
+
const iam8 = new IAMClient2({ region: "us-east-1" });
|
|
12949
13212
|
let roleExists4 = false;
|
|
12950
13213
|
try {
|
|
12951
|
-
await
|
|
13214
|
+
await iam8.send(new GetRoleCommand({ RoleName: roleName }));
|
|
12952
13215
|
roleExists4 = true;
|
|
12953
13216
|
} catch (error) {
|
|
12954
13217
|
if (error && typeof error === "object" && "name" in error && error.name !== "NoSuchEntity") {
|
|
@@ -12989,7 +13252,7 @@ Run ${pc18.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
12989
13252
|
const smsEventTracking = smsConfig?.eventTracking;
|
|
12990
13253
|
await progress.execute("Updating IAM role permissions", async () => {
|
|
12991
13254
|
const { PutRolePolicyCommand } = await import("@aws-sdk/client-iam");
|
|
12992
|
-
await
|
|
13255
|
+
await iam8.send(
|
|
12993
13256
|
new PutRolePolicyCommand({
|
|
12994
13257
|
RoleName: roleName,
|
|
12995
13258
|
PolicyName: "wraps-console-access-policy",
|
|
@@ -16014,15 +16277,15 @@ import pc22 from "picocolors";
|
|
|
16014
16277
|
|
|
16015
16278
|
// src/infrastructure/sms-stack.ts
|
|
16016
16279
|
init_esm_shims();
|
|
16017
|
-
import * as
|
|
16280
|
+
import * as aws12 from "@pulumi/aws";
|
|
16018
16281
|
import * as pulumi14 from "@pulumi/pulumi";
|
|
16019
16282
|
async function roleExists2(roleName) {
|
|
16020
16283
|
try {
|
|
16021
16284
|
const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
|
|
16022
|
-
const
|
|
16285
|
+
const iam8 = new IAMClient3({
|
|
16023
16286
|
region: process.env.AWS_REGION || "us-east-1"
|
|
16024
16287
|
});
|
|
16025
|
-
await
|
|
16288
|
+
await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
|
|
16026
16289
|
return true;
|
|
16027
16290
|
} catch (error) {
|
|
16028
16291
|
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
|
|
@@ -16092,7 +16355,7 @@ async function createSMSIAMRole(config2) {
|
|
|
16092
16355
|
}
|
|
16093
16356
|
const roleName = "wraps-sms-role";
|
|
16094
16357
|
const exists = await roleExists2(roleName);
|
|
16095
|
-
const role = exists ? new
|
|
16358
|
+
const role = exists ? new aws12.iam.Role(
|
|
16096
16359
|
roleName,
|
|
16097
16360
|
{
|
|
16098
16361
|
name: roleName,
|
|
@@ -16107,7 +16370,7 @@ async function createSMSIAMRole(config2) {
|
|
|
16107
16370
|
import: roleName,
|
|
16108
16371
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16109
16372
|
}
|
|
16110
|
-
) : new
|
|
16373
|
+
) : new aws12.iam.Role(
|
|
16111
16374
|
roleName,
|
|
16112
16375
|
{
|
|
16113
16376
|
name: roleName,
|
|
@@ -16202,7 +16465,7 @@ async function createSMSIAMRole(config2) {
|
|
|
16202
16465
|
Resource: "arn:aws:logs:*:*:log-group:/aws/lambda/wraps-sms-*"
|
|
16203
16466
|
});
|
|
16204
16467
|
}
|
|
16205
|
-
new
|
|
16468
|
+
new aws12.iam.RolePolicy("wraps-sms-policy", {
|
|
16206
16469
|
role: role.name,
|
|
16207
16470
|
policy: JSON.stringify({
|
|
16208
16471
|
Version: "2012-10-17",
|
|
@@ -16212,7 +16475,7 @@ async function createSMSIAMRole(config2) {
|
|
|
16212
16475
|
return role;
|
|
16213
16476
|
}
|
|
16214
16477
|
function createSMSConfigurationSet() {
|
|
16215
|
-
return new
|
|
16478
|
+
return new aws12.pinpoint.Smsvoicev2ConfigurationSet("wraps-sms-config", {
|
|
16216
16479
|
name: "wraps-sms-config",
|
|
16217
16480
|
defaultMessageType: "TRANSACTIONAL",
|
|
16218
16481
|
tags: {
|
|
@@ -16222,7 +16485,7 @@ function createSMSConfigurationSet() {
|
|
|
16222
16485
|
});
|
|
16223
16486
|
}
|
|
16224
16487
|
function createSMSOptOutList() {
|
|
16225
|
-
return new
|
|
16488
|
+
return new aws12.pinpoint.Smsvoicev2OptOutList("wraps-sms-optouts", {
|
|
16226
16489
|
name: "wraps-sms-optouts",
|
|
16227
16490
|
tags: {
|
|
16228
16491
|
ManagedBy: "wraps-cli",
|
|
@@ -16286,7 +16549,7 @@ async function createSMSPhoneNumber(phoneNumberType, optOutList) {
|
|
|
16286
16549
|
}
|
|
16287
16550
|
};
|
|
16288
16551
|
if (existingArn) {
|
|
16289
|
-
return new
|
|
16552
|
+
return new aws12.pinpoint.Smsvoicev2PhoneNumber(
|
|
16290
16553
|
"wraps-sms-number",
|
|
16291
16554
|
phoneConfig,
|
|
16292
16555
|
{
|
|
@@ -16295,7 +16558,7 @@ async function createSMSPhoneNumber(phoneNumberType, optOutList) {
|
|
|
16295
16558
|
}
|
|
16296
16559
|
);
|
|
16297
16560
|
}
|
|
16298
|
-
return new
|
|
16561
|
+
return new aws12.pinpoint.Smsvoicev2PhoneNumber(
|
|
16299
16562
|
"wraps-sms-number",
|
|
16300
16563
|
phoneConfig,
|
|
16301
16564
|
{
|
|
@@ -16338,10 +16601,10 @@ async function createSMSSQSResources() {
|
|
|
16338
16601
|
Description: "Dead letter queue for failed SMS event processing"
|
|
16339
16602
|
}
|
|
16340
16603
|
};
|
|
16341
|
-
const dlq = dlqUrl ? new
|
|
16604
|
+
const dlq = dlqUrl ? new aws12.sqs.Queue(dlqName, dlqConfig, {
|
|
16342
16605
|
import: dlqUrl,
|
|
16343
16606
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16344
|
-
}) : new
|
|
16607
|
+
}) : new aws12.sqs.Queue(dlqName, dlqConfig, {
|
|
16345
16608
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16346
16609
|
});
|
|
16347
16610
|
const queueConfig = {
|
|
@@ -16364,10 +16627,10 @@ async function createSMSSQSResources() {
|
|
|
16364
16627
|
Description: "Queue for SMS events from SNS"
|
|
16365
16628
|
}
|
|
16366
16629
|
};
|
|
16367
|
-
const queue = queueUrl ? new
|
|
16630
|
+
const queue = queueUrl ? new aws12.sqs.Queue(queueName, queueConfig, {
|
|
16368
16631
|
import: queueUrl,
|
|
16369
16632
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16370
|
-
}) : new
|
|
16633
|
+
}) : new aws12.sqs.Queue(queueName, queueConfig, {
|
|
16371
16634
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16372
16635
|
});
|
|
16373
16636
|
return { queue, dlq };
|
|
@@ -16407,13 +16670,13 @@ async function createSMSSNSResources(config2) {
|
|
|
16407
16670
|
Description: "SNS topic for SMS delivery events"
|
|
16408
16671
|
}
|
|
16409
16672
|
};
|
|
16410
|
-
const topic = topicArn ? new
|
|
16673
|
+
const topic = topicArn ? new aws12.sns.Topic("wraps-sms-events-topic", topicConfig, {
|
|
16411
16674
|
import: topicArn,
|
|
16412
16675
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16413
|
-
}) : new
|
|
16676
|
+
}) : new aws12.sns.Topic("wraps-sms-events-topic", topicConfig, {
|
|
16414
16677
|
customTimeouts: { create: "2m", update: "2m", delete: "2m" }
|
|
16415
16678
|
});
|
|
16416
|
-
new
|
|
16679
|
+
new aws12.sns.TopicPolicy("wraps-sms-events-topic-policy", {
|
|
16417
16680
|
arn: topic.arn,
|
|
16418
16681
|
policy: topic.arn.apply(
|
|
16419
16682
|
(topicArn2) => JSON.stringify({
|
|
@@ -16430,7 +16693,7 @@ async function createSMSSNSResources(config2) {
|
|
|
16430
16693
|
})
|
|
16431
16694
|
)
|
|
16432
16695
|
});
|
|
16433
|
-
new
|
|
16696
|
+
new aws12.sqs.QueuePolicy("wraps-sms-events-queue-policy", {
|
|
16434
16697
|
queueUrl: config2.queueUrl,
|
|
16435
16698
|
policy: pulumi14.all([config2.queueArn, topic.arn]).apply(
|
|
16436
16699
|
([queueArn, topicArn2]) => JSON.stringify({
|
|
@@ -16449,7 +16712,7 @@ async function createSMSSNSResources(config2) {
|
|
|
16449
16712
|
})
|
|
16450
16713
|
)
|
|
16451
16714
|
});
|
|
16452
|
-
const subscription = new
|
|
16715
|
+
const subscription = new aws12.sns.TopicSubscription(
|
|
16453
16716
|
"wraps-sms-events-subscription",
|
|
16454
16717
|
{
|
|
16455
16718
|
topic: topic.arn,
|
|
@@ -16498,17 +16761,17 @@ async function createSMSDynamoDBTable() {
|
|
|
16498
16761
|
Service: "sms"
|
|
16499
16762
|
}
|
|
16500
16763
|
};
|
|
16501
|
-
return exists ? new
|
|
16764
|
+
return exists ? new aws12.dynamodb.Table(tableName, tableConfig, {
|
|
16502
16765
|
import: tableName,
|
|
16503
16766
|
customTimeouts: { create: "5m", update: "5m", delete: "5m" }
|
|
16504
|
-
}) : new
|
|
16767
|
+
}) : new aws12.dynamodb.Table(tableName, tableConfig, {
|
|
16505
16768
|
customTimeouts: { create: "5m", update: "5m", delete: "5m" }
|
|
16506
16769
|
});
|
|
16507
16770
|
}
|
|
16508
16771
|
async function deploySMSLambdaFunction(config2) {
|
|
16509
16772
|
const { getLambdaCode: getLambdaCode2 } = await Promise.resolve().then(() => (init_lambda(), lambda_exports));
|
|
16510
16773
|
const codeDir = await getLambdaCode2("sms-event-processor");
|
|
16511
|
-
const lambdaRole = new
|
|
16774
|
+
const lambdaRole = new aws12.iam.Role("wraps-sms-lambda-role", {
|
|
16512
16775
|
name: "wraps-sms-lambda-role",
|
|
16513
16776
|
assumeRolePolicy: JSON.stringify({
|
|
16514
16777
|
Version: "2012-10-17",
|
|
@@ -16525,11 +16788,11 @@ async function deploySMSLambdaFunction(config2) {
|
|
|
16525
16788
|
Service: "sms"
|
|
16526
16789
|
}
|
|
16527
16790
|
});
|
|
16528
|
-
new
|
|
16791
|
+
new aws12.iam.RolePolicyAttachment("wraps-sms-lambda-basic-execution", {
|
|
16529
16792
|
role: lambdaRole.name,
|
|
16530
16793
|
policyArn: "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
16531
16794
|
});
|
|
16532
|
-
new
|
|
16795
|
+
new aws12.iam.RolePolicy("wraps-sms-lambda-policy", {
|
|
16533
16796
|
role: lambdaRole.name,
|
|
16534
16797
|
policy: pulumi14.all([config2.tableName, config2.queueArn]).apply(
|
|
16535
16798
|
([tableName, queueArn]) => JSON.stringify({
|
|
@@ -16561,7 +16824,7 @@ async function deploySMSLambdaFunction(config2) {
|
|
|
16561
16824
|
})
|
|
16562
16825
|
)
|
|
16563
16826
|
});
|
|
16564
|
-
const eventProcessor = new
|
|
16827
|
+
const eventProcessor = new aws12.lambda.Function(
|
|
16565
16828
|
"wraps-sms-event-processor",
|
|
16566
16829
|
{
|
|
16567
16830
|
name: "wraps-sms-event-processor",
|
|
@@ -16588,7 +16851,7 @@ async function deploySMSLambdaFunction(config2) {
|
|
|
16588
16851
|
customTimeouts: { create: "5m", update: "5m", delete: "2m" }
|
|
16589
16852
|
}
|
|
16590
16853
|
);
|
|
16591
|
-
new
|
|
16854
|
+
new aws12.lambda.EventSourceMapping(
|
|
16592
16855
|
"wraps-sms-event-source-mapping",
|
|
16593
16856
|
{
|
|
16594
16857
|
eventSourceArn: config2.queueArn,
|
|
@@ -16604,7 +16867,7 @@ async function deploySMSLambdaFunction(config2) {
|
|
|
16604
16867
|
return eventProcessor;
|
|
16605
16868
|
}
|
|
16606
16869
|
async function deploySMSStack(config2) {
|
|
16607
|
-
const identity = await
|
|
16870
|
+
const identity = await aws12.getCallerIdentity();
|
|
16608
16871
|
const accountId = identity.accountId;
|
|
16609
16872
|
let oidcProvider;
|
|
16610
16873
|
if (config2.provider === "vercel" && config2.vercel) {
|
|
@@ -20152,12 +20415,12 @@ import pc31 from "picocolors";
|
|
|
20152
20415
|
|
|
20153
20416
|
// src/infrastructure/storage-stack.ts
|
|
20154
20417
|
init_esm_shims();
|
|
20155
|
-
import * as
|
|
20418
|
+
import * as aws14 from "@pulumi/aws";
|
|
20156
20419
|
import * as pulumi22 from "@pulumi/pulumi";
|
|
20157
20420
|
|
|
20158
20421
|
// src/infrastructure/resources/s3-storage.ts
|
|
20159
20422
|
init_esm_shims();
|
|
20160
|
-
import * as
|
|
20423
|
+
import * as aws13 from "@pulumi/aws";
|
|
20161
20424
|
import * as pulumi21 from "@pulumi/pulumi";
|
|
20162
20425
|
function retentionToDays(retention) {
|
|
20163
20426
|
switch (retention) {
|
|
@@ -20179,7 +20442,7 @@ function retentionToDays(retention) {
|
|
|
20179
20442
|
}
|
|
20180
20443
|
async function createStorageBucket(config2) {
|
|
20181
20444
|
const bucketName = config2.storageConfig.bucketName || `wraps-storage-${config2.accountId}`;
|
|
20182
|
-
const bucket = new
|
|
20445
|
+
const bucket = new aws13.s3.BucketV2("wraps-storage-bucket", {
|
|
20183
20446
|
bucket: bucketName,
|
|
20184
20447
|
tags: {
|
|
20185
20448
|
ManagedBy: "wraps-cli",
|
|
@@ -20187,14 +20450,14 @@ async function createStorageBucket(config2) {
|
|
|
20187
20450
|
}
|
|
20188
20451
|
});
|
|
20189
20452
|
if (config2.storageConfig.versioning) {
|
|
20190
|
-
new
|
|
20453
|
+
new aws13.s3.BucketVersioningV2("wraps-storage-versioning", {
|
|
20191
20454
|
bucket: bucket.id,
|
|
20192
20455
|
versioningConfiguration: {
|
|
20193
20456
|
status: "Enabled"
|
|
20194
20457
|
}
|
|
20195
20458
|
});
|
|
20196
20459
|
}
|
|
20197
|
-
new
|
|
20460
|
+
new aws13.s3.BucketServerSideEncryptionConfigurationV2(
|
|
20198
20461
|
"wraps-storage-encryption",
|
|
20199
20462
|
{
|
|
20200
20463
|
bucket: bucket.id,
|
|
@@ -20220,7 +20483,7 @@ async function createStorageBucket(config2) {
|
|
|
20220
20483
|
"http://localhost:8080",
|
|
20221
20484
|
...config2.storageConfig.additionalOrigins || []
|
|
20222
20485
|
];
|
|
20223
|
-
new
|
|
20486
|
+
new aws13.s3.BucketCorsConfigurationV2("wraps-storage-cors", {
|
|
20224
20487
|
bucket: bucket.id,
|
|
20225
20488
|
corsRules: [
|
|
20226
20489
|
{
|
|
@@ -20234,7 +20497,7 @@ async function createStorageBucket(config2) {
|
|
|
20234
20497
|
});
|
|
20235
20498
|
const retentionDays = config2.storageConfig.retention ? retentionToDays(config2.storageConfig.retention) : null;
|
|
20236
20499
|
if (retentionDays) {
|
|
20237
|
-
new
|
|
20500
|
+
new aws13.s3.BucketLifecycleConfigurationV2("wraps-storage-lifecycle", {
|
|
20238
20501
|
bucket: bucket.id,
|
|
20239
20502
|
rules: [
|
|
20240
20503
|
{
|
|
@@ -20247,7 +20510,7 @@ async function createStorageBucket(config2) {
|
|
|
20247
20510
|
]
|
|
20248
20511
|
});
|
|
20249
20512
|
}
|
|
20250
|
-
new
|
|
20513
|
+
new aws13.s3.BucketPublicAccessBlock("wraps-storage-public-access", {
|
|
20251
20514
|
bucket: bucket.id,
|
|
20252
20515
|
blockPublicAcls: true,
|
|
20253
20516
|
blockPublicPolicy: true,
|
|
@@ -20261,10 +20524,10 @@ async function createStorageBucket(config2) {
|
|
|
20261
20524
|
};
|
|
20262
20525
|
}
|
|
20263
20526
|
async function createStorageWAF() {
|
|
20264
|
-
const usEast1Provider = new
|
|
20527
|
+
const usEast1Provider = new aws13.Provider("storage-waf-us-east-1", {
|
|
20265
20528
|
region: "us-east-1"
|
|
20266
20529
|
});
|
|
20267
|
-
const webAcl = new
|
|
20530
|
+
const webAcl = new aws13.wafv2.WebAcl(
|
|
20268
20531
|
"wraps-storage-waf",
|
|
20269
20532
|
{
|
|
20270
20533
|
scope: "CLOUDFRONT",
|
|
@@ -20316,7 +20579,7 @@ async function createStorageWAF() {
|
|
|
20316
20579
|
}
|
|
20317
20580
|
async function createStorageCDN(config2) {
|
|
20318
20581
|
const webAcl = config2.wafEnabled ? await createStorageWAF() : void 0;
|
|
20319
|
-
const oac = new
|
|
20582
|
+
const oac = new aws13.cloudfront.OriginAccessControl("wraps-storage-oac", {
|
|
20320
20583
|
name: "wraps-storage-oac",
|
|
20321
20584
|
description: "OAC for Wraps storage S3 bucket",
|
|
20322
20585
|
originAccessControlOriginType: "s3",
|
|
@@ -20351,7 +20614,7 @@ async function createStorageCDN(config2) {
|
|
|
20351
20614
|
} : {
|
|
20352
20615
|
restrictionType: "none"
|
|
20353
20616
|
};
|
|
20354
|
-
const distribution = new
|
|
20617
|
+
const distribution = new aws13.cloudfront.Distribution("wraps-storage-cdn", {
|
|
20355
20618
|
enabled: true,
|
|
20356
20619
|
comment: "Wraps storage CDN",
|
|
20357
20620
|
aliases,
|
|
@@ -20394,7 +20657,7 @@ async function createStorageCDN(config2) {
|
|
|
20394
20657
|
Service: "storage"
|
|
20395
20658
|
}
|
|
20396
20659
|
});
|
|
20397
|
-
new
|
|
20660
|
+
new aws13.s3.BucketPolicy("wraps-storage-bucket-policy", {
|
|
20398
20661
|
bucket: config2.bucket.id,
|
|
20399
20662
|
policy: pulumi21.interpolate`{
|
|
20400
20663
|
"Version": "2012-10-17",
|
|
@@ -20423,10 +20686,10 @@ async function createStorageCDN(config2) {
|
|
|
20423
20686
|
};
|
|
20424
20687
|
}
|
|
20425
20688
|
async function createStorageACMCertificate(config2) {
|
|
20426
|
-
const usEast1Provider = new
|
|
20689
|
+
const usEast1Provider = new aws13.Provider("storage-acm-us-east-1", {
|
|
20427
20690
|
region: "us-east-1"
|
|
20428
20691
|
});
|
|
20429
|
-
const certificate = new
|
|
20692
|
+
const certificate = new aws13.acm.Certificate(
|
|
20430
20693
|
"wraps-storage-cert",
|
|
20431
20694
|
{
|
|
20432
20695
|
domainName: config2.domain,
|
|
@@ -20450,7 +20713,7 @@ async function createStorageACMCertificate(config2) {
|
|
|
20450
20713
|
);
|
|
20451
20714
|
let certificateValidation;
|
|
20452
20715
|
if (config2.hostedZoneId) {
|
|
20453
|
-
const validationRecord = new
|
|
20716
|
+
const validationRecord = new aws13.route53.Record(
|
|
20454
20717
|
"wraps-storage-cert-validation",
|
|
20455
20718
|
{
|
|
20456
20719
|
zoneId: config2.hostedZoneId,
|
|
@@ -20460,7 +20723,7 @@ async function createStorageACMCertificate(config2) {
|
|
|
20460
20723
|
ttl: 60
|
|
20461
20724
|
}
|
|
20462
20725
|
);
|
|
20463
|
-
certificateValidation = new
|
|
20726
|
+
certificateValidation = new aws13.acm.CertificateValidation(
|
|
20464
20727
|
"wraps-storage-cert-validation-waiter",
|
|
20465
20728
|
{
|
|
20466
20729
|
certificateArn: certificate.arn,
|
|
@@ -20482,10 +20745,10 @@ async function createStorageACMCertificate(config2) {
|
|
|
20482
20745
|
async function roleExists3(roleName) {
|
|
20483
20746
|
try {
|
|
20484
20747
|
const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
|
|
20485
|
-
const
|
|
20748
|
+
const iam8 = new IAMClient3({
|
|
20486
20749
|
region: process.env.AWS_REGION || "us-east-1"
|
|
20487
20750
|
});
|
|
20488
|
-
await
|
|
20751
|
+
await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
|
|
20489
20752
|
return true;
|
|
20490
20753
|
} catch (error) {
|
|
20491
20754
|
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
|
|
@@ -20532,7 +20795,7 @@ async function createStorageIAMRole(config2) {
|
|
|
20532
20795
|
}
|
|
20533
20796
|
const roleName = "wraps-storage-role";
|
|
20534
20797
|
const exists = await roleExists3(roleName);
|
|
20535
|
-
const role = exists ? new
|
|
20798
|
+
const role = exists ? new aws14.iam.Role(
|
|
20536
20799
|
roleName,
|
|
20537
20800
|
{
|
|
20538
20801
|
name: roleName,
|
|
@@ -20546,7 +20809,7 @@ async function createStorageIAMRole(config2) {
|
|
|
20546
20809
|
{
|
|
20547
20810
|
import: roleName
|
|
20548
20811
|
}
|
|
20549
|
-
) : new
|
|
20812
|
+
) : new aws14.iam.Role(roleName, {
|
|
20550
20813
|
name: roleName,
|
|
20551
20814
|
assumeRolePolicy,
|
|
20552
20815
|
tags: {
|
|
@@ -20584,7 +20847,7 @@ async function createStorageIAMRole(config2) {
|
|
|
20584
20847
|
Resource: config2.distributionArn
|
|
20585
20848
|
});
|
|
20586
20849
|
}
|
|
20587
|
-
new
|
|
20850
|
+
new aws14.iam.RolePolicy("wraps-storage-policy", {
|
|
20588
20851
|
role: role.name,
|
|
20589
20852
|
policy: pulumi22.all([statements]).apply(
|
|
20590
20853
|
([stmts]) => JSON.stringify({
|