@jaypie/constructs 1.1.63 → 1.1.65

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.
Files changed (131) hide show
  1. package/package.json +6 -6
  2. package/dist/cjs/JaypieAccountLoggingBucket.d.ts +0 -60
  3. package/dist/cjs/JaypieApiGateway.d.ts +0 -47
  4. package/dist/cjs/JaypieAppStack.d.ts +0 -5
  5. package/dist/cjs/JaypieBucketQueuedLambda.d.ts +0 -48
  6. package/dist/cjs/JaypieDatadogBucket.d.ts +0 -55
  7. package/dist/cjs/JaypieDatadogForwarder.d.ts +0 -76
  8. package/dist/cjs/JaypieDatadogSecret.d.ts +0 -5
  9. package/dist/cjs/JaypieDistribution.d.ts +0 -76
  10. package/dist/cjs/JaypieDnsRecord.d.ts +0 -45
  11. package/dist/cjs/JaypieEnvSecret.d.ts +0 -40
  12. package/dist/cjs/JaypieEventsRule.d.ts +0 -45
  13. package/dist/cjs/JaypieExpressLambda.d.ts +0 -5
  14. package/dist/cjs/JaypieGitHubDeployRole.d.ts +0 -14
  15. package/dist/cjs/JaypieHostedZone.d.ts +0 -59
  16. package/dist/cjs/JaypieInfrastructureStack.d.ts +0 -5
  17. package/dist/cjs/JaypieLambda.d.ts +0 -100
  18. package/dist/cjs/JaypieMongoDbSecret.d.ts +0 -5
  19. package/dist/cjs/JaypieNextJs.d.ts +0 -18
  20. package/dist/cjs/JaypieNextJs.test.d.ts +0 -1
  21. package/dist/cjs/JaypieOpenAiSecret.d.ts +0 -5
  22. package/dist/cjs/JaypieOrganizationTrail.d.ts +0 -62
  23. package/dist/cjs/JaypieQueuedLambda.d.ts +0 -73
  24. package/dist/cjs/JaypieSsoPermissions.d.ts +0 -96
  25. package/dist/cjs/JaypieSsoSyncApplication.d.ts +0 -27
  26. package/dist/cjs/JaypieStack.d.ts +0 -8
  27. package/dist/cjs/JaypieStaticWebBucket.d.ts +0 -22
  28. package/dist/cjs/JaypieTraceSigningKeySecret.d.ts +0 -5
  29. package/dist/cjs/JaypieWebDeploymentBucket.d.ts +0 -84
  30. package/dist/cjs/__tests__/JaypieBucketQueuedLambda.spec.d.ts +0 -1
  31. package/dist/cjs/__tests__/JaypieDistribution.spec.d.ts +0 -1
  32. package/dist/cjs/__tests__/JaypieDnsRecord.spec.d.ts +0 -1
  33. package/dist/cjs/__tests__/JaypieEnvSecret.spec.d.ts +0 -1
  34. package/dist/cjs/__tests__/JaypieExpressLambda.spec.d.ts +0 -1
  35. package/dist/cjs/__tests__/JaypieHostedZone.spec.d.ts +0 -1
  36. package/dist/cjs/__tests__/JaypieLambda.spec.d.ts +0 -1
  37. package/dist/cjs/__tests__/JaypieQueuedLambda.spec.d.ts +0 -1
  38. package/dist/cjs/__tests__/JaypieSsoPermissions.spec.d.ts +0 -1
  39. package/dist/cjs/__tests__/JaypieSsoSyncApplication.spec.d.ts +0 -1
  40. package/dist/cjs/__tests__/JaypieStaticWebBucket.spec.d.ts +0 -1
  41. package/dist/cjs/__tests__/index.spec.d.ts +0 -1
  42. package/dist/cjs/constants.d.ts +0 -151
  43. package/dist/cjs/helpers/__tests__/envHostname.spec.d.ts +0 -1
  44. package/dist/cjs/helpers/__tests__/jaypieLambdaEnv.spec.d.ts +0 -1
  45. package/dist/cjs/helpers/__tests__/resolveDatadogForwarderFunction.spec.d.ts +0 -1
  46. package/dist/cjs/helpers/__tests__/resolveDatadogLoggingDestination.spec.d.ts +0 -1
  47. package/dist/cjs/helpers/addDatadogLayers.d.ts +0 -5
  48. package/dist/cjs/helpers/constructEnvName.d.ts +0 -5
  49. package/dist/cjs/helpers/constructStackName.d.ts +0 -1
  50. package/dist/cjs/helpers/constructTagger.d.ts +0 -4
  51. package/dist/cjs/helpers/envHostname.d.ts +0 -6
  52. package/dist/cjs/helpers/extendDatadogRole.d.ts +0 -31
  53. package/dist/cjs/helpers/index.d.ts +0 -16
  54. package/dist/cjs/helpers/isEnv.d.ts +0 -12
  55. package/dist/cjs/helpers/isValidHostname.d.ts +0 -1
  56. package/dist/cjs/helpers/isValidSubdomain.d.ts +0 -1
  57. package/dist/cjs/helpers/jaypieLambdaEnv.d.ts +0 -8
  58. package/dist/cjs/helpers/mergeDomain.d.ts +0 -1
  59. package/dist/cjs/helpers/resolveDatadogForwarderFunction.d.ts +0 -7
  60. package/dist/cjs/helpers/resolveDatadogLayers.d.ts +0 -7
  61. package/dist/cjs/helpers/resolveDatadogLoggingDestination.d.ts +0 -4
  62. package/dist/cjs/helpers/resolveHostedZone.d.ts +0 -6
  63. package/dist/cjs/helpers/resolveParamsAndSecrets.d.ts +0 -13
  64. package/dist/cjs/index.cjs +0 -3322
  65. package/dist/cjs/index.cjs.map +0 -1
  66. package/dist/cjs/index.d.ts +0 -29
  67. package/dist/esm/JaypieAccountLoggingBucket.d.ts +0 -60
  68. package/dist/esm/JaypieApiGateway.d.ts +0 -47
  69. package/dist/esm/JaypieAppStack.d.ts +0 -5
  70. package/dist/esm/JaypieBucketQueuedLambda.d.ts +0 -48
  71. package/dist/esm/JaypieDatadogBucket.d.ts +0 -55
  72. package/dist/esm/JaypieDatadogForwarder.d.ts +0 -76
  73. package/dist/esm/JaypieDatadogSecret.d.ts +0 -5
  74. package/dist/esm/JaypieDistribution.d.ts +0 -76
  75. package/dist/esm/JaypieDnsRecord.d.ts +0 -45
  76. package/dist/esm/JaypieEnvSecret.d.ts +0 -40
  77. package/dist/esm/JaypieEventsRule.d.ts +0 -45
  78. package/dist/esm/JaypieExpressLambda.d.ts +0 -5
  79. package/dist/esm/JaypieGitHubDeployRole.d.ts +0 -14
  80. package/dist/esm/JaypieHostedZone.d.ts +0 -59
  81. package/dist/esm/JaypieInfrastructureStack.d.ts +0 -5
  82. package/dist/esm/JaypieLambda.d.ts +0 -100
  83. package/dist/esm/JaypieMongoDbSecret.d.ts +0 -5
  84. package/dist/esm/JaypieNextJs.d.ts +0 -18
  85. package/dist/esm/JaypieNextJs.test.d.ts +0 -1
  86. package/dist/esm/JaypieOpenAiSecret.d.ts +0 -5
  87. package/dist/esm/JaypieOrganizationTrail.d.ts +0 -62
  88. package/dist/esm/JaypieQueuedLambda.d.ts +0 -73
  89. package/dist/esm/JaypieSsoPermissions.d.ts +0 -96
  90. package/dist/esm/JaypieSsoSyncApplication.d.ts +0 -27
  91. package/dist/esm/JaypieStack.d.ts +0 -8
  92. package/dist/esm/JaypieStaticWebBucket.d.ts +0 -22
  93. package/dist/esm/JaypieTraceSigningKeySecret.d.ts +0 -5
  94. package/dist/esm/JaypieWebDeploymentBucket.d.ts +0 -84
  95. package/dist/esm/__tests__/JaypieBucketQueuedLambda.spec.d.ts +0 -1
  96. package/dist/esm/__tests__/JaypieDistribution.spec.d.ts +0 -1
  97. package/dist/esm/__tests__/JaypieDnsRecord.spec.d.ts +0 -1
  98. package/dist/esm/__tests__/JaypieEnvSecret.spec.d.ts +0 -1
  99. package/dist/esm/__tests__/JaypieExpressLambda.spec.d.ts +0 -1
  100. package/dist/esm/__tests__/JaypieHostedZone.spec.d.ts +0 -1
  101. package/dist/esm/__tests__/JaypieLambda.spec.d.ts +0 -1
  102. package/dist/esm/__tests__/JaypieQueuedLambda.spec.d.ts +0 -1
  103. package/dist/esm/__tests__/JaypieSsoPermissions.spec.d.ts +0 -1
  104. package/dist/esm/__tests__/JaypieSsoSyncApplication.spec.d.ts +0 -1
  105. package/dist/esm/__tests__/JaypieStaticWebBucket.spec.d.ts +0 -1
  106. package/dist/esm/__tests__/index.spec.d.ts +0 -1
  107. package/dist/esm/constants.d.ts +0 -151
  108. package/dist/esm/helpers/__tests__/envHostname.spec.d.ts +0 -1
  109. package/dist/esm/helpers/__tests__/jaypieLambdaEnv.spec.d.ts +0 -1
  110. package/dist/esm/helpers/__tests__/resolveDatadogForwarderFunction.spec.d.ts +0 -1
  111. package/dist/esm/helpers/__tests__/resolveDatadogLoggingDestination.spec.d.ts +0 -1
  112. package/dist/esm/helpers/addDatadogLayers.d.ts +0 -5
  113. package/dist/esm/helpers/constructEnvName.d.ts +0 -5
  114. package/dist/esm/helpers/constructStackName.d.ts +0 -1
  115. package/dist/esm/helpers/constructTagger.d.ts +0 -4
  116. package/dist/esm/helpers/envHostname.d.ts +0 -6
  117. package/dist/esm/helpers/extendDatadogRole.d.ts +0 -31
  118. package/dist/esm/helpers/index.d.ts +0 -16
  119. package/dist/esm/helpers/isEnv.d.ts +0 -12
  120. package/dist/esm/helpers/isValidHostname.d.ts +0 -1
  121. package/dist/esm/helpers/isValidSubdomain.d.ts +0 -1
  122. package/dist/esm/helpers/jaypieLambdaEnv.d.ts +0 -8
  123. package/dist/esm/helpers/mergeDomain.d.ts +0 -1
  124. package/dist/esm/helpers/resolveDatadogForwarderFunction.d.ts +0 -7
  125. package/dist/esm/helpers/resolveDatadogLayers.d.ts +0 -7
  126. package/dist/esm/helpers/resolveDatadogLoggingDestination.d.ts +0 -4
  127. package/dist/esm/helpers/resolveHostedZone.d.ts +0 -6
  128. package/dist/esm/helpers/resolveParamsAndSecrets.d.ts +0 -13
  129. package/dist/esm/index.d.ts +0 -29
  130. package/dist/esm/index.js +0 -3246
  131. package/dist/esm/index.js.map +0 -1
@@ -1,3322 +0,0 @@
1
- 'use strict';
2
-
3
- var cdk = require('aws-cdk-lib');
4
- var s3 = require('aws-cdk-lib/aws-s3');
5
- var constructs = require('constructs');
6
- var acm = require('aws-cdk-lib/aws-certificatemanager');
7
- var apiGateway = require('aws-cdk-lib/aws-apigateway');
8
- var route53 = require('aws-cdk-lib/aws-route53');
9
- var route53Targets = require('aws-cdk-lib/aws-route53-targets');
10
- var secretsmanager = require('aws-cdk-lib/aws-secretsmanager');
11
- var datadogCdkConstructsV2 = require('datadog-cdk-constructs-v2');
12
- var errors = require('@jaypie/errors');
13
- var awsIam = require('aws-cdk-lib/aws-iam');
14
- var lambda = require('aws-cdk-lib/aws-lambda');
15
- var logDestinations = require('aws-cdk-lib/aws-logs-destinations');
16
- var s3n = require('aws-cdk-lib/aws-s3-notifications');
17
- var sqs = require('aws-cdk-lib/aws-sqs');
18
- var lambdaEventSources = require('aws-cdk-lib/aws-lambda-event-sources');
19
- var logs = require('aws-cdk-lib/aws-logs');
20
- var awsEvents = require('aws-cdk-lib/aws-events');
21
- var awsEventsTargets = require('aws-cdk-lib/aws-events-targets');
22
- var cloudfront = require('aws-cdk-lib/aws-cloudfront');
23
- var origins = require('aws-cdk-lib/aws-cloudfront-origins');
24
- var cdkNextjsStandalone = require('cdk-nextjs-standalone');
25
- var path = require('path');
26
- var awsCloudtrail = require('aws-cdk-lib/aws-cloudtrail');
27
- var awsSso = require('aws-cdk-lib/aws-sso');
28
- var awsSam = require('aws-cdk-lib/aws-sam');
29
-
30
- function _interopNamespaceDefault(e) {
31
- var n = Object.create(null);
32
- if (e) {
33
- Object.keys(e).forEach(function (k) {
34
- if (k !== 'default') {
35
- var d = Object.getOwnPropertyDescriptor(e, k);
36
- Object.defineProperty(n, k, d.get ? d : {
37
- enumerable: true,
38
- get: function () { return e[k]; }
39
- });
40
- }
41
- });
42
- }
43
- n.default = e;
44
- return Object.freeze(n);
45
- }
46
-
47
- var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
48
- var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
49
- var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
50
- var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
51
- var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
52
- var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Targets);
53
- var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
54
- var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
55
- var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
56
- var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
57
- var sqs__namespace = /*#__PURE__*/_interopNamespaceDefault(sqs);
58
- var lambdaEventSources__namespace = /*#__PURE__*/_interopNamespaceDefault(lambdaEventSources);
59
- var logs__namespace = /*#__PURE__*/_interopNamespaceDefault(logs);
60
- var cloudfront__namespace = /*#__PURE__*/_interopNamespaceDefault(cloudfront);
61
- var origins__namespace = /*#__PURE__*/_interopNamespaceDefault(origins);
62
- var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
63
-
64
- const CDK$2 = {
65
- ACCOUNT: {
66
- DEVELOPMENT: "development",
67
- MANAGEMENT: "management",
68
- OPERATIONS: "operations",
69
- PRODUCTION: "production",
70
- SANDBOX: "sandbox",
71
- SECURITY: "security",
72
- STAGE: "stage",
73
- },
74
- BUILD: {
75
- CONFIG: {
76
- ALL: "all",
77
- API: "api",
78
- INFRASTRUCTURE: "infrastructure",
79
- NONE: "none",
80
- WEB: "web",
81
- },
82
- PERSONAL: "personal",
83
- /**
84
- * @deprecated rename "ephemeral" to "personal" (since 2/24/2025)
85
- */
86
- EPHEMERAL: "ephemeral",
87
- /**
88
- * @deprecated as even "ephemeral" builds have static assets (since 7/6/2024)
89
- */
90
- STATIC: "static",
91
- },
92
- CREATION: {
93
- CDK: "cdk",
94
- CLOUDFORMATION_TEMPLATE: "template",
95
- MANUAL: "manual",
96
- },
97
- DATADOG: {
98
- SITE: "datadoghq.com",
99
- LAYER: {
100
- // https://docs.datadoghq.com/serverless/aws_lambda/installation/nodejs/?tab=awscdk
101
- NODE: 127, // 127 on 9/12/2025
102
- EXTENSION: 86, // 86 on 9/12/2025
103
- },
104
- },
105
- DEFAULT: {
106
- REGION: "us-east-1",
107
- },
108
- DNS: {
109
- CONFIG: {
110
- TTL: 300, // 5 minutes in seconds for Route53
111
- },
112
- RECORD: {
113
- A: "A",
114
- CNAME: "CNAME",
115
- MX: "MX",
116
- NS: "NS",
117
- TXT: "TXT",
118
- },
119
- },
120
- DURATION: {
121
- EXPRESS_API: 30,
122
- LAMBDA_MAXIMUM: 900,
123
- LAMBDA_WORKER: 900,
124
- },
125
- ENV: {
126
- DEMO: "demo", // Mirror of production
127
- DEVELOPMENT: "development", // Internal most stable development space
128
- /** @deprecated */ EPHEMERAL: "ephemeral", // Alias for "build"
129
- LOCAL: "local",
130
- /** @deprecated */ MAIN: "main", // Alias for development
131
- META: "meta", // For non-environment/infrastructure stacks
132
- PERSONAL: "personal", // Personal builds using resources provided by sandbox
133
- PREVIEW: "preview", // External next thing to be released
134
- PRODUCTION: "production",
135
- RELEASE: "release", // Internal next thing to be released
136
- REVIEW: "review", // Internal place to collaborate on issues
137
- SANDBOX: "sandbox", // Internal build space with no guaranteed longevity
138
- TRAINING: "training", // aka "test"; mirror of production for external audiences
139
- },
140
- HOST: {
141
- APEX: "@",
142
- },
143
- IMPORT: {
144
- DATADOG_LOG_FORWARDER: "account-datadog-forwarder",
145
- DATADOG_ROLE: "account-datadog-role",
146
- DATADOG_SECRET: "account-datadog-secret",
147
- LOG_BUCKET: "account-log-bucket",
148
- OIDC_PROVIDER: "github-oidc-provider",
149
- },
150
- LAMBDA: {
151
- LOG_RETENTION: 90,
152
- MEMORY_SIZE: 1024,
153
- },
154
- PRINCIPAL: {
155
- ROUTE53: "route53.amazonaws.com",
156
- },
157
- PRINCIPAL_TYPE: {
158
- GROUP: "GROUP",
159
- USER: "USER",
160
- },
161
- PROJECT: {
162
- INFRASTRUCTURE: "infrastructure",
163
- },
164
- ROLE: {
165
- API: "api",
166
- DEPLOY: "deploy",
167
- HOSTING: "hosting",
168
- MONITORING: "monitoring",
169
- NETWORKING: "networking",
170
- PROCESSING: "processing",
171
- SECURITY: "security",
172
- STACK: "stack",
173
- STORAGE: "storage",
174
- TOY: "toy",
175
- },
176
- SERVICE: {
177
- DATADOG: "datadog",
178
- INFRASTRUCTURE: "infrastructure",
179
- LIBRARIES: "libraries",
180
- NONE: "none",
181
- SSO: "sso",
182
- TRACE: "trace",
183
- },
184
- TAG: {
185
- BUILD_DATE: "buildDate",
186
- BUILD_HEX: "buildHex",
187
- BUILD_NUMBER: "buildNumber",
188
- BUILD_TIME: "buildTime",
189
- BUILD_TYPE: "buildType",
190
- COMMIT: "commit",
191
- CREATION: "creation",
192
- ENV: "env",
193
- NONCE: "nonce",
194
- PROJECT: "project",
195
- ROLE: "role",
196
- SERVICE: "service",
197
- SPONSOR: "sponsor",
198
- STACK: "stack",
199
- STACK_SHA: "stackSha",
200
- VENDOR: "vendor",
201
- VERSION: "version",
202
- },
203
- TARGET_TYPE: {
204
- AWS_ACCOUNT: "AWS_ACCOUNT",
205
- },
206
- VENDOR: {
207
- ANTHROPIC: "anthropic",
208
- AUTH0: "auth0",
209
- DATADOG: "datadog",
210
- KNOWTRACE: "knowtrace",
211
- MONGODB: "mongodb",
212
- OPENAI: "openai",
213
- SPLINTERLANDS: "splinterlands",
214
- },
215
- };
216
-
217
- class JaypieAccountLoggingBucket extends constructs.Construct {
218
- /**
219
- * Create a new account-wide logging S3 bucket with lifecycle policies and export
220
- */
221
- constructor(scope, idOrProps, propsOrUndefined) {
222
- // Handle overloaded constructor signatures
223
- let props;
224
- let id;
225
- if (typeof idOrProps === "string") {
226
- // First param is ID, second is props
227
- props = propsOrUndefined || {};
228
- id = idOrProps;
229
- }
230
- else {
231
- // First param is props
232
- props = idOrProps || {};
233
- id = props.id || "AccountLoggingBucket";
234
- }
235
- super(scope, id);
236
- // Generate default bucket name with PROJECT_NONCE
237
- const defaultBucketName = process.env.PROJECT_NONCE
238
- ? `account-logging-stack-${process.env.PROJECT_NONCE.toLowerCase()}`
239
- : "account-logging-stack";
240
- // Extract Jaypie-specific options
241
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
242
- const { bucketName = defaultBucketName, createOutput = true, expirationDays = 365, exportName = CDK$2.IMPORT.LOG_BUCKET, glacierTransitionDays = 180, id: _id, infrequentAccessTransitionDays = 30, outputDescription = "Account-wide logging bucket", project, service = CDK$2.SERVICE.INFRASTRUCTURE, ...bucketProps } = props;
243
- // Create the bucket with lifecycle rules
244
- this.bucket = new s3.Bucket(this, "Bucket", {
245
- accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
246
- bucketName,
247
- lifecycleRules: [
248
- {
249
- expiration: cdk__namespace.Duration.days(expirationDays),
250
- transitions: [
251
- {
252
- storageClass: s3.StorageClass.INFREQUENT_ACCESS,
253
- transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
254
- },
255
- {
256
- storageClass: s3.StorageClass.GLACIER,
257
- transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
258
- },
259
- ],
260
- },
261
- ],
262
- ...bucketProps,
263
- });
264
- // Add tags
265
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
266
- if (service) {
267
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
268
- }
269
- if (project) {
270
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
271
- }
272
- // Create CloudFormation output if enabled
273
- if (createOutput) {
274
- new cdk__namespace.CfnOutput(this, "BucketNameOutput", {
275
- description: outputDescription,
276
- exportName,
277
- value: this.bucket.bucketName,
278
- });
279
- }
280
- }
281
- }
282
-
283
- function addDatadogLayers(lambdaFunction, options = {}) {
284
- const datadogApiKeyArn = options?.datadogApiKeyArn;
285
- const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
286
- process.env.DATADOG_API_KEY_ARN ||
287
- process.env.CDK_ENV_DATADOG_API_KEY_ARN;
288
- if (!resolvedDatadogApiKeyArn) {
289
- return false;
290
- }
291
- // Define Datadog environment variables
292
- const datadogEnvVars = {
293
- DD_API_KEY_SECRET_ARN: resolvedDatadogApiKeyArn,
294
- DD_ENHANCED_METRICS: "true",
295
- DD_ENV: process.env.PROJECT_ENV || "",
296
- DD_PROFILING_ENABLED: "false",
297
- DD_SERVERLESS_APPSEC_ENABLED: "false",
298
- DD_SERVICE: process.env.PROJECT_SERVICE || "",
299
- DD_SITE: CDK$2.DATADOG.SITE,
300
- DD_TAGS: `${CDK$2.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
301
- DD_TRACE_OTEL_ENABLED: "false",
302
- };
303
- // Add environment variables only if they don't already exist
304
- Object.entries(datadogEnvVars).forEach(([key, value]) => {
305
- lambdaFunction.addEnvironment(key, value);
306
- });
307
- const datadogApiKeySecret = secretsmanager__namespace.Secret.fromSecretCompleteArn(lambdaFunction, "DatadogApiKey", resolvedDatadogApiKeyArn);
308
- const datadogLambda = new datadogCdkConstructsV2.DatadogLambda(lambdaFunction, "DatadogLambda", {
309
- apiKeySecret: datadogApiKeySecret, // apiKeySecret auto-grants secret access to the added lambdas
310
- nodeLayerVersion: CDK$2.DATADOG.LAYER.NODE,
311
- extensionLayerVersion: CDK$2.DATADOG.LAYER.EXTENSION,
312
- env: process.env.PROJECT_ENV,
313
- service: process.env.PROJECT_SERVICE,
314
- version: process.env.PROJECT_VERSION,
315
- });
316
- datadogLambda.addLambdaFunctions([lambdaFunction]);
317
- return true;
318
- }
319
-
320
- function constructEnvName(name, opts) {
321
- const env = opts?.env ?? process.env.PROJECT_ENV ?? "build";
322
- const key = opts?.key ?? process.env.PROJECT_KEY ?? "project";
323
- const nonce = opts?.nonce ?? process.env.PROJECT_NONCE ?? "cfe2"; // This default is intentionally short. It is not a special value but should not be changed.
324
- return `${env}-${key}-${name}-${nonce}`;
325
- }
326
-
327
- function constructStackName(key) {
328
- if (!key) {
329
- return `cdk-${process.env.PROJECT_SPONSOR}-${process.env.PROJECT_KEY}-${process.env.PROJECT_ENV}-${process.env.PROJECT_NONCE}`;
330
- }
331
- else {
332
- return `cdk-${process.env.PROJECT_SPONSOR}-${process.env.PROJECT_KEY}-${process.env.PROJECT_ENV}-${process.env.PROJECT_NONCE}-${key}`;
333
- }
334
- }
335
-
336
- const CDK$1 = {
337
- CREATION: {
338
- CDK: "cdk",
339
- },
340
- ROLE: {
341
- STACK: "stack",
342
- },
343
- TAG: {
344
- BUILD_DATE: "buildDate",
345
- BUILD_HEX: "buildHex",
346
- BUILD_TIME: "buildTime",
347
- COMMIT: "commit",
348
- CREATION: "creation",
349
- ENV: "env",
350
- NONCE: "nonce",
351
- PROJECT: "project",
352
- ROLE: "role",
353
- SERVICE: "service",
354
- SPONSOR: "sponsor",
355
- STACK: "stack",
356
- VERSION: "version",
357
- },
358
- };
359
- function constructTagger(construct, { name } = {}) {
360
- const stackName = name || constructStackName();
361
- const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
362
- if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
363
- cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
364
- }
365
- cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
366
- cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_TIME, Date.now().toString());
367
- if (process.env.PROJECT_COMMIT)
368
- cdk.Tags.of(construct).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
369
- cdk.Tags.of(construct).add(CDK$1.TAG.CREATION, CDK$1.CREATION.CDK);
370
- if (process.env.PROJECT_ENV)
371
- cdk.Tags.of(construct).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
372
- if (process.env.PROJECT_NONCE)
373
- cdk.Tags.of(construct).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
374
- if (process.env.PROJECT_KEY)
375
- cdk.Tags.of(construct).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
376
- cdk.Tags.of(construct).add(CDK$1.TAG.ROLE, CDK$1.ROLE.STACK);
377
- if (process.env.PROJECT_SERVICE)
378
- cdk.Tags.of(construct).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
379
- if (process.env.PROJECT_SPONSOR)
380
- cdk.Tags.of(construct).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
381
- if (stackName)
382
- cdk.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
383
- if (version)
384
- cdk.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
385
- return true;
386
- }
387
-
388
- function envHostname({ component, domain, env, subdomain, } = {}) {
389
- const resolvedDomain = domain || process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
390
- if (!resolvedDomain) {
391
- throw new errors.ConfigurationError("No hostname `domain` provided. Set CDK_ENV_DOMAIN or CDK_ENV_HOSTED_ZONE to use environment domain");
392
- }
393
- const resolvedComponent = component === "@" || component === "" ? undefined : component;
394
- const resolvedSubdomain = subdomain || process.env.CDK_ENV_SUBDOMAIN;
395
- const resolvedEnv = env || process.env.PROJECT_ENV;
396
- const filteredEnv = resolvedEnv === CDK$2.ENV.PRODUCTION ? undefined : resolvedEnv;
397
- const parts = [
398
- resolvedComponent,
399
- resolvedSubdomain,
400
- filteredEnv,
401
- resolvedDomain,
402
- ].filter((part) => part);
403
- return parts.join(".");
404
- }
405
-
406
- /**
407
- * Extends the Datadog IAM role with additional permissions
408
- *
409
- * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
410
- * If found, creates a custom policy with:
411
- * - budgets:ViewBudget
412
- * - logs:DescribeLogGroups
413
- *
414
- * @param scope - The construct scope
415
- * @param options - Configuration options
416
- * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
417
- */
418
- function extendDatadogRole(scope, options) {
419
- const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
420
- // Early return if no Datadog role ARN is configured
421
- if (!datadogRoleArn) {
422
- return undefined;
423
- }
424
- const { id = "DatadogCustomPolicy", project, service = CDK$2.SERVICE.DATADOG, } = options || {};
425
- // Lookup the Datadog role
426
- const datadogRole = awsIam.Role.fromRoleArn(scope, "DatadogRole", datadogRoleArn);
427
- // Build policy statements
428
- const statements = [
429
- // Allow view budget
430
- new awsIam.PolicyStatement({
431
- actions: ["budgets:ViewBudget"],
432
- resources: ["*"],
433
- }),
434
- // Allow describe log groups
435
- new awsIam.PolicyStatement({
436
- actions: ["logs:DescribeLogGroups"],
437
- resources: ["*"],
438
- }),
439
- ];
440
- // Create the custom policy
441
- const datadogCustomPolicy = new awsIam.Policy(scope, id, {
442
- roles: [datadogRole],
443
- statements,
444
- });
445
- // Add tags
446
- cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.SERVICE, service);
447
- cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
448
- cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
449
- if (project) {
450
- cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.PROJECT, project);
451
- }
452
- return datadogCustomPolicy;
453
- }
454
-
455
- /**
456
- * Check if the current environment matches the given environment
457
- */
458
- function isEnv(env) {
459
- return process.env.PROJECT_ENV === env;
460
- }
461
- /**
462
- * Check if the current environment is production
463
- */
464
- function isProductionEnv() {
465
- return isEnv(CDK$2.ENV.PRODUCTION);
466
- }
467
- /**
468
- * Check if the current environment is sandbox
469
- */
470
- function isSandboxEnv() {
471
- return isEnv(CDK$2.ENV.SANDBOX);
472
- }
473
-
474
- // In short the RFC 1035 standard for valid hostnames is:
475
- // 1. Must be less than 253 characters
476
- // 2. Must start with a letter
477
- // 3. Must end with a letter or number
478
- // 4. Can only contain letters, numbers, and hyphens
479
- // 5. Last part of the domain must be at least 2 characters
480
- function validPart$1(part) {
481
- if (!part.match(/^[a-z]/))
482
- return false;
483
- if (!part.match(/[a-z0-9]$/))
484
- return false;
485
- return /^[a-zA-Z0-9-]+$/.test(part);
486
- }
487
- function isValidHostname$1(hostname) {
488
- // Check hostname is a string
489
- if (typeof hostname !== "string")
490
- return false;
491
- // Convert hostname to lowercase
492
- const check = hostname.toString().toLowerCase();
493
- // Check hostname is less than 253 characters
494
- if (check.length > 253)
495
- return false;
496
- // Split on dots
497
- const parts = check.split(".");
498
- // Check each part is validPart
499
- const validParts = parts.map(validPart$1);
500
- // Confirm all parts are valid
501
- if (!validParts.every((part) => part))
502
- return false;
503
- // Confirm last part is at least 2 characters
504
- const lastPart = parts[parts.length - 1];
505
- if (lastPart.length < 2)
506
- return false;
507
- // Confirm last part is all letters
508
- if (!lastPart.match(/^[a-z]+$/))
509
- return false;
510
- // This is a valid hostname
511
- return true;
512
- }
513
-
514
- function validPart(part) {
515
- if (!part.match(/^[a-z]/))
516
- return false;
517
- if (!part.match(/[a-z0-9]$/))
518
- return false;
519
- return /^[a-zA-Z0-9-]+$/.test(part);
520
- }
521
- function isValidSubdomain(subdomain) {
522
- // Check subdomain is a string
523
- if (typeof subdomain !== "string")
524
- return false;
525
- // Special case for apex
526
- if (subdomain === CDK$2.HOST.APEX)
527
- return true;
528
- // Convert subdomain to lowercase
529
- const check = subdomain.toString().toLowerCase();
530
- // Check subdomain is less than 250 characters
531
- // We use 250 instead of 253 because we need to leave room for the dot top-level domain
532
- if (check.length > 250)
533
- return false;
534
- // Split on dots
535
- const parts = check.split(".");
536
- // Check each part is validPart
537
- const validParts = parts.map(validPart);
538
- // Confirm all parts are valid
539
- if (!validParts.every((part) => part))
540
- return false;
541
- // Do not care if last part is at least 2 characters
542
- // Do not care if last part is all letters
543
- // This is a valid subdomain
544
- return true;
545
- }
546
-
547
- function jaypieLambdaEnv(options = {}) {
548
- const { initialEnvironment = {} } = options;
549
- // Start with empty environment - we'll only add valid values
550
- let environment = {};
551
- // First, add all valid string values from initialEnvironment
552
- Object.entries(initialEnvironment).forEach(([key, value]) => {
553
- if (typeof value === "string") {
554
- environment[key] = value;
555
- }
556
- });
557
- // Default environment values
558
- const defaultEnvValues = {
559
- AWS_LAMBDA_NODEJS_DISABLE_CALLBACK_WARNING: "true",
560
- };
561
- // Apply default environment values with user overrides
562
- Object.entries(defaultEnvValues).forEach(([key, defaultValue]) => {
563
- if (key in initialEnvironment) {
564
- const userValue = initialEnvironment[key];
565
- // If user passes a string, it's already added above
566
- // If user passes non-string falsy value, omit the key
567
- if (!userValue) {
568
- delete environment[key];
569
- }
570
- // Ignore non-string truthy values (key not added)
571
- }
572
- else {
573
- // No user override, use default value
574
- environment[key] = defaultValue;
575
- }
576
- });
577
- // Default environment variables from process.env if present
578
- const defaultEnvVars = [
579
- "DATADOG_API_KEY_ARN",
580
- "LOG_LEVEL",
581
- "MODULE_LOGGER",
582
- "MODULE_LOG_LEVEL",
583
- "PROJECT_CHAOS",
584
- "PROJECT_COMMIT",
585
- "PROJECT_ENV",
586
- "PROJECT_KEY",
587
- "PROJECT_SECRET",
588
- "PROJECT_SERVICE",
589
- "PROJECT_SPONSOR",
590
- "PROJECT_VERSION",
591
- ];
592
- // Add default environment variables if they exist in process.env
593
- defaultEnvVars.forEach((envVar) => {
594
- if (process.env[envVar] && !environment[envVar]) {
595
- environment[envVar] = process.env[envVar];
596
- }
597
- });
598
- return environment;
599
- }
600
-
601
- function mergeDomain(subDomain, hostedZone) {
602
- if (!hostedZone) {
603
- throw new errors.ConfigurationError("hostedZone is required");
604
- }
605
- if (!subDomain) {
606
- // Return hostedZone if subDomain is not passed
607
- // Pass CDK.HOST.APEX to explicitly indicate apex domain
608
- return hostedZone;
609
- }
610
- if (subDomain === CDK$2.HOST.APEX) {
611
- return hostedZone;
612
- }
613
- return `${subDomain}.${hostedZone}`;
614
- }
615
-
616
- const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
617
- // Cache to store resolved functions
618
- // Using nested structure to support multiple functions per scope with automatic GC
619
- const functionCache = new WeakMap();
620
- function resolveDatadogForwarderFunction(scope, options) {
621
- const { import: importValue, name } = options || {};
622
- const functionName = name || DEFAULT_FUNCTION_NAME$1;
623
- const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
624
- // Create a cache key based on name and import
625
- const cacheKey = `${functionName}:${importKey}`;
626
- // Get or create scope cache
627
- let scopeCache = functionCache.get(scope);
628
- if (!scopeCache) {
629
- scopeCache = new Map();
630
- functionCache.set(scope, scopeCache);
631
- }
632
- // Return cached function if it exists
633
- const cachedFunction = scopeCache.get(cacheKey);
634
- if (cachedFunction) {
635
- return cachedFunction;
636
- }
637
- // Create and cache the function
638
- const func = lambda__namespace.Function.fromFunctionArn(scope, functionName, cdk__namespace.Fn.importValue(importKey));
639
- scopeCache.set(cacheKey, func);
640
- return func;
641
- }
642
-
643
- function resolveDatadogLayers(scope, options = {}) {
644
- const { datadogApiKeyArn, uniqueId } = options;
645
- let resolvedRegion = cdk.Stack.of(scope).region || "us-east-1";
646
- // Resolve the Datadog API key ARN from multiple sources
647
- const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
648
- process.env.DATADOG_API_KEY_ARN ||
649
- process.env.CDK_ENV_DATADOG_API_KEY_ARN;
650
- // Return null if no API key is found
651
- if (!resolvedDatadogApiKeyArn) {
652
- return undefined;
653
- }
654
- const layerIdSuffix = uniqueId || process.env.PROJECT_NONCE || Date.now().toString();
655
- // Create Datadog Node.js layer
656
- const datadogNodeLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogNodeLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Node20-x:${CDK$2.DATADOG.LAYER.NODE}`);
657
- // Create Datadog Extension layer
658
- const datadogExtensionLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogExtensionLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Extension:${CDK$2.DATADOG.LAYER.EXTENSION}`);
659
- return [datadogNodeLayer, datadogExtensionLayer];
660
- }
661
-
662
- const DEFAULT_FUNCTION_NAME = "DatadogForwarderFunction";
663
- // Cache to store resolved logging destinations
664
- // Using nested structure to support multiple destinations per scope with automatic GC
665
- const destinationCache = new WeakMap();
666
- function resolveDatadogLoggingDestination(scope, options) {
667
- const { import: importValue, name } = options || {};
668
- // Create a cache key based on name and import (same as forwarder function)
669
- const functionName = name || DEFAULT_FUNCTION_NAME;
670
- const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
671
- const cacheKey = `${functionName}:${importKey}`;
672
- // Get or create scope cache
673
- let scopeCache = destinationCache.get(scope);
674
- if (!scopeCache) {
675
- scopeCache = new Map();
676
- destinationCache.set(scope, scopeCache);
677
- }
678
- // Return cached destination if it exists
679
- const cachedDestination = scopeCache.get(cacheKey);
680
- if (cachedDestination) {
681
- return cachedDestination;
682
- }
683
- // Resolve the Datadog forwarder function
684
- const datadogForwarderFunction = resolveDatadogForwarderFunction(scope, options);
685
- // Create and cache the logging destination
686
- const datadogLoggingDestination = new logDestinations__namespace.LambdaDestination(datadogForwarderFunction);
687
- scopeCache.set(cacheKey, datadogLoggingDestination);
688
- return datadogLoggingDestination;
689
- }
690
-
691
- function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
692
- if (!zone) {
693
- throw new errors.ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
694
- }
695
- if (typeof zone === "string") {
696
- return route53__namespace.HostedZone.fromLookup(scope, name, {
697
- domainName: zone,
698
- });
699
- }
700
- return zone;
701
- }
702
-
703
- const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
704
- if (paramsAndSecrets === false) {
705
- return;
706
- }
707
- let resolvedParamsAndSecrets;
708
- if (paramsAndSecrets instanceof lambda__namespace.ParamsAndSecretsLayerVersion) {
709
- resolvedParamsAndSecrets = paramsAndSecrets;
710
- }
711
- else {
712
- const resolvedOptions = options || {};
713
- resolvedParamsAndSecrets = lambda__namespace.ParamsAndSecretsLayerVersion.fromVersion(lambda__namespace.ParamsAndSecretsVersions.V1_0_103, {
714
- cacheSize: resolvedOptions.cacheSize,
715
- logLevel: resolvedOptions.logLevel || lambda__namespace.ParamsAndSecretsLogLevel.WARN,
716
- parameterStoreTtl: resolvedOptions.parameterStoreTtl,
717
- secretsManagerTtl: resolvedOptions.secretsManagerTtl,
718
- });
719
- }
720
- return resolvedParamsAndSecrets;
721
- };
722
-
723
- class JaypieApiGateway extends constructs.Construct {
724
- constructor(scope, id, props) {
725
- super(scope, id);
726
- const { certificate = true, handler, host: propsHost, name, roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
727
- // Determine zone from props or environment
728
- let zone = propsZone;
729
- if (!zone && process.env.CDK_ENV_API_HOSTED_ZONE) {
730
- zone = process.env.CDK_ENV_API_HOSTED_ZONE;
731
- }
732
- // Determine host from props or environment
733
- let host = propsHost;
734
- if (!host) {
735
- if (process.env.CDK_ENV_API_HOST_NAME) {
736
- host = process.env.CDK_ENV_API_HOST_NAME;
737
- }
738
- else if (process.env.CDK_ENV_API_SUBDOMAIN &&
739
- process.env.CDK_ENV_API_HOSTED_ZONE) {
740
- host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
741
- }
742
- }
743
- const apiGatewayName = name || constructEnvName("ApiGateway");
744
- const certificateName = constructEnvName("Certificate");
745
- const apiDomainName = constructEnvName("ApiDomainName");
746
- let hostedZone;
747
- let certificateToUse;
748
- if (host && zone) {
749
- hostedZone = resolveHostedZone(this, { zone });
750
- if (certificate === true) {
751
- certificateToUse = new acm__namespace.Certificate(this, certificateName, {
752
- domainName: host,
753
- validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
754
- });
755
- cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, CDK$2.ROLE.HOSTING);
756
- }
757
- else if (typeof certificate === "object") {
758
- certificateToUse = certificate;
759
- }
760
- this._certificate = certificateToUse;
761
- this._host = host;
762
- }
763
- const {
764
- // * `...lambdaRestApiProps` cannot be moved to the first const destructuring because it needs to exclude the custom properties first.
765
- // Ignore the variables we already assigned to other properties
766
- /* eslint-disable @typescript-eslint/no-unused-vars */
767
- certificate: _certificate, host: _host, name: _name, roleTag: _roleTag, zone: _zone, handler: _handler,
768
- /* eslint-enable @typescript-eslint/no-unused-vars */
769
- ...lambdaRestApiProps } = props;
770
- this._api = new apiGateway__namespace.LambdaRestApi(this, apiGatewayName, {
771
- handler,
772
- ...lambdaRestApiProps,
773
- });
774
- cdk.Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
775
- if (host && certificateToUse && hostedZone) {
776
- this._domainName = this._api.addDomainName(apiDomainName, {
777
- domainName: host,
778
- certificate: certificateToUse,
779
- });
780
- cdk.Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
781
- const record = new route53__namespace.ARecord(this, "AliasRecord", {
782
- recordName: host,
783
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.ApiGatewayDomain(this._domainName)),
784
- zone: hostedZone,
785
- });
786
- cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
787
- }
788
- }
789
- get api() {
790
- return this._api;
791
- }
792
- get url() {
793
- return this._api.url;
794
- }
795
- get certificateArn() {
796
- return this._certificate?.certificateArn;
797
- }
798
- get domainName() {
799
- return this._domainName?.domainName;
800
- }
801
- get host() {
802
- return this._host;
803
- }
804
- get restApiId() {
805
- return this._api.restApiId;
806
- }
807
- get restApiName() {
808
- return this._api.restApiName;
809
- }
810
- get restApiRootResourceId() {
811
- return this._api.restApiRootResourceId;
812
- }
813
- get deploymentStage() {
814
- return this._api.deploymentStage;
815
- }
816
- get domainNameAliasDomainName() {
817
- return this._domainName?.domainNameAliasDomainName;
818
- }
819
- get domainNameAliasHostedZoneId() {
820
- return this._domainName?.domainNameAliasHostedZoneId;
821
- }
822
- get root() {
823
- return this._api.root;
824
- }
825
- get env() {
826
- return {
827
- account: cdk.Stack.of(this).account,
828
- region: cdk.Stack.of(this).region,
829
- };
830
- }
831
- get stack() {
832
- return this._api.stack;
833
- }
834
- arnForExecuteApi(method, path, stage) {
835
- return this._api.arnForExecuteApi(method, path, stage);
836
- }
837
- metric(metricName, props) {
838
- return this._api.metric(metricName, props);
839
- }
840
- metricCacheHitCount(props) {
841
- return this._api.metricCacheHitCount(props);
842
- }
843
- metricCacheMissCount(props) {
844
- return this._api.metricCacheMissCount(props);
845
- }
846
- metricClientError(props) {
847
- return this._api.metricClientError(props);
848
- }
849
- metricCount(props) {
850
- return this._api.metricCount(props);
851
- }
852
- metricIntegrationLatency(props) {
853
- return this._api.metricIntegrationLatency(props);
854
- }
855
- metricLatency(props) {
856
- return this._api.metricLatency(props);
857
- }
858
- metricServerError(props) {
859
- return this._api.metricServerError(props);
860
- }
861
- applyRemovalPolicy(policy) {
862
- this._api.applyRemovalPolicy(policy);
863
- }
864
- get restApiRef() {
865
- return {
866
- restApiId: this._api.restApiId,
867
- };
868
- }
869
- }
870
-
871
- class JaypieStack extends cdk.Stack {
872
- constructor(scope, id, props = {}) {
873
- const { key, ...stackProps } = props;
874
- // Handle stackName
875
- if (!stackProps.stackName) {
876
- stackProps.stackName = constructStackName(key);
877
- }
878
- // Handle env
879
- stackProps.env = {
880
- account: process.env.CDK_DEFAULT_ACCOUNT,
881
- region: process.env.CDK_DEFAULT_REGION,
882
- ...stackProps.env,
883
- };
884
- super(scope, id, stackProps);
885
- // Apply tags
886
- constructTagger(this, { name: stackProps.stackName });
887
- }
888
- }
889
-
890
- class JaypieAppStack extends JaypieStack {
891
- constructor(scope, id, props = {}) {
892
- const { key = "app", ...stackProps } = props;
893
- // Handle stackName
894
- if (!stackProps.stackName) {
895
- stackProps.stackName = constructStackName(key);
896
- }
897
- super(scope, id, { key, ...stackProps });
898
- }
899
- }
900
-
901
- class JaypieLambda extends constructs.Construct {
902
- constructor(scope, id, props) {
903
- super(scope, id);
904
- const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment: initialEnvironment = {}, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = CDK$2.ROLE.PROCESSING, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
905
- // Get base environment with defaults
906
- const environment = jaypieLambdaEnv({ initialEnvironment });
907
- const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
908
- // Create a working copy of layers
909
- const resolvedLayers = [...layers];
910
- // Process secrets environment variables
911
- const secretsEnvironment = Object.entries(envSecrets).reduce((acc, [key, secret]) => ({
912
- ...acc,
913
- [`SECRET_${key}`]: secret.secretName,
914
- }), {});
915
- // Process JaypieEnvSecret array
916
- const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
917
- if (secret.envKey) {
918
- return {
919
- ...acc,
920
- [`SECRET_${secret.envKey}`]: secret.secretName,
921
- };
922
- }
923
- return acc;
924
- }, {});
925
- // Add ParamsAndSecrets layer if configured
926
- const resolvedParamsAndSecrets = resolveParamsAndSecrets({
927
- paramsAndSecrets,
928
- options: paramsAndSecretsOptions,
929
- });
930
- // Create LogGroup if not provided
931
- const resolvedLogGroup = logGroup ??
932
- new logs__namespace.LogGroup(this, "LogGroup", {
933
- retention: logRetention,
934
- removalPolicy: cdk.RemovalPolicy.DESTROY,
935
- });
936
- // Create Lambda Function
937
- this._lambda = new lambda__namespace.Function(this, "Function", {
938
- allowAllOutbound,
939
- allowPublicSubnet,
940
- architecture,
941
- code: codeAsset,
942
- deadLetterQueue,
943
- deadLetterQueueEnabled,
944
- deadLetterTopic,
945
- description,
946
- environment: {
947
- ...environment,
948
- ...secretsEnvironment,
949
- ...jaypieSecretsEnvironment,
950
- },
951
- ephemeralStorageSize,
952
- filesystem,
953
- handler,
954
- initialPolicy,
955
- layers: resolvedLayers,
956
- logGroup: resolvedLogGroup,
957
- maxEventAge,
958
- memorySize,
959
- paramsAndSecrets: resolvedParamsAndSecrets,
960
- profiling,
961
- profilingGroup,
962
- reservedConcurrentExecutions,
963
- retryAttempts,
964
- runtime,
965
- runtimeManagementMode,
966
- securityGroups,
967
- timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
968
- tracing,
969
- vpc,
970
- vpcSubnets,
971
- // Enable auto-publishing of versions when using provisioned concurrency
972
- currentVersionOptions: provisionedConcurrentExecutions !== undefined
973
- ? {
974
- removalPolicy: cdk.RemovalPolicy.RETAIN,
975
- description: "Auto-published version for provisioned concurrency",
976
- // Don't set provisioned concurrency here - it will be set on the alias
977
- }
978
- : undefined,
979
- });
980
- addDatadogLayers(this._lambda, { datadogApiKeyArn });
981
- // Grant secret read permissions
982
- Object.values(envSecrets).forEach((secret) => {
983
- secret.grantRead(this._lambda);
984
- });
985
- // Grant read permissions for JaypieEnvSecrets
986
- secrets.forEach((secret) => {
987
- secret.grantRead(this._lambda);
988
- });
989
- // Configure provisioned concurrency if specified
990
- if (provisionedConcurrentExecutions !== undefined) {
991
- // Use currentVersion which is auto-published with proper configuration
992
- const version = this._lambda.currentVersion;
993
- // Create alias for provisioned concurrency
994
- this._provisioned = new lambda__namespace.Alias(this, "ProvisionedAlias", {
995
- aliasName: "provisioned",
996
- version,
997
- provisionedConcurrentExecutions,
998
- });
999
- // Add explicit dependencies to ensure proper creation order
1000
- this._provisioned.node.addDependency(version);
1001
- }
1002
- if (roleTag) {
1003
- cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
1004
- }
1005
- if (vendorTag) {
1006
- cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
1007
- }
1008
- // Assign _reference based on provisioned state
1009
- this._reference =
1010
- this._provisioned !== undefined ? this._provisioned : this._lambda;
1011
- }
1012
- // Public accessors
1013
- get lambda() {
1014
- return this._lambda;
1015
- }
1016
- get provisioned() {
1017
- return this._provisioned;
1018
- }
1019
- get reference() {
1020
- return this._reference;
1021
- }
1022
- // IFunction implementation
1023
- get functionArn() {
1024
- return this._reference.functionArn;
1025
- }
1026
- get functionName() {
1027
- return this._reference.functionName;
1028
- }
1029
- get grantPrincipal() {
1030
- return this._reference.grantPrincipal;
1031
- }
1032
- get role() {
1033
- return this._reference.role;
1034
- }
1035
- get architecture() {
1036
- return this._reference.architecture;
1037
- }
1038
- get connections() {
1039
- return this._reference.connections;
1040
- }
1041
- get isBoundToVpc() {
1042
- return this._reference.isBoundToVpc;
1043
- }
1044
- get latestVersion() {
1045
- return this._reference.latestVersion;
1046
- }
1047
- get permissionsNode() {
1048
- return this._reference.permissionsNode;
1049
- }
1050
- get resourceArnsForGrantInvoke() {
1051
- return this._reference.resourceArnsForGrantInvoke;
1052
- }
1053
- get functionRef() {
1054
- return {
1055
- functionArn: this._reference.functionArn,
1056
- functionName: this._reference.functionName,
1057
- };
1058
- }
1059
- addEventSource(source) {
1060
- this._reference.addEventSource(source);
1061
- }
1062
- addEventSourceMapping(id, options) {
1063
- return this._reference.addEventSourceMapping(id, options);
1064
- }
1065
- addFunctionUrl(options) {
1066
- return this._reference.addFunctionUrl(options);
1067
- }
1068
- addPermission(id, permission) {
1069
- this._reference.addPermission(id, permission);
1070
- }
1071
- addToRolePolicy(statement) {
1072
- this._reference.addToRolePolicy(statement);
1073
- }
1074
- configureAsyncInvoke(options) {
1075
- this._reference.configureAsyncInvoke(options);
1076
- }
1077
- grantInvoke(grantee) {
1078
- return this._reference.grantInvoke(grantee);
1079
- }
1080
- grantInvokeCompositePrincipal(compositePrincipal) {
1081
- return this._reference.grantInvokeCompositePrincipal(compositePrincipal);
1082
- }
1083
- grantInvokeUrl(grantee) {
1084
- return this._reference.grantInvokeUrl(grantee);
1085
- }
1086
- grantInvokeLatestVersion(grantee) {
1087
- return this._reference.grantInvokeLatestVersion(grantee);
1088
- }
1089
- grantInvokeVersion(grantee, version) {
1090
- return this._reference.grantInvokeVersion(grantee, version);
1091
- }
1092
- metric(metricName, props) {
1093
- return this._reference.metric(metricName, props);
1094
- }
1095
- metricDuration(props) {
1096
- return this._reference.metricDuration(props);
1097
- }
1098
- metricErrors(props) {
1099
- return this._reference.metricErrors(props);
1100
- }
1101
- metricInvocations(props) {
1102
- return this._reference.metricInvocations(props);
1103
- }
1104
- metricThrottles(props) {
1105
- return this._reference.metricThrottles(props);
1106
- }
1107
- get env() {
1108
- return {
1109
- account: cdk.Stack.of(this).account,
1110
- region: cdk.Stack.of(this).region,
1111
- };
1112
- }
1113
- get stack() {
1114
- return this._reference.stack;
1115
- }
1116
- applyRemovalPolicy(policy) {
1117
- this._reference.applyRemovalPolicy(policy);
1118
- }
1119
- addEnvironment(key, value) {
1120
- this._lambda.addEnvironment(key, value);
1121
- }
1122
- }
1123
-
1124
- class JaypieQueuedLambda extends constructs.Construct {
1125
- constructor(scope, id, props) {
1126
- super(scope, id);
1127
- const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
1128
- // Create SQS Queue
1129
- this._queue = new sqs__namespace.Queue(this, "Queue", {
1130
- fifo,
1131
- visibilityTimeout: typeof visibilityTimeout === "number"
1132
- ? cdk.Duration.seconds(visibilityTimeout)
1133
- : visibilityTimeout,
1134
- });
1135
- if (roleTag) {
1136
- cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
1137
- }
1138
- if (vendorTag) {
1139
- cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
1140
- }
1141
- // Create Lambda with JaypieLambda
1142
- this._lambdaConstruct = new JaypieLambda(this, "Function", {
1143
- allowAllOutbound,
1144
- allowPublicSubnet,
1145
- architecture,
1146
- code,
1147
- datadogApiKeyArn,
1148
- deadLetterQueue,
1149
- deadLetterQueueEnabled,
1150
- deadLetterTopic,
1151
- description,
1152
- environment: {
1153
- ...environment,
1154
- CDK_ENV_QUEUE_URL: this._queue.queueUrl,
1155
- },
1156
- envSecrets,
1157
- ephemeralStorageSize,
1158
- filesystem,
1159
- handler,
1160
- initialPolicy,
1161
- layers,
1162
- logGroup,
1163
- logRetention,
1164
- maxEventAge,
1165
- memorySize,
1166
- paramsAndSecrets,
1167
- paramsAndSecretsOptions,
1168
- profiling,
1169
- profilingGroup,
1170
- provisionedConcurrentExecutions,
1171
- reservedConcurrentExecutions,
1172
- retryAttempts,
1173
- roleTag,
1174
- runtime,
1175
- runtimeManagementMode,
1176
- secrets,
1177
- securityGroups,
1178
- timeout,
1179
- tracing,
1180
- vendorTag,
1181
- vpc,
1182
- vpcSubnets,
1183
- });
1184
- // Set up queue and lambda integration
1185
- this._queue.grantConsumeMessages(this._lambdaConstruct);
1186
- this._queue.grantSendMessages(this._lambdaConstruct);
1187
- this._lambdaConstruct.addEventSource(new lambdaEventSources__namespace.SqsEventSource(this._queue, {
1188
- batchSize,
1189
- }));
1190
- }
1191
- // Public accessors
1192
- get queue() {
1193
- return this._queue;
1194
- }
1195
- get lambda() {
1196
- return this._lambdaConstruct.lambda;
1197
- }
1198
- // IFunction implementation
1199
- get functionArn() {
1200
- return this._lambdaConstruct.functionArn;
1201
- }
1202
- get functionName() {
1203
- return this._lambdaConstruct.functionName;
1204
- }
1205
- get grantPrincipal() {
1206
- return this._lambdaConstruct.grantPrincipal;
1207
- }
1208
- get role() {
1209
- return this._lambdaConstruct.role;
1210
- }
1211
- get architecture() {
1212
- return this._lambdaConstruct.architecture;
1213
- }
1214
- get connections() {
1215
- return this._lambdaConstruct.connections;
1216
- }
1217
- get isBoundToVpc() {
1218
- return this._lambdaConstruct.isBoundToVpc;
1219
- }
1220
- get latestVersion() {
1221
- return this._lambdaConstruct.latestVersion;
1222
- }
1223
- get permissionsNode() {
1224
- return this._lambdaConstruct.permissionsNode;
1225
- }
1226
- get resourceArnsForGrantInvoke() {
1227
- return this._lambdaConstruct.resourceArnsForGrantInvoke;
1228
- }
1229
- get functionRef() {
1230
- return this._lambdaConstruct.functionRef;
1231
- }
1232
- addEventSource(source) {
1233
- this._lambdaConstruct.addEventSource(source);
1234
- }
1235
- addEventSourceMapping(id, options) {
1236
- return this._lambdaConstruct.addEventSourceMapping(id, options);
1237
- }
1238
- addFunctionUrl(options) {
1239
- return this._lambdaConstruct.addFunctionUrl(options);
1240
- }
1241
- addPermission(id, permission) {
1242
- this._lambdaConstruct.addPermission(id, permission);
1243
- }
1244
- addToRolePolicy(statement) {
1245
- this._lambdaConstruct.addToRolePolicy(statement);
1246
- }
1247
- configureAsyncInvoke(options) {
1248
- this._lambdaConstruct.configureAsyncInvoke(options);
1249
- }
1250
- grantInvoke(grantee) {
1251
- return this._lambdaConstruct.grantInvoke(grantee);
1252
- }
1253
- grantInvokeCompositePrincipal(compositePrincipal) {
1254
- return this._lambdaConstruct.grantInvokeCompositePrincipal(compositePrincipal);
1255
- }
1256
- grantInvokeUrl(grantee) {
1257
- return this._lambdaConstruct.grantInvokeUrl(grantee);
1258
- }
1259
- metric(metricName, props) {
1260
- return this._lambdaConstruct.metric(metricName, props);
1261
- }
1262
- metricDuration(props) {
1263
- return this._lambdaConstruct.metricDuration(props);
1264
- }
1265
- metricErrors(props) {
1266
- return this._lambdaConstruct.metricErrors(props);
1267
- }
1268
- metricInvocations(props) {
1269
- return this._lambdaConstruct.metricInvocations(props);
1270
- }
1271
- metricThrottles(props) {
1272
- return this._lambdaConstruct.metricThrottles(props);
1273
- }
1274
- // Additional IFunction implementation
1275
- grantInvokeLatestVersion(grantee) {
1276
- return this._lambdaConstruct.grantInvokeLatestVersion(grantee);
1277
- }
1278
- grantInvokeVersion(grantee, version) {
1279
- return this._lambdaConstruct.grantInvokeVersion(grantee, version);
1280
- }
1281
- get env() {
1282
- return {
1283
- account: cdk.Stack.of(this).account,
1284
- region: cdk.Stack.of(this).region,
1285
- };
1286
- }
1287
- get stack() {
1288
- return cdk.Stack.of(this);
1289
- }
1290
- applyRemovalPolicy(policy) {
1291
- this._lambdaConstruct.applyRemovalPolicy(policy);
1292
- this._queue.applyRemovalPolicy(policy);
1293
- }
1294
- // IQueue implementation
1295
- get fifo() {
1296
- return this._queue.fifo;
1297
- }
1298
- get queueArn() {
1299
- return this._queue.queueArn;
1300
- }
1301
- get queueName() {
1302
- return this._queue.queueName;
1303
- }
1304
- get queueUrl() {
1305
- return this._queue.queueUrl;
1306
- }
1307
- get encryptionMasterKey() {
1308
- return this._queue.encryptionMasterKey;
1309
- }
1310
- addToResourcePolicy(statement) {
1311
- return this._queue.addToResourcePolicy(statement);
1312
- }
1313
- grant(grantee, ...actions) {
1314
- return this._queue.grant(grantee, ...actions);
1315
- }
1316
- grantConsumeMessages(grantee) {
1317
- return this._queue.grantConsumeMessages(grantee);
1318
- }
1319
- grantPurge(grantee) {
1320
- return this._queue.grantPurge(grantee);
1321
- }
1322
- grantSendMessages(grantee) {
1323
- return this._queue.grantSendMessages(grantee);
1324
- }
1325
- // Queue metrics
1326
- metricApproximateAgeOfOldestMessage(props) {
1327
- return this._queue.metricApproximateAgeOfOldestMessage(props);
1328
- }
1329
- metricApproximateNumberOfMessagesDelayed(props) {
1330
- return this._queue.metricApproximateNumberOfMessagesDelayed(props);
1331
- }
1332
- metricApproximateNumberOfMessagesNotVisible(props) {
1333
- return this._queue.metricApproximateNumberOfMessagesNotVisible(props);
1334
- }
1335
- metricApproximateNumberOfMessagesVisible(props) {
1336
- return this._queue.metricApproximateNumberOfMessagesVisible(props);
1337
- }
1338
- metricNumberOfEmptyReceives(props) {
1339
- return this._queue.metricNumberOfEmptyReceives(props);
1340
- }
1341
- metricNumberOfMessagesDeleted(props) {
1342
- return this._queue.metricNumberOfMessagesDeleted(props);
1343
- }
1344
- metricNumberOfMessagesReceived(props) {
1345
- return this._queue.metricNumberOfMessagesReceived(props);
1346
- }
1347
- metricNumberOfMessagesSent(props) {
1348
- return this._queue.metricNumberOfMessagesSent(props);
1349
- }
1350
- metricSentMessageSize(props) {
1351
- return this._queue.metricSentMessageSize(props);
1352
- }
1353
- addEnvironment(key, value) {
1354
- this._lambdaConstruct.addEnvironment(key, value);
1355
- }
1356
- }
1357
-
1358
- class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1359
- constructor(scope, id, props) {
1360
- props.fifo = false; // S3 event notifications are not supported for FIFO queues
1361
- super(scope, id, props);
1362
- const { bucketName, roleTag, vendorTag, bucketOptions = {} } = props;
1363
- // Create S3 Bucket
1364
- this._bucket = new s3__namespace.Bucket(this, "Bucket", {
1365
- bucketName: bucketOptions.bucketName || bucketName,
1366
- removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
1367
- ...bucketOptions,
1368
- });
1369
- // Add tags to bucket
1370
- if (roleTag) {
1371
- cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1372
- }
1373
- if (vendorTag) {
1374
- cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1375
- }
1376
- // Add an event notification from the bucket to the queue
1377
- this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
1378
- // Grant the lambda access to the bucket
1379
- this._bucket.grantReadWrite(this);
1380
- // Add environment variable for bucket name
1381
- this.lambda.addEnvironment("CDK_ENV_BUCKET_NAME", this._bucket.bucketName);
1382
- }
1383
- // Public accessors
1384
- get bucket() {
1385
- return this._bucket;
1386
- }
1387
- // IBucket implementation
1388
- get bucketArn() {
1389
- return this._bucket.bucketArn;
1390
- }
1391
- get bucketDomainName() {
1392
- return this._bucket.bucketDomainName;
1393
- }
1394
- get bucketDualStackDomainName() {
1395
- return this._bucket.bucketDualStackDomainName;
1396
- }
1397
- get bucketName() {
1398
- return this._bucket.bucketName;
1399
- }
1400
- get bucketRegionalDomainName() {
1401
- return this._bucket.bucketRegionalDomainName;
1402
- }
1403
- get bucketWebsiteDomainName() {
1404
- return this._bucket.bucketWebsiteDomainName;
1405
- }
1406
- get bucketWebsiteUrl() {
1407
- return this._bucket.bucketWebsiteUrl;
1408
- }
1409
- get encryptionKey() {
1410
- return this._bucket.encryptionKey;
1411
- }
1412
- get isWebsite() {
1413
- return this._bucket.isWebsite || false;
1414
- }
1415
- get policy() {
1416
- return this._bucket.policy;
1417
- }
1418
- addEventNotification(event, dest, ...filters) {
1419
- this._bucket.addEventNotification(event, dest, ...filters);
1420
- }
1421
- addObjectCreatedNotification(dest, ...filters) {
1422
- this._bucket.addObjectCreatedNotification(dest, ...filters);
1423
- }
1424
- addObjectRemovedNotification(dest, ...filters) {
1425
- this._bucket.addObjectRemovedNotification(dest, ...filters);
1426
- }
1427
- addToResourcePolicy(permission) {
1428
- return this._bucket.addToResourcePolicy(permission);
1429
- }
1430
- arnForObjects(objectKeyPattern) {
1431
- return this._bucket.arnForObjects(objectKeyPattern);
1432
- }
1433
- enableEventBridgeNotification() {
1434
- this._bucket.enableEventBridgeNotification();
1435
- }
1436
- grantDelete(grantee, objectsKeyPattern) {
1437
- return this._bucket.grantDelete(grantee, objectsKeyPattern);
1438
- }
1439
- grantPublicAccess(keyPrefix, ...allowedActions) {
1440
- return this._bucket.grantPublicAccess(keyPrefix, ...allowedActions);
1441
- }
1442
- grantPut(grantee, objectsKeyPattern) {
1443
- return this._bucket.grantPut(grantee, objectsKeyPattern);
1444
- }
1445
- grantPutAcl(grantee, objectsKeyPattern) {
1446
- return this._bucket.grantPutAcl(grantee, objectsKeyPattern);
1447
- }
1448
- grantRead(grantee, objectsKeyPattern) {
1449
- return this._bucket.grantRead(grantee, objectsKeyPattern);
1450
- }
1451
- grantReadWrite(grantee, objectsKeyPattern) {
1452
- return this._bucket.grantReadWrite(grantee, objectsKeyPattern);
1453
- }
1454
- grantWrite(grantee, objectsKeyPattern) {
1455
- return this._bucket.grantWrite(grantee, objectsKeyPattern);
1456
- }
1457
- onCloudTrailEvent(id, options) {
1458
- return this._bucket.onCloudTrailEvent(id, options);
1459
- }
1460
- onCloudTrailPutObject(id, options) {
1461
- return this._bucket.onCloudTrailPutObject(id, options);
1462
- }
1463
- onCloudTrailWriteObject(id, options) {
1464
- return this._bucket.onCloudTrailWriteObject(id, options);
1465
- }
1466
- s3UrlForObject(key) {
1467
- return this._bucket.s3UrlForObject(key);
1468
- }
1469
- transferAccelerationUrlForObject(key, options) {
1470
- return this._bucket.transferAccelerationUrlForObject(key, options);
1471
- }
1472
- urlForObject(key) {
1473
- return this._bucket.urlForObject(key);
1474
- }
1475
- virtualHostedUrlForObject(key, options) {
1476
- return this._bucket.virtualHostedUrlForObject(key, options);
1477
- }
1478
- grantReplicationPermission(identity, props) {
1479
- return this._bucket.grantReplicationPermission(identity, props);
1480
- }
1481
- addReplicationPolicy(policy) {
1482
- this._bucket.addReplicationPolicy(policy);
1483
- }
1484
- get bucketRef() {
1485
- return {
1486
- bucketArn: this._bucket.bucketArn,
1487
- bucketName: this._bucket.bucketName,
1488
- };
1489
- }
1490
- // Override applyRemovalPolicy to apply to all resources
1491
- applyRemovalPolicy(policy) {
1492
- super.applyRemovalPolicy(policy);
1493
- this._bucket.applyRemovalPolicy(policy);
1494
- }
1495
- }
1496
-
1497
- class JaypieDatadogBucket extends constructs.Construct {
1498
- /**
1499
- * Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
1500
- */
1501
- constructor(scope, idOrProps, propsOrUndefined) {
1502
- // Handle overloaded constructor signatures
1503
- let props;
1504
- let id;
1505
- if (typeof idOrProps === "string") {
1506
- // First param is ID, second is props
1507
- props = propsOrUndefined || {};
1508
- id = idOrProps;
1509
- }
1510
- else {
1511
- // First param is props
1512
- props = idOrProps || {};
1513
- id = props.id || "JaypieDatadogBucket";
1514
- }
1515
- super(scope, id);
1516
- // Extract Jaypie-specific options
1517
- const { bucketId = "DatadogArchiveBucket", bucketScope, grantDatadogAccess = true, project, service = CDK$2.SERVICE.DATADOG, ...bucketProps } = props;
1518
- // Create the bucket using bucketScope (defaults to this) and bucketId
1519
- const effectiveBucketScope = bucketScope || this;
1520
- this.bucket = new s3.Bucket(effectiveBucketScope, bucketId, bucketProps);
1521
- // Add tags to bucket
1522
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
1523
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1524
- if (project) {
1525
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
1526
- }
1527
- // Grant Datadog role access to bucket if enabled
1528
- if (grantDatadogAccess) {
1529
- this.policy = this.grantDatadogRoleBucketAccess({ project, service });
1530
- }
1531
- }
1532
- /**
1533
- * Grants the Datadog IAM role access to this bucket
1534
- *
1535
- * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
1536
- * If found, creates a custom policy with:
1537
- * - s3:ListBucket on bucket
1538
- * - s3:GetObject and s3:PutObject on bucket/*
1539
- *
1540
- * @param options - Configuration options
1541
- * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
1542
- */
1543
- grantDatadogRoleBucketAccess(options) {
1544
- const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
1545
- // Early return if no Datadog role ARN is configured
1546
- if (!datadogRoleArn) {
1547
- return undefined;
1548
- }
1549
- const { project, service = CDK$2.SERVICE.DATADOG } = options || {};
1550
- // Lookup the Datadog role
1551
- const datadogRole = awsIam.Role.fromRoleArn(this, "DatadogRole", datadogRoleArn);
1552
- // Build policy statements for bucket access
1553
- const statements = [
1554
- // Allow list bucket
1555
- new awsIam.PolicyStatement({
1556
- actions: ["s3:ListBucket"],
1557
- resources: [this.bucket.bucketArn],
1558
- }),
1559
- // Allow read and write to the bucket
1560
- new awsIam.PolicyStatement({
1561
- actions: ["s3:GetObject", "s3:PutObject"],
1562
- resources: [`${this.bucket.bucketArn}/*`],
1563
- }),
1564
- ];
1565
- // Create the custom policy
1566
- const datadogBucketPolicy = new awsIam.Policy(this, "DatadogBucketPolicy", {
1567
- roles: [datadogRole],
1568
- statements,
1569
- });
1570
- // Add tags
1571
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.SERVICE, service);
1572
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1573
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1574
- if (project) {
1575
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.PROJECT, project);
1576
- }
1577
- return datadogBucketPolicy;
1578
- }
1579
- }
1580
-
1581
- const DATADOG_FORWARDER_TEMPLATE_URL = "https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml";
1582
- const DEFAULT_RESERVED_CONCURRENCY = "10";
1583
- class JaypieDatadogForwarder extends constructs.Construct {
1584
- /**
1585
- * Create a new Datadog forwarder with CloudFormation nested stack
1586
- */
1587
- constructor(scope, idOrProps, propsOrUndefined) {
1588
- // Handle overloaded constructor signatures
1589
- let props;
1590
- let id;
1591
- if (typeof idOrProps === "string") {
1592
- // First param is ID, second is props
1593
- props = propsOrUndefined || {};
1594
- id = idOrProps;
1595
- }
1596
- else {
1597
- // First param is props
1598
- props = idOrProps || {};
1599
- id = props.id || "DatadogForwarder";
1600
- }
1601
- super(scope, id);
1602
- // Resolve options with defaults
1603
- const { account = process.env.CDK_ENV_ACCOUNT, additionalTags, createOutput = true, datadogApiKey = process.env.CDK_ENV_DATADOG_API_KEY, enableCloudFormationEvents = true, enableRoleExtension = true, exportName = CDK$2.IMPORT.DATADOG_LOG_FORWARDER, project, reservedConcurrency = DEFAULT_RESERVED_CONCURRENCY, service = CDK$2.VENDOR.DATADOG, templateUrl = DATADOG_FORWARDER_TEMPLATE_URL, } = props;
1604
- // Validate required parameters
1605
- if (!datadogApiKey) {
1606
- throw new Error("Datadog API key is required. Provide via datadogApiKey prop or CDK_ENV_DATADOG_API_KEY environment variable.");
1607
- }
1608
- // Build Datadog tags
1609
- let ddTags = account ? `account:${account}` : "";
1610
- if (additionalTags) {
1611
- ddTags = ddTags ? `${ddTags},${additionalTags}` : additionalTags;
1612
- }
1613
- // Deploy Datadog CloudFormation stack
1614
- this.cfnStack = new cdk.CfnStack(this, "Stack", {
1615
- parameters: {
1616
- DdApiKey: datadogApiKey,
1617
- DdTags: ddTags,
1618
- ReservedConcurrency: reservedConcurrency,
1619
- },
1620
- templateUrl,
1621
- });
1622
- // Add tags to stack
1623
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1624
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.SERVICE, service);
1625
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1626
- if (project) {
1627
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.PROJECT, project);
1628
- }
1629
- // Extract forwarder function from stack outputs
1630
- this.forwarderFunction = lambda__namespace.Function.fromFunctionArn(this, "Function", this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString());
1631
- // Extend Datadog role with custom permissions if enabled
1632
- if (enableRoleExtension) {
1633
- extendDatadogRole(this, { project, service });
1634
- }
1635
- // Create CloudFormation events rule if enabled
1636
- if (enableCloudFormationEvents) {
1637
- this.eventsRule = new awsEvents.Rule(this, "CloudFormationEventsRule", {
1638
- eventPattern: {
1639
- source: ["aws.cloudformation"],
1640
- },
1641
- targets: [
1642
- new awsEventsTargets.LambdaFunction(this.forwarderFunction, {
1643
- event: awsEvents.RuleTargetInput.fromEventPath("$"),
1644
- }),
1645
- ],
1646
- });
1647
- // Add tags to events rule
1648
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1649
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.SERVICE, service);
1650
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1651
- if (project) {
1652
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.PROJECT, project);
1653
- }
1654
- }
1655
- // Create CloudFormation output if enabled
1656
- if (createOutput) {
1657
- new cdk__namespace.CfnOutput(this, "ForwarderArnOutput", {
1658
- description: "Datadog Log Forwarder Lambda ARN",
1659
- exportName,
1660
- value: this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString(),
1661
- });
1662
- }
1663
- }
1664
- }
1665
-
1666
- class JaypieDistribution extends constructs.Construct {
1667
- constructor(scope, id, props) {
1668
- super(scope, id);
1669
- const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, invokeMode = lambda__namespace.InvokeMode.BUFFERED, roleTag = CDK$2.ROLE.API, zone: propsZone, ...distributionProps } = props;
1670
- // Validate environment variables
1671
- if (process.env.CDK_ENV_API_SUBDOMAIN &&
1672
- !isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
1673
- throw new Error("CDK_ENV_API_SUBDOMAIN is not a valid subdomain");
1674
- }
1675
- if (process.env.CDK_ENV_API_HOSTED_ZONE &&
1676
- !isValidHostname$1(process.env.CDK_ENV_API_HOSTED_ZONE)) {
1677
- throw new Error("CDK_ENV_API_HOSTED_ZONE is not a valid hostname");
1678
- }
1679
- if (process.env.CDK_ENV_HOSTED_ZONE &&
1680
- !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
1681
- throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
1682
- }
1683
- // Determine host from props or environment
1684
- let host = propsHost;
1685
- if (!host) {
1686
- try {
1687
- if (process.env.CDK_ENV_API_HOST_NAME) {
1688
- host = process.env.CDK_ENV_API_HOST_NAME;
1689
- }
1690
- else if (process.env.CDK_ENV_API_SUBDOMAIN) {
1691
- host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
1692
- process.env.CDK_ENV_HOSTED_ZONE ||
1693
- "");
1694
- }
1695
- }
1696
- catch {
1697
- host = undefined;
1698
- }
1699
- }
1700
- if (host && !isValidHostname$1(host)) {
1701
- throw new Error("Host is not a valid hostname");
1702
- }
1703
- this.host = host;
1704
- // Determine zone from props or environment
1705
- const zone = propsZone || process.env.CDK_ENV_HOSTED_ZONE;
1706
- // Resolve the origin from handler
1707
- // Check order matters: IFunctionUrl before IOrigin (FunctionUrl also has bind method)
1708
- // IFunction before IFunctionUrl (IFunction doesn't have functionUrlId)
1709
- let origin;
1710
- if (handler) {
1711
- if (this.isIFunction(handler)) {
1712
- // Create FunctionUrl for the Lambda function
1713
- const functionUrl = new lambda__namespace.FunctionUrl(this, "FunctionUrl", {
1714
- function: handler,
1715
- authType: lambda__namespace.FunctionUrlAuthType.NONE,
1716
- invokeMode,
1717
- });
1718
- this.functionUrl = functionUrl;
1719
- origin = new origins__namespace.FunctionUrlOrigin(functionUrl);
1720
- }
1721
- else if (this.isIFunctionUrl(handler)) {
1722
- origin = new origins__namespace.FunctionUrlOrigin(handler);
1723
- }
1724
- else if (this.isIOrigin(handler)) {
1725
- origin = handler;
1726
- }
1727
- }
1728
- // Build default behavior
1729
- let defaultBehavior;
1730
- if (propsDefaultBehavior) {
1731
- defaultBehavior = propsDefaultBehavior;
1732
- }
1733
- else if (origin) {
1734
- defaultBehavior = {
1735
- allowedMethods: cloudfront__namespace.AllowedMethods.ALLOW_ALL,
1736
- cachePolicy: cloudfront__namespace.CachePolicy.CACHING_DISABLED,
1737
- origin,
1738
- originRequestPolicy: cloudfront__namespace.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
1739
- viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
1740
- };
1741
- }
1742
- else {
1743
- throw new Error("Either handler or defaultBehavior must be provided to JaypieDistribution");
1744
- }
1745
- // Resolve hosted zone and certificate
1746
- // Only resolve zone when we need it (for certificate or DNS)
1747
- let hostedZone;
1748
- let certificateToUse;
1749
- if (host && zone && certificateProp !== false) {
1750
- hostedZone = resolveHostedZone(this, { zone });
1751
- if (certificateProp === true) {
1752
- certificateToUse = new acm__namespace.Certificate(this, constructEnvName("Certificate"), {
1753
- domainName: host,
1754
- validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
1755
- });
1756
- cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, roleTag);
1757
- }
1758
- else if (typeof certificateProp === "object") {
1759
- certificateToUse = certificateProp;
1760
- }
1761
- this.certificate = certificateToUse;
1762
- }
1763
- // Create log bucket if logging is enabled
1764
- let logBucket;
1765
- if (destinationProp !== false) {
1766
- logBucket = new s3__namespace.Bucket(this, constructEnvName("LogBucket"), {
1767
- objectOwnership: s3__namespace.ObjectOwnership.OBJECT_WRITER,
1768
- removalPolicy: cdk.RemovalPolicy.DESTROY,
1769
- autoDeleteObjects: true,
1770
- lifecycleRules: [
1771
- {
1772
- expiration: cdk.Duration.days(90),
1773
- transitions: [
1774
- {
1775
- storageClass: s3__namespace.StorageClass.INFREQUENT_ACCESS,
1776
- transitionAfter: cdk.Duration.days(30),
1777
- },
1778
- ],
1779
- },
1780
- ],
1781
- });
1782
- cdk.Tags.of(logBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
1783
- // Add S3 notification to Datadog forwarder
1784
- const lambdaDestination = destinationProp === true
1785
- ? new s3n.LambdaDestination(resolveDatadogForwarderFunction(this))
1786
- : destinationProp;
1787
- logBucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, lambdaDestination);
1788
- this.logBucket = logBucket;
1789
- }
1790
- // Create the CloudFront distribution
1791
- this.distribution = new cloudfront__namespace.Distribution(this, constructEnvName("Distribution"), {
1792
- defaultBehavior,
1793
- ...(host && certificateToUse
1794
- ? {
1795
- certificate: certificateToUse,
1796
- domainNames: [host],
1797
- }
1798
- : {}),
1799
- ...(logBucket
1800
- ? {
1801
- enableLogging: true,
1802
- logBucket,
1803
- logFilePrefix: "cloudfront-logs/",
1804
- }
1805
- : {}),
1806
- ...distributionProps,
1807
- });
1808
- cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
1809
- this.distributionArn = `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${this.distribution.distributionId}`;
1810
- this.distributionDomainName = this.distribution.distributionDomainName;
1811
- this.distributionId = this.distribution.distributionId;
1812
- this.domainName = this.distribution.domainName;
1813
- // Create DNS records if we have host and zone
1814
- if (host && hostedZone) {
1815
- const aRecord = new route53__namespace.ARecord(this, "AliasRecord", {
1816
- recordName: host,
1817
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
1818
- zone: hostedZone,
1819
- });
1820
- cdk.Tags.of(aRecord).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1821
- const aaaaRecord = new route53__namespace.AaaaRecord(this, "AaaaAliasRecord", {
1822
- recordName: host,
1823
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
1824
- zone: hostedZone,
1825
- });
1826
- cdk.Tags.of(aaaaRecord).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1827
- }
1828
- }
1829
- // Type guards for handler types
1830
- isIOrigin(handler) {
1831
- return (typeof handler === "object" &&
1832
- handler !== null &&
1833
- "bind" in handler &&
1834
- typeof handler.bind === "function");
1835
- }
1836
- isIFunctionUrl(handler) {
1837
- // FunctionUrl has 'url' property which is the function URL string
1838
- // IFunction does not have 'url' property
1839
- return (typeof handler === "object" &&
1840
- handler !== null &&
1841
- "url" in handler &&
1842
- "functionArn" in handler);
1843
- }
1844
- isIFunction(handler) {
1845
- // IFunction has functionArn and functionName but NOT 'url'
1846
- // (FunctionUrl also has functionArn but also has 'url')
1847
- return (typeof handler === "object" &&
1848
- handler !== null &&
1849
- "functionArn" in handler &&
1850
- "functionName" in handler &&
1851
- !("url" in handler));
1852
- }
1853
- // Implement IDistribution interface
1854
- get env() {
1855
- return {
1856
- account: cdk.Stack.of(this).account,
1857
- region: cdk.Stack.of(this).region,
1858
- };
1859
- }
1860
- get stack() {
1861
- return this.distribution.stack;
1862
- }
1863
- applyRemovalPolicy(policy) {
1864
- this.distribution.applyRemovalPolicy(policy);
1865
- }
1866
- grant(identity, ...actions) {
1867
- return this.distribution.grant(identity, ...actions);
1868
- }
1869
- grantCreateInvalidation(identity) {
1870
- return this.distribution.grantCreateInvalidation(identity);
1871
- }
1872
- get distributionRef() {
1873
- return {
1874
- distributionId: this.distribution.distributionId,
1875
- };
1876
- }
1877
- }
1878
-
1879
- // It is a consumer if the environment is ephemeral
1880
- function checkEnvIsConsumer(env = process.env) {
1881
- return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
1882
- !!env.CDK_ENV_PERSONAL ||
1883
- /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
1884
- /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
1885
- }
1886
- function checkEnvIsProvider(env = process.env) {
1887
- return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
1888
- }
1889
- function cleanName(name) {
1890
- return name.replace(/[^a-zA-Z0-9:-]/g, "");
1891
- }
1892
- function exportEnvName(name, env = process.env) {
1893
- let rawName;
1894
- if (checkEnvIsProvider(env)) {
1895
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1896
- // Clean the entire name to only allow alphanumeric, colons, and hyphens
1897
- return cleanName(rawName);
1898
- }
1899
- else {
1900
- if (checkEnvIsConsumer(env)) {
1901
- rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1902
- }
1903
- else {
1904
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1905
- }
1906
- }
1907
- return cleanName(rawName);
1908
- }
1909
- class JaypieEnvSecret extends constructs.Construct {
1910
- constructor(scope, idOrEnvKey, props) {
1911
- // Check if idOrEnvKey should be treated as envKey:
1912
- // - No props provided OR props.envKey is not set
1913
- // - AND idOrEnvKey exists as a non-empty string in process.env
1914
- const treatAsEnvKey = (!props || props.envKey === undefined) &&
1915
- typeof process.env[idOrEnvKey] === "string" &&
1916
- process.env[idOrEnvKey] !== "";
1917
- const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
1918
- super(scope, id);
1919
- const { consumer = checkEnvIsConsumer(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider(), roleTag, vendorTag, value, } = props || {};
1920
- const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
1921
- this._envKey = envKey;
1922
- let exportName;
1923
- if (!exportParam) {
1924
- exportName = exportEnvName(id);
1925
- }
1926
- else {
1927
- exportName = cleanName(exportParam);
1928
- }
1929
- if (consumer) {
1930
- const secretName = cdk.Fn.importValue(exportName);
1931
- this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1932
- // Add CfnOutput for consumer secrets
1933
- new cdk.CfnOutput(this, `ConsumedName`, {
1934
- value: this._secret.secretName,
1935
- });
1936
- }
1937
- else {
1938
- const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1939
- const secretProps = {
1940
- generateSecretString,
1941
- secretStringValue: !generateSecretString && secretValue
1942
- ? cdk.SecretValue.unsafePlainText(secretValue)
1943
- : undefined,
1944
- };
1945
- this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
1946
- if (roleTag) {
1947
- cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
1948
- }
1949
- if (vendorTag) {
1950
- cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
1951
- }
1952
- if (provider) {
1953
- new cdk.CfnOutput(this, `ProvidedName`, {
1954
- value: this._secret.secretName,
1955
- exportName,
1956
- });
1957
- }
1958
- else {
1959
- new cdk.CfnOutput(this, `CreatedName`, {
1960
- value: this._secret.secretName,
1961
- });
1962
- }
1963
- }
1964
- }
1965
- // IResource implementation
1966
- get stack() {
1967
- return cdk.Stack.of(this);
1968
- }
1969
- get env() {
1970
- return {
1971
- account: cdk.Stack.of(this).account,
1972
- region: cdk.Stack.of(this).region,
1973
- };
1974
- }
1975
- applyRemovalPolicy(policy) {
1976
- this._secret.applyRemovalPolicy(policy);
1977
- }
1978
- // ISecret implementation
1979
- get secretArn() {
1980
- return this._secret.secretArn;
1981
- }
1982
- get secretName() {
1983
- return this._secret.secretName;
1984
- }
1985
- get secretFullArn() {
1986
- return this._secret.secretFullArn;
1987
- }
1988
- get encryptionKey() {
1989
- return this._secret.encryptionKey;
1990
- }
1991
- get secretValue() {
1992
- return this._secret.secretValue;
1993
- }
1994
- secretValueFromJson(key) {
1995
- return this._secret.secretValueFromJson(key);
1996
- }
1997
- grantRead(grantee, versionStages) {
1998
- return this._secret.grantRead(grantee, versionStages);
1999
- }
2000
- grantWrite(grantee) {
2001
- return this._secret.grantWrite(grantee);
2002
- }
2003
- addRotationSchedule(id, options) {
2004
- return this._secret.addRotationSchedule(id, options);
2005
- }
2006
- addToResourcePolicy(statement) {
2007
- return this._secret.addToResourcePolicy(statement);
2008
- }
2009
- denyAccountRootDelete() {
2010
- this._secret.denyAccountRootDelete();
2011
- }
2012
- attach(target) {
2013
- return this._secret.attach(target);
2014
- }
2015
- get envKey() {
2016
- return this._envKey;
2017
- }
2018
- }
2019
-
2020
- class JaypieDatadogSecret extends JaypieEnvSecret {
2021
- constructor(scope, id = "MongoConnectionString", props) {
2022
- const defaultProps = {
2023
- envKey: "DATADOG_API_KEY",
2024
- roleTag: CDK$2.ROLE.MONITORING,
2025
- vendorTag: CDK$2.VENDOR.DATADOG,
2026
- ...props,
2027
- };
2028
- super(scope, id, defaultProps);
2029
- }
2030
- }
2031
-
2032
- class JaypieDnsRecord extends constructs.Construct {
2033
- constructor(scope, id, props) {
2034
- super(scope, id);
2035
- const { comment, recordName, type, values } = props;
2036
- const ttl = props.ttl || cdk__namespace.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
2037
- // Resolve the hosted zone (supports both string and IHostedZone)
2038
- const zone = resolveHostedZone(scope, {
2039
- name: `${id}HostedZone`,
2040
- zone: props.zone,
2041
- });
2042
- // Common properties for all record types
2043
- const baseProps = {
2044
- comment,
2045
- recordName,
2046
- ttl,
2047
- zone,
2048
- };
2049
- // Create the appropriate record based on type
2050
- switch (type) {
2051
- case CDK$2.DNS.RECORD.A: {
2052
- if (!Array.isArray(values) || values.length === 0) {
2053
- throw new errors.ConfigurationError("A record requires at least one IP address");
2054
- }
2055
- this.record = new route53.ARecord(this, "Record", {
2056
- ...baseProps,
2057
- target: route53.RecordTarget.fromIpAddresses(...values),
2058
- });
2059
- break;
2060
- }
2061
- case CDK$2.DNS.RECORD.CNAME: {
2062
- if (!Array.isArray(values) || values.length === 0) {
2063
- throw new errors.ConfigurationError("CNAME record requires a domain name");
2064
- }
2065
- this.record = new route53.CnameRecord(this, "Record", {
2066
- ...baseProps,
2067
- domainName: values[0],
2068
- });
2069
- break;
2070
- }
2071
- case CDK$2.DNS.RECORD.MX: {
2072
- if (!Array.isArray(values) || values.length === 0) {
2073
- throw new errors.ConfigurationError("MX record requires at least one mail server");
2074
- }
2075
- this.record = new route53.MxRecord(this, "Record", {
2076
- ...baseProps,
2077
- values: values,
2078
- });
2079
- break;
2080
- }
2081
- case CDK$2.DNS.RECORD.NS: {
2082
- if (!Array.isArray(values) || values.length === 0) {
2083
- throw new errors.ConfigurationError("NS record requires at least one name server");
2084
- }
2085
- this.record = new route53.NsRecord(this, "Record", {
2086
- ...baseProps,
2087
- values: values,
2088
- });
2089
- break;
2090
- }
2091
- case CDK$2.DNS.RECORD.TXT: {
2092
- if (!Array.isArray(values) || values.length === 0) {
2093
- throw new errors.ConfigurationError("TXT record requires at least one value");
2094
- }
2095
- this.record = new route53.TxtRecord(this, "Record", {
2096
- ...baseProps,
2097
- values: values,
2098
- });
2099
- break;
2100
- }
2101
- default:
2102
- throw new errors.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
2103
- }
2104
- // Add standard tags to the DNS record
2105
- cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.INFRASTRUCTURE);
2106
- cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2107
- }
2108
- }
2109
-
2110
- class JaypieEventsRule extends constructs.Construct {
2111
- /**
2112
- * Create a new EventBridge rule that targets a Lambda function
2113
- */
2114
- constructor(scope, idOrSourceOrProps, propsOrUndefined) {
2115
- // Handle overloaded constructor signatures
2116
- let props;
2117
- let id;
2118
- if (typeof idOrSourceOrProps === "string") {
2119
- // Check if it looks like an AWS source (starts with "aws.")
2120
- if (idOrSourceOrProps.startsWith("aws.")) {
2121
- // First param is source, second is props
2122
- props = propsOrUndefined || {};
2123
- props.source = idOrSourceOrProps;
2124
- // Generate ID from source
2125
- const sourceName = idOrSourceOrProps
2126
- .replace("aws.", "")
2127
- .split(".")
2128
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2129
- .join("");
2130
- id = props.id || `${sourceName}EventsRule`;
2131
- }
2132
- else {
2133
- // First param is ID, second is props
2134
- props = propsOrUndefined || {};
2135
- id = idOrSourceOrProps;
2136
- }
2137
- }
2138
- else {
2139
- // First param is props
2140
- props = idOrSourceOrProps || {};
2141
- if (props.source) {
2142
- const sourceName = typeof props.source === "string"
2143
- ? props.source
2144
- .replace("aws.", "")
2145
- .split(".")
2146
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2147
- .join("")
2148
- : "Events";
2149
- id = props.id || `${sourceName}EventsRule`;
2150
- }
2151
- else {
2152
- id = props.id || "EventsRule";
2153
- }
2154
- }
2155
- super(scope, id);
2156
- // Extract Jaypie-specific options
2157
- const { id: _id, project, service = CDK$2.SERVICE.DATADOG, source, targetFunction, vendor = CDK$2.VENDOR.DATADOG, ...ruleProps } = props;
2158
- // Resolve target function
2159
- this.targetFunction =
2160
- targetFunction || resolveDatadogForwarderFunction(scope);
2161
- // Build event pattern if source is specified
2162
- const eventPattern = source
2163
- ? {
2164
- ...ruleProps.eventPattern,
2165
- source: Array.isArray(source) ? source : [source],
2166
- }
2167
- : ruleProps.eventPattern;
2168
- // Build rule props
2169
- const finalRuleProps = {
2170
- ...ruleProps,
2171
- eventPattern,
2172
- targets: [
2173
- new awsEventsTargets.LambdaFunction(this.targetFunction, {
2174
- event: awsEvents.RuleTargetInput.fromEventPath("$"),
2175
- }),
2176
- ],
2177
- };
2178
- // Create the rule
2179
- this.rule = new awsEvents.Rule(this, "Rule", finalRuleProps);
2180
- // Add tags
2181
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2182
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.SERVICE, service);
2183
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.VENDOR, vendor);
2184
- if (project) {
2185
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.PROJECT, project);
2186
- }
2187
- }
2188
- }
2189
-
2190
- class JaypieGitHubDeployRole extends constructs.Construct {
2191
- constructor(scope, id = "GitHubDeployRole", props = {}) {
2192
- super(scope, id);
2193
- const { oidcProviderArn = cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
2194
- // Extract account ID from the scope
2195
- const accountId = cdk.Stack.of(this).account;
2196
- // Resolve repoRestriction from props or environment variables
2197
- let repoRestriction = propsRepoRestriction;
2198
- if (!repoRestriction) {
2199
- const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
2200
- if (!envRepo) {
2201
- throw new errors.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
2202
- }
2203
- // Extract organization from owner/repo format and create org-wide restriction
2204
- const organization = envRepo.split("/")[0];
2205
- repoRestriction = `repo:${organization}/*:*`;
2206
- }
2207
- // Create the IAM role
2208
- this._role = new awsIam.Role(this, "GitHubActionsRole", {
2209
- assumedBy: new awsIam.FederatedPrincipal(oidcProviderArn, {
2210
- StringLike: {
2211
- "token.actions.githubusercontent.com:sub": repoRestriction,
2212
- },
2213
- }, "sts:AssumeRoleWithWebIdentity"),
2214
- maxSessionDuration: cdk.Duration.hours(1),
2215
- path: "/",
2216
- });
2217
- cdk.Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
2218
- // Allow the role to access the GitHub OIDC provider
2219
- this._role.addToPolicy(new awsIam.PolicyStatement({
2220
- actions: ["sts:AssumeRoleWithWebIdentity"],
2221
- resources: [`arn:aws:iam::${accountId}:oidc-provider/*`],
2222
- }));
2223
- // Allow the role to deploy CDK apps
2224
- this._role.addToPolicy(new awsIam.PolicyStatement({
2225
- actions: [
2226
- "cloudformation:CreateStack",
2227
- "cloudformation:DeleteStack",
2228
- "cloudformation:DescribeStackEvents",
2229
- "cloudformation:DescribeStackResource",
2230
- "cloudformation:DescribeStackResources",
2231
- "cloudformation:DescribeStacks",
2232
- "cloudformation:GetTemplate",
2233
- "cloudformation:SetStackPolicy",
2234
- "cloudformation:UpdateStack",
2235
- "cloudformation:ValidateTemplate",
2236
- "iam:PassRole",
2237
- "route53:ListHostedZones*",
2238
- "s3:GetObject",
2239
- "s3:ListBucket",
2240
- ],
2241
- effect: awsIam.Effect.ALLOW,
2242
- resources: ["*"],
2243
- }));
2244
- this._role.addToPolicy(new awsIam.PolicyStatement({
2245
- actions: ["iam:PassRole", "sts:AssumeRole"],
2246
- effect: awsIam.Effect.ALLOW,
2247
- resources: [
2248
- "arn:aws:iam::*:role/cdk-hnb659fds-deploy-role-*",
2249
- "arn:aws:iam::*:role/cdk-hnb659fds-file-publishing-*",
2250
- "arn:aws:iam::*:role/cdk-readOnlyRole",
2251
- ],
2252
- }));
2253
- // Export the ARN of the role
2254
- if (output !== false) {
2255
- const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
2256
- new cdk.CfnOutput(this, outputId, {
2257
- value: this._role.roleArn,
2258
- });
2259
- }
2260
- }
2261
- get role() {
2262
- return this._role;
2263
- }
2264
- get roleArn() {
2265
- return this._role.roleArn;
2266
- }
2267
- get roleName() {
2268
- return this._role.roleName;
2269
- }
2270
- }
2271
-
2272
- class JaypieExpressLambda extends JaypieLambda {
2273
- constructor(scope, id, props) {
2274
- super(scope, id, {
2275
- timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
2276
- roleTag: CDK$2.ROLE.API,
2277
- ...props,
2278
- });
2279
- }
2280
- }
2281
-
2282
- const SERVICE = {
2283
- ROUTE53: "route53.amazonaws.com",
2284
- };
2285
- /**
2286
- * Check if a string is a valid hostname
2287
- */
2288
- function isValidHostname(str) {
2289
- // Check if it contains a dot and matches hostname pattern
2290
- if (!str.includes("."))
2291
- return false;
2292
- // Basic hostname validation: alphanumeric, hyphens, dots
2293
- // Each label must start and end with alphanumeric
2294
- const hostnameRegex = /^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i;
2295
- return hostnameRegex.test(str);
2296
- }
2297
- class JaypieHostedZone extends constructs.Construct {
2298
- /**
2299
- * Create a new hosted zone with query logging and optional DNS records
2300
- */
2301
- constructor(scope, idOrProps, propsOrRecords) {
2302
- // Handle overloaded constructor signatures
2303
- let props;
2304
- let id;
2305
- if (typeof idOrProps === "string") {
2306
- // If it's a valid hostname, treat it as zoneName
2307
- if (isValidHostname(idOrProps)) {
2308
- // Third param can be props object or records array
2309
- if (Array.isArray(propsOrRecords)) {
2310
- props = { zoneName: idOrProps, records: propsOrRecords };
2311
- }
2312
- else {
2313
- props = propsOrRecords || { zoneName: idOrProps };
2314
- // Set zoneName if not already set
2315
- if (!props.zoneName) {
2316
- props = { ...props, zoneName: idOrProps };
2317
- }
2318
- }
2319
- // Use id from props if provided, otherwise derive from zoneName
2320
- id = props.id || `${idOrProps}-HostedZone`;
2321
- }
2322
- else {
2323
- // Otherwise treat it as an explicit id
2324
- props = propsOrRecords;
2325
- id = idOrProps;
2326
- }
2327
- }
2328
- else {
2329
- // idOrProps is props
2330
- props = idOrProps;
2331
- id = props.id || `${props.zoneName}-HostedZone`;
2332
- }
2333
- super(scope, id);
2334
- const { zoneName, project } = props;
2335
- const destination = props.destination ?? true;
2336
- const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
2337
- // Create the log group
2338
- this.logGroup = new logs.LogGroup(this, "LogGroup", {
2339
- logGroupName: process.env.PROJECT_NONCE
2340
- ? `/aws/route53/${zoneName}-${process.env.PROJECT_NONCE}`
2341
- : `/aws/route53/${zoneName}`,
2342
- retention: logs.RetentionDays.ONE_WEEK,
2343
- });
2344
- // Add tags
2345
- cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
2346
- cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2347
- if (project) {
2348
- cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
2349
- }
2350
- // Grant Route 53 permissions to write to the log group
2351
- this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
2352
- // Add destination based on configuration
2353
- if (destination !== false) {
2354
- const lambdaDestination = destination === true
2355
- ? resolveDatadogLoggingDestination(scope)
2356
- : destination;
2357
- this.logGroup.addSubscriptionFilter("DatadogLambdaDestination", {
2358
- destination: lambdaDestination,
2359
- filterPattern: logs.FilterPattern.allEvents(),
2360
- });
2361
- }
2362
- // Create the hosted zone
2363
- this.hostedZone = new route53.HostedZone(this, "HostedZone", {
2364
- queryLogsLogGroupArn: this.logGroup.logGroupArn,
2365
- zoneName,
2366
- });
2367
- // Add tags
2368
- cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
2369
- cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2370
- if (project) {
2371
- cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
2372
- }
2373
- // Create DNS records if provided
2374
- this.dnsRecords = [];
2375
- if (props.records) {
2376
- props.records.forEach((recordConfig, index) => {
2377
- const { id, ...recordProps } = recordConfig;
2378
- // Generate a default ID if not provided
2379
- const recordId = id ||
2380
- `${recordProps.type}${recordProps.recordName ? `-${recordProps.recordName}` : ""}-${index}`;
2381
- const dnsRecord = new JaypieDnsRecord(this, recordId, {
2382
- ...recordProps,
2383
- zone: this.hostedZone,
2384
- });
2385
- this.dnsRecords.push(dnsRecord);
2386
- });
2387
- }
2388
- }
2389
- }
2390
-
2391
- const CDK = {
2392
- TAG: {
2393
- STACK_SHA: "stackSha",
2394
- },
2395
- };
2396
- class JaypieInfrastructureStack extends JaypieStack {
2397
- constructor(scope, id, props = {}) {
2398
- const { key = "infra", ...stackProps } = props;
2399
- // Handle stackName
2400
- if (!stackProps.stackName) {
2401
- stackProps.stackName = constructStackName(key);
2402
- }
2403
- super(scope, id, { key, ...stackProps });
2404
- // Add infrastructure-specific tag
2405
- if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
2406
- cdk.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
2407
- }
2408
- }
2409
- }
2410
-
2411
- class JaypieMongoDbSecret extends JaypieEnvSecret {
2412
- constructor(scope, id = "MongoConnectionString", props) {
2413
- const defaultProps = {
2414
- envKey: "MONGODB_URI",
2415
- roleTag: CDK$2.ROLE.STORAGE,
2416
- vendorTag: CDK$2.VENDOR.MONGODB,
2417
- ...props,
2418
- };
2419
- super(scope, id, defaultProps);
2420
- }
2421
- }
2422
-
2423
- class JaypieNextJs extends constructs.Construct {
2424
- constructor(scope, id, props) {
2425
- super(scope, id);
2426
- const domainName = props?.domainName || envHostname();
2427
- this.domainName = domainName;
2428
- const domainNameSanitized = domainName
2429
- .replace(/\./g, "-")
2430
- .replace(/[^a-zA-Z0-9]/g, "_");
2431
- const envSecrets = props?.envSecrets || {};
2432
- const nextjsPath = props?.nextjsPath?.startsWith("..")
2433
- ? path__namespace.join(process.cwd(), props.nextjsPath)
2434
- : props?.nextjsPath || path__namespace.join(process.cwd(), "..", "nextjs");
2435
- const paramsAndSecrets = resolveParamsAndSecrets();
2436
- const secrets = props?.secrets || [];
2437
- // Process secrets environment variables
2438
- const secretsEnvironment = Object.entries(envSecrets).reduce((acc, [key, secret]) => ({
2439
- ...acc,
2440
- [`SECRET_${key}`]: secret.secretName,
2441
- }), {});
2442
- // Process JaypieEnvSecret array
2443
- const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
2444
- if (secret.envKey) {
2445
- return {
2446
- ...acc,
2447
- [`SECRET_${secret.envKey}`]: secret.secretName,
2448
- };
2449
- }
2450
- return acc;
2451
- }, {});
2452
- // Process NEXT_PUBLIC_ environment variables
2453
- const nextPublicEnv = Object.entries(process.env).reduce((acc, [key, value]) => {
2454
- if (key.startsWith("NEXT_PUBLIC_") && value) {
2455
- return {
2456
- ...acc,
2457
- [key]: value,
2458
- };
2459
- }
2460
- return acc;
2461
- }, {});
2462
- const nextjs = new cdkNextjsStandalone.Nextjs(this, "NextJsApp", {
2463
- nextjsPath,
2464
- domainProps: {
2465
- domainName,
2466
- hostedZone: resolveHostedZone(this, {
2467
- zone: props?.hostedZone,
2468
- }),
2469
- },
2470
- environment: {
2471
- ...jaypieLambdaEnv(),
2472
- ...secretsEnvironment,
2473
- ...jaypieSecretsEnvironment,
2474
- ...nextPublicEnv,
2475
- NEXT_PUBLIC_SITE_URL: `https://${domainName}`,
2476
- },
2477
- overrides: {
2478
- nextjsDistribution: {
2479
- imageCachePolicyProps: {
2480
- cachePolicyName: `NextJsImageCachePolicy-${domainNameSanitized}`,
2481
- },
2482
- serverCachePolicyProps: {
2483
- cachePolicyName: `NextJsServerCachePolicy-${domainNameSanitized}`,
2484
- },
2485
- },
2486
- nextjsImage: {
2487
- functionProps: {
2488
- paramsAndSecrets,
2489
- },
2490
- },
2491
- nextjsServer: {
2492
- functionProps: {
2493
- paramsAndSecrets,
2494
- },
2495
- },
2496
- },
2497
- });
2498
- addDatadogLayers(nextjs.imageOptimizationFunction);
2499
- addDatadogLayers(nextjs.serverFunction.lambdaFunction);
2500
- // Grant secret read permissions
2501
- Object.values(envSecrets).forEach((secret) => {
2502
- secret.grantRead(nextjs.serverFunction.lambdaFunction);
2503
- });
2504
- // Grant read permissions for JaypieEnvSecrets
2505
- secrets.forEach((secret) => {
2506
- secret.grantRead(nextjs.serverFunction.lambdaFunction);
2507
- });
2508
- }
2509
- }
2510
-
2511
- class JaypieOpenAiSecret extends JaypieEnvSecret {
2512
- constructor(scope, id = "OpenAiApiKey", props) {
2513
- const defaultProps = {
2514
- envKey: "OPENAI_API_KEY",
2515
- roleTag: CDK$2.ROLE.PROCESSING,
2516
- vendorTag: CDK$2.VENDOR.OPENAI,
2517
- ...props,
2518
- };
2519
- super(scope, id, defaultProps);
2520
- }
2521
- }
2522
-
2523
- class JaypieOrganizationTrail extends constructs.Construct {
2524
- /**
2525
- * Create a new organization CloudTrail with S3 bucket and lifecycle policies
2526
- */
2527
- constructor(scope, idOrProps, propsOrUndefined) {
2528
- // Handle overloaded constructor signatures
2529
- let props;
2530
- let id;
2531
- if (typeof idOrProps === "string") {
2532
- // First param is ID, second is props
2533
- props = propsOrUndefined || {};
2534
- id = idOrProps;
2535
- }
2536
- else {
2537
- // First param is props
2538
- props = idOrProps || {};
2539
- const defaultName = process.env.PROJECT_NONCE
2540
- ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2541
- : "organization-cloudtrail";
2542
- id = props.id || `${props.trailName || defaultName}-Trail`;
2543
- }
2544
- super(scope, id);
2545
- // Resolve options with defaults
2546
- const { bucketName = process.env.PROJECT_NONCE
2547
- ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2548
- : "organization-cloudtrail", enableDatadogNotifications = true, enableFileValidation = false, expirationDays = 365, glacierTransitionDays = 180, infrequentAccessTransitionDays = 30, project, service = CDK$2.SERVICE.INFRASTRUCTURE, trailName = process.env.PROJECT_NONCE
2549
- ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2550
- : "organization-cloudtrail", } = props;
2551
- // Create the S3 bucket for CloudTrail logs
2552
- this.bucket = new s3.Bucket(this, "Bucket", {
2553
- accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
2554
- bucketName,
2555
- lifecycleRules: [
2556
- {
2557
- expiration: cdk__namespace.Duration.days(expirationDays),
2558
- transitions: [
2559
- {
2560
- storageClass: s3.StorageClass.INFREQUENT_ACCESS,
2561
- transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
2562
- },
2563
- {
2564
- storageClass: s3.StorageClass.GLACIER,
2565
- transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
2566
- },
2567
- ],
2568
- },
2569
- ],
2570
- });
2571
- // Add CloudTrail bucket policies
2572
- this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2573
- actions: ["s3:GetBucketAcl"],
2574
- effect: awsIam.Effect.ALLOW,
2575
- principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2576
- resources: [this.bucket.bucketArn],
2577
- }));
2578
- this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2579
- actions: ["s3:PutObject"],
2580
- conditions: {
2581
- StringEquals: {
2582
- "s3:x-amz-acl": "bucket-owner-full-control",
2583
- },
2584
- },
2585
- effect: awsIam.Effect.ALLOW,
2586
- principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2587
- resources: [`${this.bucket.bucketArn}/*`],
2588
- }));
2589
- // Add tags to bucket
2590
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
2591
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2592
- if (project) {
2593
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
2594
- }
2595
- // Add Datadog notifications if enabled
2596
- if (enableDatadogNotifications) {
2597
- const datadogForwarderFunction = resolveDatadogForwarderFunction(scope);
2598
- this.bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(datadogForwarderFunction));
2599
- }
2600
- // Create the organization trail
2601
- this.trail = new awsCloudtrail.Trail(this, "Trail", {
2602
- bucket: this.bucket,
2603
- enableFileValidation,
2604
- isOrganizationTrail: true,
2605
- managementEvents: awsCloudtrail.ReadWriteType.ALL,
2606
- trailName,
2607
- });
2608
- // Add tags to trail
2609
- cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.SERVICE, service);
2610
- cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2611
- if (project) {
2612
- cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.PROJECT, project);
2613
- }
2614
- }
2615
- }
2616
-
2617
- /**
2618
- * JaypieSsoPermissions Construct
2619
- *
2620
- * Creates and manages AWS IAM Identity Center (SSO) permission sets and assignments
2621
- *
2622
- * @example
2623
- * const permissionSets = new JaypieSsoPermissions(this, "PermissionSets", {
2624
- * iamIdentityCenterArn: "arn:aws:sso:::instance/...",
2625
- * administratorGroupId: "b4c8b438-4031-7000-782d-5046945fb956",
2626
- * analystGroupId: "2488f4e8-d061-708e-abe1-c315f0e30005",
2627
- * developerGroupId: "b438a4f8-e0e1-707c-c6e8-21841daf9ad1",
2628
- * administratorAccountAssignments: {
2629
- * "211125635435": ["Administrator", "Analyst", "Developer"],
2630
- * "381492033431": ["Administrator", "Analyst"],
2631
- * },
2632
- * analystAccountAssignments: {
2633
- * "211125635435": ["Analyst", "Developer"],
2634
- * "381492033431": [],
2635
- * },
2636
- * developerAccountAssignments: {
2637
- * "211125635435": ["Analyst", "Developer"],
2638
- * "381492033431": [],
2639
- * },
2640
- * });
2641
- */
2642
- class JaypieSsoPermissions extends constructs.Construct {
2643
- constructor(scope, id, props) {
2644
- super(scope, id);
2645
- const { iamIdentityCenterArn: iamIdentityCenterArnProp, administratorGroupId, analystGroupId, developerGroupId, administratorAccountAssignments, analystAccountAssignments, developerAccountAssignments, } = props;
2646
- const iamIdentityCenterArn = iamIdentityCenterArnProp || process.env.CDK_ENV_IAM_IDENTITY_CENTER_ARN;
2647
- if (!iamIdentityCenterArn) {
2648
- // If no IAM Identity Center ARN provided, skip SSO setup
2649
- return;
2650
- }
2651
- //
2652
- // Permission Sets
2653
- //
2654
- this.administratorPermissionSet = new awsSso.CfnPermissionSet(this, "AdministratorPermissionSet", {
2655
- // Required
2656
- instanceArn: iamIdentityCenterArn,
2657
- name: "Administrator",
2658
- // Optional
2659
- description: "Unrestricted access",
2660
- inlinePolicy: {
2661
- Version: "2012-10-17",
2662
- Statement: [
2663
- {
2664
- Effect: "Allow",
2665
- Action: [
2666
- "aws-portal:ViewUsage",
2667
- "aws-portal:ViewBilling",
2668
- "budgets:*",
2669
- "cur:DescribeReportDefinitions",
2670
- "cur:PutReportDefinition",
2671
- "cur:DeleteReportDefinition",
2672
- "cur:ModifyReportDefinition",
2673
- ],
2674
- Resource: "*",
2675
- },
2676
- ],
2677
- },
2678
- managedPolicies: [
2679
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess")
2680
- .managedPolicyArn,
2681
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2682
- ],
2683
- sessionDuration: cdk.Duration.hours(1).toIsoString(),
2684
- tags: [
2685
- {
2686
- key: CDK$2.TAG.SERVICE,
2687
- value: CDK$2.SERVICE.SSO,
2688
- },
2689
- {
2690
- key: CDK$2.TAG.ROLE,
2691
- value: CDK$2.ROLE.SECURITY,
2692
- },
2693
- ],
2694
- });
2695
- this.analystPermissionSet = new awsSso.CfnPermissionSet(this, "AnalystPermissionSet", {
2696
- // Required
2697
- instanceArn: iamIdentityCenterArn,
2698
- name: "Analyst",
2699
- // Optional
2700
- description: "Read-only access; may expand to limited write access",
2701
- inlinePolicy: {
2702
- Version: "2012-10-17",
2703
- Statement: [
2704
- {
2705
- Effect: "Allow",
2706
- Action: [
2707
- "aws-portal:ViewUsage",
2708
- "aws-portal:ViewBilling",
2709
- "budgets:Describe*",
2710
- "budgets:View*",
2711
- "ce:Get*",
2712
- "ce:List*",
2713
- "cloudformation:Describe*",
2714
- "cloudformation:Get*",
2715
- "cloudformation:List*",
2716
- "cloudwatch:BatchGet*",
2717
- "cloudwatch:Get*",
2718
- "cloudwatch:List*",
2719
- "cost-optimization-hub:Get*",
2720
- "cost-optimization-hub:List*",
2721
- "ec2:Describe*",
2722
- "ec2:Get*",
2723
- "ec2:List*",
2724
- "ec2:Search*",
2725
- "iam:Get*",
2726
- "iam:List*",
2727
- "iam:PassRole",
2728
- "lambda:Get*",
2729
- "lambda:List*",
2730
- "logs:Describe*",
2731
- "logs:Get*",
2732
- "logs:List*",
2733
- "pipes:Describe*",
2734
- "pipes:List*",
2735
- "s3:Get*",
2736
- "s3:List*",
2737
- "secretsmanager:GetRandomPassword",
2738
- "secretsmanager:GetResourcePolicy",
2739
- "secretsmanager:List*",
2740
- "securityhub:Describe*",
2741
- "securityhub:Get*",
2742
- "securityhub:List*",
2743
- "servicecatalog:Describe*",
2744
- "sns:Get*",
2745
- "sns:List*",
2746
- "sqs:Get*",
2747
- "sqs:List*",
2748
- "states:Describe*",
2749
- "states:Get*",
2750
- "states:List*",
2751
- "tag:*",
2752
- "uxc:*",
2753
- "xray:*",
2754
- ],
2755
- Resource: "*",
2756
- },
2757
- ],
2758
- },
2759
- managedPolicies: [
2760
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
2761
- .managedPolicyArn,
2762
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2763
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
2764
- .managedPolicyArn,
2765
- ],
2766
- sessionDuration: cdk.Duration.hours(12).toIsoString(),
2767
- tags: [
2768
- {
2769
- key: CDK$2.TAG.SERVICE,
2770
- value: CDK$2.SERVICE.SSO,
2771
- },
2772
- {
2773
- key: CDK$2.TAG.ROLE,
2774
- value: CDK$2.ROLE.SECURITY,
2775
- },
2776
- ],
2777
- });
2778
- this.developerPermissionSet = new awsSso.CfnPermissionSet(this, "DeveloperPermissionSet", {
2779
- // Required
2780
- instanceArn: iamIdentityCenterArn,
2781
- name: "Developer",
2782
- // Optional
2783
- description: "Administrative access with limited restrictions",
2784
- inlinePolicy: {
2785
- Version: "2012-10-17",
2786
- Statement: [
2787
- {
2788
- Effect: "Allow",
2789
- Action: [
2790
- "budgets:*",
2791
- "ce:*",
2792
- "cloudformation:*",
2793
- "cloudwatch:*",
2794
- "cost-optimization-hub:*",
2795
- "ec2:*",
2796
- "iam:Get*",
2797
- "iam:List*",
2798
- "iam:PassRole",
2799
- "lambda:*",
2800
- "logs:*",
2801
- "pipes:*",
2802
- "s3:*",
2803
- "secretsmanager:*",
2804
- "securityhub:*",
2805
- "servicecatalog:*",
2806
- "sns:*",
2807
- "sqs:*",
2808
- "states:*",
2809
- "tag:*",
2810
- "uxc:*",
2811
- "xray:*",
2812
- ],
2813
- Resource: "*",
2814
- },
2815
- ],
2816
- },
2817
- managedPolicies: [
2818
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
2819
- .managedPolicyArn,
2820
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2821
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
2822
- .managedPolicyArn,
2823
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
2824
- ],
2825
- sessionDuration: cdk.Duration.hours(4).toIsoString(),
2826
- tags: [
2827
- {
2828
- key: CDK$2.TAG.SERVICE,
2829
- value: CDK$2.SERVICE.SSO,
2830
- },
2831
- {
2832
- key: CDK$2.TAG.ROLE,
2833
- value: CDK$2.ROLE.SECURITY,
2834
- },
2835
- ],
2836
- });
2837
- // Map permission set names to their ARNs and labels
2838
- const permissionSetMap = {
2839
- Administrator: {
2840
- arn: this.administratorPermissionSet.attrPermissionSetArn,
2841
- label: "Administrator",
2842
- },
2843
- Analyst: {
2844
- arn: this.analystPermissionSet.attrPermissionSetArn,
2845
- label: "Analyst",
2846
- },
2847
- Developer: {
2848
- arn: this.developerPermissionSet.attrPermissionSetArn,
2849
- label: "Developer",
2850
- },
2851
- };
2852
- //
2853
- // Assignments
2854
- //
2855
- // Helper function to create assignments for a group
2856
- const createAssignments = (groupId, accountAssignments) => {
2857
- if (!groupId || !accountAssignments) {
2858
- return; // Skip if group ID or assignments not provided
2859
- }
2860
- Object.keys(accountAssignments).forEach((accountId) => {
2861
- const permissionSetNames = accountAssignments[accountId];
2862
- permissionSetNames.forEach((permissionSetName) => {
2863
- const permissionSet = permissionSetMap[permissionSetName];
2864
- if (!permissionSet) {
2865
- throw new errors.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
2866
- }
2867
- const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
2868
- // Required
2869
- instanceArn: iamIdentityCenterArn,
2870
- permissionSetArn: permissionSet.arn,
2871
- principalId: groupId,
2872
- principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
2873
- targetId: accountId,
2874
- targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
2875
- });
2876
- cdk.Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
2877
- cdk.Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
2878
- });
2879
- });
2880
- };
2881
- // Create assignments for each group
2882
- createAssignments(administratorGroupId, administratorAccountAssignments);
2883
- createAssignments(analystGroupId, analystAccountAssignments);
2884
- createAssignments(developerGroupId, developerAccountAssignments);
2885
- }
2886
- }
2887
-
2888
- //
2889
- //
2890
- // Constants
2891
- //
2892
- const DEFAULT_APPLICATION_ID = "arn:aws:serverlessrepo:us-east-2:004480582608:applications/SSOSync";
2893
- const DEFAULT_APPLICATION_VERSION = "2.3.3";
2894
- const DEFAULT_GOOGLE_GROUP_MATCH = "name:AWS*";
2895
- //
2896
- //
2897
- // Class
2898
- //
2899
- class JaypieSsoSyncApplication extends constructs.Construct {
2900
- constructor(scope, id = "SsoSyncApplication", props = {}) {
2901
- super(scope, id);
2902
- const { googleAdminEmail, googleAdminEmailEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_ADMIN_EMAIL", googleCredentials, googleCredentialsEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_CREDENTIALS", googleGroupMatch, googleGroupMatchEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_GROUP_MATCH", identityStoreId, identityStoreIdEnvKey = "CDK_ENV_SSOSYNC_IDENTITY_STORE_ID", scimEndpointAccessToken, scimEndpointAccessTokenEnvKey = "CDK_ENV_SCIM_ENDPOINT_ACCESS_TOKEN", scimEndpointUrl, scimEndpointUrlEnvKey = "CDK_ENV_SSOSYNC_SCIM_ENDPOINT_URL", semanticVersion, semanticVersionEnvKey = "CDK_ENV_SSOSYNC_SEMANTIC_VERSION", ssoSyncApplicationId = DEFAULT_APPLICATION_ID, tags, } = props;
2903
- // Resolve all values from props or environment variables
2904
- const resolvedGoogleAdminEmail = googleAdminEmail || process.env[googleAdminEmailEnvKey];
2905
- const resolvedGoogleCredentials = googleCredentials || process.env[googleCredentialsEnvKey];
2906
- const resolvedGoogleGroupMatch = googleGroupMatch ||
2907
- process.env[googleGroupMatchEnvKey] ||
2908
- DEFAULT_GOOGLE_GROUP_MATCH;
2909
- const resolvedIdentityStoreId = identityStoreId || process.env[identityStoreIdEnvKey];
2910
- const resolvedScimEndpointAccessToken = scimEndpointAccessToken || process.env[scimEndpointAccessTokenEnvKey];
2911
- const resolvedScimEndpointUrl = scimEndpointUrl || process.env[scimEndpointUrlEnvKey];
2912
- const resolvedSemanticVersion = semanticVersion ||
2913
- process.env[semanticVersionEnvKey] ||
2914
- DEFAULT_APPLICATION_VERSION;
2915
- // Validate required parameters
2916
- const missingParams = [];
2917
- if (!resolvedGoogleAdminEmail) {
2918
- missingParams.push(`googleAdminEmail or ${googleAdminEmailEnvKey} environment variable`);
2919
- }
2920
- if (!resolvedGoogleCredentials) {
2921
- missingParams.push(`googleCredentials or ${googleCredentialsEnvKey} environment variable`);
2922
- }
2923
- if (!resolvedIdentityStoreId) {
2924
- missingParams.push(`identityStoreId or ${identityStoreIdEnvKey} environment variable`);
2925
- }
2926
- if (!resolvedScimEndpointAccessToken) {
2927
- missingParams.push(`scimEndpointAccessToken or ${scimEndpointAccessTokenEnvKey} environment variable`);
2928
- }
2929
- if (!resolvedScimEndpointUrl) {
2930
- missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
2931
- }
2932
- if (missingParams.length > 0) {
2933
- throw new errors.ConfigurationError(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
2934
- }
2935
- // Create the SSO Sync Application
2936
- // Type assertion is safe because we validated all required values above
2937
- this._application = new awsSam.CfnApplication(this, "Application", {
2938
- location: {
2939
- applicationId: ssoSyncApplicationId,
2940
- semanticVersion: resolvedSemanticVersion,
2941
- },
2942
- parameters: {
2943
- GoogleAdminEmail: resolvedGoogleAdminEmail,
2944
- GoogleCredentials: resolvedGoogleCredentials,
2945
- GoogleGroupMatch: resolvedGoogleGroupMatch,
2946
- IdentityStoreID: resolvedIdentityStoreId,
2947
- Region: cdk.Stack.of(this).region,
2948
- SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
2949
- SCIMEndpointUrl: resolvedScimEndpointUrl,
2950
- },
2951
- });
2952
- // Add tags
2953
- const defaultTags = {
2954
- [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
2955
- };
2956
- const allTags = { ...defaultTags, ...tags };
2957
- Object.entries(allTags).forEach(([key, value]) => {
2958
- cdk.Tags.of(this._application).add(key, value);
2959
- });
2960
- }
2961
- get application() {
2962
- return this._application;
2963
- }
2964
- }
2965
-
2966
- class JaypieWebDeploymentBucket extends constructs.Construct {
2967
- constructor(scope, id, props = {}) {
2968
- super(scope, id);
2969
- const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
2970
- // Environment variable validation
2971
- if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
2972
- !isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
2973
- throw new errors.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
2974
- }
2975
- if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
2976
- !isValidHostname$1(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
2977
- throw new errors.ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
2978
- }
2979
- if (process.env.CDK_ENV_HOSTED_ZONE &&
2980
- !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
2981
- throw new errors.ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
2982
- }
2983
- // Determine host from props or environment
2984
- let host = props.host;
2985
- if (!host) {
2986
- try {
2987
- host =
2988
- process.env.CDK_ENV_WEB_HOST ||
2989
- mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
2990
- process.env.CDK_ENV_HOSTED_ZONE ||
2991
- "");
2992
- }
2993
- catch {
2994
- host = undefined;
2995
- }
2996
- }
2997
- if (host && !isValidHostname$1(host)) {
2998
- throw new errors.ConfigurationError("Host is not a valid hostname");
2999
- }
3000
- // Determine zone from props or environment
3001
- const zone = props.zone ||
3002
- process.env.CDK_ENV_WEB_HOSTED_ZONE ||
3003
- process.env.CDK_ENV_HOSTED_ZONE;
3004
- // Create the S3 bucket
3005
- this.bucket = new s3__namespace.Bucket(this, "DestinationBucket", {
3006
- accessControl: s3__namespace.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
3007
- autoDeleteObjects: true,
3008
- blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS_ONLY,
3009
- bucketName: props.name || constructEnvName("web"),
3010
- publicReadAccess: true,
3011
- removalPolicy: cdk.RemovalPolicy.DESTROY,
3012
- versioned: false,
3013
- websiteErrorDocument: "index.html",
3014
- websiteIndexDocument: "index.html",
3015
- ...props,
3016
- });
3017
- // Delegate IBucket properties to the bucket
3018
- this.bucketArn = this.bucket.bucketArn;
3019
- this.bucketDomainName = this.bucket.bucketDomainName;
3020
- this.bucketDualStackDomainName = this.bucket.bucketDualStackDomainName;
3021
- this.bucketName = this.bucket.bucketName;
3022
- this.bucketRegionalDomainName = this.bucket.bucketRegionalDomainName;
3023
- this.bucketWebsiteDomainName = this.bucket.bucketWebsiteDomainName;
3024
- this.bucketWebsiteUrl = this.bucket.bucketWebsiteUrl;
3025
- this.encryptionKey = this.bucket.encryptionKey;
3026
- this.isWebsite = this.bucket.isWebsite;
3027
- this.notificationsHandlerRole = undefined;
3028
- this.policy = this.bucket.policy;
3029
- cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
3030
- // Create deployment role if repository is configured
3031
- let repo;
3032
- if (process.env.CDK_ENV_REPO) {
3033
- repo = `repo:${process.env.CDK_ENV_REPO}:*`;
3034
- }
3035
- if (repo) {
3036
- const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
3037
- assumedBy: new awsIam.FederatedPrincipal(cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
3038
- StringLike: {
3039
- "token.actions.githubusercontent.com:sub": repo,
3040
- },
3041
- }, "sts:AssumeRoleWithWebIdentity"),
3042
- maxSessionDuration: cdk.Duration.hours(1),
3043
- });
3044
- cdk.Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
3045
- // Allow the role to write to the bucket
3046
- bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3047
- effect: awsIam.Effect.ALLOW,
3048
- actions: [
3049
- "s3:DeleteObject",
3050
- "s3:GetObject",
3051
- "s3:ListObjectsV2",
3052
- "s3:PutObject",
3053
- ],
3054
- resources: [`${this.bucket.bucketArn}/*`],
3055
- }));
3056
- bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3057
- effect: awsIam.Effect.ALLOW,
3058
- actions: ["s3:ListBucket"],
3059
- resources: [this.bucket.bucketArn],
3060
- }));
3061
- // Allow the role to describe the current stack
3062
- const stack = cdk.Stack.of(this);
3063
- bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3064
- actions: ["cloudformation:DescribeStacks"],
3065
- effect: awsIam.Effect.ALLOW,
3066
- resources: [
3067
- `arn:aws:cloudformation:${stack.region}:${stack.account}:stack/${stack.stackName}/*`,
3068
- ],
3069
- }));
3070
- this.deployRoleArn = bucketDeployRole.roleArn;
3071
- // Output the deploy role ARN
3072
- new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
3073
- value: bucketDeployRole.roleArn,
3074
- });
3075
- }
3076
- // Create CloudFront distribution and certificate if host and zone are provided
3077
- if (host && zone) {
3078
- let hostedZone;
3079
- if (typeof zone === "string") {
3080
- hostedZone = route53__namespace.HostedZone.fromLookup(this, "HostedZone", {
3081
- domainName: zone,
3082
- });
3083
- }
3084
- else if (zone instanceof JaypieHostedZone) {
3085
- hostedZone = zone.hostedZone;
3086
- }
3087
- else {
3088
- hostedZone = zone;
3089
- }
3090
- // Create certificate if not provided
3091
- if (props.certificate !== false) {
3092
- this.certificate =
3093
- typeof props.certificate === "object"
3094
- ? props.certificate
3095
- : new acm__namespace.Certificate(this, "Certificate", {
3096
- domainName: host,
3097
- validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
3098
- });
3099
- new cdk.CfnOutput(this, "CertificateArn", {
3100
- value: this.certificate.certificateArn,
3101
- });
3102
- cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
3103
- }
3104
- // Create CloudFront distribution
3105
- this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
3106
- defaultBehavior: {
3107
- cachePolicy: cloudfront__namespace.CachePolicy.CACHING_DISABLED,
3108
- origin: new origins__namespace.S3StaticWebsiteOrigin(this.bucket),
3109
- viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
3110
- },
3111
- certificate: this.certificate,
3112
- domainNames: [host],
3113
- });
3114
- cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
3115
- // If this is production, enable caching on everything but index.html
3116
- if (isProductionEnv()) {
3117
- this.distribution.addBehavior("/*", new origins__namespace.S3StaticWebsiteOrigin(this.bucket), {
3118
- viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
3119
- cachePolicy: cloudfront__namespace.CachePolicy.CACHING_OPTIMIZED,
3120
- });
3121
- }
3122
- // Create DNS record
3123
- const record = new route53__namespace.ARecord(this, "AliasRecord", {
3124
- recordName: host,
3125
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
3126
- zone: hostedZone,
3127
- });
3128
- cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
3129
- this.distributionDomainName = this.distribution.distributionDomainName;
3130
- }
3131
- }
3132
- // Implement remaining IBucket methods by delegating to the bucket
3133
- addEventNotification(event, dest, ...filters) {
3134
- this.bucket.addEventNotification(event, dest, ...filters);
3135
- }
3136
- addObjectCreatedNotification(dest, ...filters) {
3137
- this.bucket.addObjectCreatedNotification(dest, ...filters);
3138
- }
3139
- addObjectRemovedNotification(dest, ...filters) {
3140
- this.bucket.addObjectRemovedNotification(dest, ...filters);
3141
- }
3142
- addToResourcePolicy(permission) {
3143
- return this.bucket.addToResourcePolicy(permission);
3144
- }
3145
- arnForObjects(keyPattern) {
3146
- return this.bucket.arnForObjects(keyPattern);
3147
- }
3148
- grantDelete(identity, objectsKeyPattern) {
3149
- return this.bucket.grantDelete(identity, objectsKeyPattern);
3150
- }
3151
- grantPublicAccess(allowedActions, keyPrefix) {
3152
- return keyPrefix
3153
- ? this.bucket.grantPublicAccess(allowedActions, keyPrefix)
3154
- : this.bucket.grantPublicAccess(allowedActions);
3155
- }
3156
- grantPut(identity, objectsKeyPattern) {
3157
- return this.bucket.grantPut(identity, objectsKeyPattern);
3158
- }
3159
- grantPutAcl(identity, objectsKeyPattern) {
3160
- return this.bucket.grantPutAcl(identity, objectsKeyPattern);
3161
- }
3162
- grantRead(identity, objectsKeyPattern) {
3163
- return this.bucket.grantRead(identity, objectsKeyPattern);
3164
- }
3165
- grantReadWrite(identity, objectsKeyPattern) {
3166
- return this.bucket.grantReadWrite(identity, objectsKeyPattern);
3167
- }
3168
- grantWrite(identity, objectsKeyPattern) {
3169
- return this.bucket.grantWrite(identity, objectsKeyPattern);
3170
- }
3171
- grantReplicationPermission(identity, props) {
3172
- return this.bucket.grantReplicationPermission(identity, props);
3173
- }
3174
- s3UrlForObject(key) {
3175
- return this.bucket.s3UrlForObject(key);
3176
- }
3177
- urlForObject(key) {
3178
- return this.bucket.urlForObject(key);
3179
- }
3180
- virtualHostedUrlForObject(key, options) {
3181
- return this.bucket.virtualHostedUrlForObject(key, options);
3182
- }
3183
- transferAccelerationUrlForObject(key) {
3184
- return this.bucket.transferAccelerationUrlForObject(key);
3185
- }
3186
- onCloudTrailEvent(id, options) {
3187
- return this.bucket.onCloudTrailEvent(id, options);
3188
- }
3189
- onCloudTrailPutObject(id, options) {
3190
- return this.bucket.onCloudTrailPutObject(id, options);
3191
- }
3192
- onCloudTrailWriteObject(id, options) {
3193
- return this.bucket.onCloudTrailWriteObject(id, options);
3194
- }
3195
- addCorsRule(rule) {
3196
- this.bucket.addCorsRule(rule);
3197
- }
3198
- addInventory(inventory) {
3199
- this.bucket.addInventory(inventory);
3200
- }
3201
- addLifecycleRule(rule) {
3202
- this.bucket.addLifecycleRule(rule);
3203
- }
3204
- addMetric(metric) {
3205
- this.bucket.addMetric(metric);
3206
- }
3207
- enableEventBridgeNotification() {
3208
- this.bucket.enableEventBridgeNotification();
3209
- }
3210
- addReplicationPolicy(policy) {
3211
- this.bucket.addReplicationPolicy(policy);
3212
- }
3213
- get stack() {
3214
- return this.bucket.stack;
3215
- }
3216
- get env() {
3217
- return this.bucket.env;
3218
- }
3219
- applyRemovalPolicy(policy) {
3220
- this.bucket.applyRemovalPolicy(policy);
3221
- }
3222
- get bucketRef() {
3223
- return {
3224
- bucketArn: this.bucket.bucketArn,
3225
- bucketName: this.bucket.bucketName,
3226
- };
3227
- }
3228
- }
3229
-
3230
- class JaypieStaticWebBucket extends JaypieWebDeploymentBucket {
3231
- constructor(scope, id, props = {}) {
3232
- // Handle overloaded signatures: (scope), (scope, props), (scope, id, props)
3233
- let resolvedId;
3234
- let resolvedProps;
3235
- if (typeof id === "string") {
3236
- resolvedId = id;
3237
- resolvedProps = props;
3238
- }
3239
- else if (typeof id === "object") {
3240
- resolvedId = "JaypieStaticWebBucket";
3241
- resolvedProps = id;
3242
- }
3243
- else {
3244
- resolvedId = "JaypieStaticWebBucket";
3245
- resolvedProps = props;
3246
- }
3247
- const host = resolvedProps.host ?? envHostname({ subdomain: "static" });
3248
- const name = resolvedProps.name ?? constructEnvName("static");
3249
- const roleTag = resolvedProps.roleTag ?? CDK$2.ROLE.HOSTING;
3250
- // Only use default zone if zone is not explicitly provided (including undefined)
3251
- const zone = "zone" in resolvedProps
3252
- ? resolvedProps.zone
3253
- : process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
3254
- super(scope, resolvedId, {
3255
- ...resolvedProps,
3256
- host,
3257
- name,
3258
- roleTag,
3259
- zone,
3260
- });
3261
- }
3262
- }
3263
-
3264
- class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
3265
- constructor(scope, id = "TraceSigningKey", props) {
3266
- const defaultProps = {
3267
- envKey: "TRACE_SIGNING_KEY",
3268
- roleTag: CDK$2.ROLE.API,
3269
- vendorTag: CDK$2.VENDOR.KNOWTRACE,
3270
- ...props,
3271
- };
3272
- super(scope, id, defaultProps);
3273
- }
3274
- }
3275
-
3276
- exports.CDK = CDK$2;
3277
- exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
3278
- exports.JaypieApiGateway = JaypieApiGateway;
3279
- exports.JaypieAppStack = JaypieAppStack;
3280
- exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
3281
- exports.JaypieDatadogBucket = JaypieDatadogBucket;
3282
- exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
3283
- exports.JaypieDatadogSecret = JaypieDatadogSecret;
3284
- exports.JaypieDistribution = JaypieDistribution;
3285
- exports.JaypieDnsRecord = JaypieDnsRecord;
3286
- exports.JaypieEnvSecret = JaypieEnvSecret;
3287
- exports.JaypieEventsRule = JaypieEventsRule;
3288
- exports.JaypieExpressLambda = JaypieExpressLambda;
3289
- exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
3290
- exports.JaypieHostedZone = JaypieHostedZone;
3291
- exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
3292
- exports.JaypieLambda = JaypieLambda;
3293
- exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
3294
- exports.JaypieNextJs = JaypieNextJs;
3295
- exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
3296
- exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
3297
- exports.JaypieQueuedLambda = JaypieQueuedLambda;
3298
- exports.JaypieSsoPermissions = JaypieSsoPermissions;
3299
- exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
3300
- exports.JaypieStack = JaypieStack;
3301
- exports.JaypieStaticWebBucket = JaypieStaticWebBucket;
3302
- exports.JaypieTraceSigningKeySecret = JaypieTraceSigningKeySecret;
3303
- exports.JaypieWebDeploymentBucket = JaypieWebDeploymentBucket;
3304
- exports.addDatadogLayers = addDatadogLayers;
3305
- exports.constructEnvName = constructEnvName;
3306
- exports.constructStackName = constructStackName;
3307
- exports.constructTagger = constructTagger;
3308
- exports.envHostname = envHostname;
3309
- exports.extendDatadogRole = extendDatadogRole;
3310
- exports.isEnv = isEnv;
3311
- exports.isProductionEnv = isProductionEnv;
3312
- exports.isSandboxEnv = isSandboxEnv;
3313
- exports.isValidHostname = isValidHostname$1;
3314
- exports.isValidSubdomain = isValidSubdomain;
3315
- exports.jaypieLambdaEnv = jaypieLambdaEnv;
3316
- exports.mergeDomain = mergeDomain;
3317
- exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
3318
- exports.resolveDatadogLayers = resolveDatadogLayers;
3319
- exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;
3320
- exports.resolveHostedZone = resolveHostedZone;
3321
- exports.resolveParamsAndSecrets = resolveParamsAndSecrets;
3322
- //# sourceMappingURL=index.cjs.map