@jaypie/constructs 1.1.64 → 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 +2 -2
  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 -41
  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 -77
  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 -3335
  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 -41
  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 -77
  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 -3259
  131. package/dist/esm/index.js.map +0 -1
@@ -1,3335 +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 = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
905
- supportsInlineCode: true,
906
- }), runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
907
- // Get base environment with defaults
908
- const environment = jaypieLambdaEnv({ initialEnvironment });
909
- const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
910
- // Create a working copy of layers
911
- const resolvedLayers = [...layers];
912
- // Process secrets environment variables
913
- const secretsEnvironment = Object.entries(envSecrets).reduce((acc, [key, secret]) => ({
914
- ...acc,
915
- [`SECRET_${key}`]: secret.secretName,
916
- }), {});
917
- // Process JaypieEnvSecret array
918
- const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
919
- if (secret.envKey) {
920
- return {
921
- ...acc,
922
- [`SECRET_${secret.envKey}`]: secret.secretName,
923
- };
924
- }
925
- return acc;
926
- }, {});
927
- // Add ParamsAndSecrets layer if configured
928
- const resolvedParamsAndSecrets = resolveParamsAndSecrets({
929
- paramsAndSecrets,
930
- options: paramsAndSecretsOptions,
931
- });
932
- // Create LogGroup if not provided
933
- const resolvedLogGroup = logGroup ??
934
- new logs__namespace.LogGroup(this, "LogGroup", {
935
- retention: logRetention,
936
- removalPolicy: cdk.RemovalPolicy.DESTROY,
937
- });
938
- // Create Lambda Function
939
- this._lambda = new lambda__namespace.Function(this, "Function", {
940
- allowAllOutbound,
941
- allowPublicSubnet,
942
- architecture,
943
- code: codeAsset,
944
- deadLetterQueue,
945
- deadLetterQueueEnabled,
946
- deadLetterTopic,
947
- description,
948
- environment: {
949
- ...environment,
950
- ...secretsEnvironment,
951
- ...jaypieSecretsEnvironment,
952
- },
953
- ephemeralStorageSize,
954
- filesystem,
955
- handler,
956
- initialPolicy,
957
- layers: resolvedLayers,
958
- logGroup: resolvedLogGroup,
959
- maxEventAge,
960
- memorySize,
961
- paramsAndSecrets: resolvedParamsAndSecrets,
962
- profiling,
963
- profilingGroup,
964
- reservedConcurrentExecutions,
965
- retryAttempts,
966
- runtime,
967
- runtimeManagementMode,
968
- securityGroups,
969
- timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
970
- tracing,
971
- vpc,
972
- vpcSubnets,
973
- // Enable auto-publishing of versions when using provisioned concurrency
974
- currentVersionOptions: provisionedConcurrentExecutions !== undefined
975
- ? {
976
- removalPolicy: cdk.RemovalPolicy.RETAIN,
977
- description: "Auto-published version for provisioned concurrency",
978
- // Don't set provisioned concurrency here - it will be set on the alias
979
- }
980
- : undefined,
981
- });
982
- addDatadogLayers(this._lambda, { datadogApiKeyArn });
983
- // Grant secret read permissions
984
- Object.values(envSecrets).forEach((secret) => {
985
- secret.grantRead(this._lambda);
986
- });
987
- // Grant read permissions for JaypieEnvSecrets
988
- secrets.forEach((secret) => {
989
- secret.grantRead(this._lambda);
990
- });
991
- // Configure provisioned concurrency if specified
992
- if (provisionedConcurrentExecutions !== undefined) {
993
- // Use currentVersion which is auto-published with proper configuration
994
- const version = this._lambda.currentVersion;
995
- // Create alias for provisioned concurrency
996
- this._provisioned = new lambda__namespace.Alias(this, "ProvisionedAlias", {
997
- aliasName: "provisioned",
998
- version,
999
- provisionedConcurrentExecutions,
1000
- });
1001
- // Add explicit dependencies to ensure proper creation order
1002
- this._provisioned.node.addDependency(version);
1003
- }
1004
- if (roleTag) {
1005
- cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
1006
- }
1007
- if (vendorTag) {
1008
- cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
1009
- }
1010
- // Assign _reference based on provisioned state
1011
- this._reference =
1012
- this._provisioned !== undefined ? this._provisioned : this._lambda;
1013
- }
1014
- // Public accessors
1015
- get lambda() {
1016
- return this._lambda;
1017
- }
1018
- get provisioned() {
1019
- return this._provisioned;
1020
- }
1021
- get reference() {
1022
- return this._reference;
1023
- }
1024
- // IFunction implementation
1025
- get functionArn() {
1026
- return this._reference.functionArn;
1027
- }
1028
- get functionName() {
1029
- return this._reference.functionName;
1030
- }
1031
- get grantPrincipal() {
1032
- return this._reference.grantPrincipal;
1033
- }
1034
- get role() {
1035
- return this._reference.role;
1036
- }
1037
- get architecture() {
1038
- return this._reference.architecture;
1039
- }
1040
- get connections() {
1041
- return this._reference.connections;
1042
- }
1043
- get isBoundToVpc() {
1044
- return this._reference.isBoundToVpc;
1045
- }
1046
- get latestVersion() {
1047
- return this._reference.latestVersion;
1048
- }
1049
- get permissionsNode() {
1050
- return this._reference.permissionsNode;
1051
- }
1052
- get resourceArnsForGrantInvoke() {
1053
- return this._reference.resourceArnsForGrantInvoke;
1054
- }
1055
- get functionRef() {
1056
- return {
1057
- functionArn: this._reference.functionArn,
1058
- functionName: this._reference.functionName,
1059
- };
1060
- }
1061
- addEventSource(source) {
1062
- this._reference.addEventSource(source);
1063
- }
1064
- addEventSourceMapping(id, options) {
1065
- return this._reference.addEventSourceMapping(id, options);
1066
- }
1067
- addFunctionUrl(options) {
1068
- return this._reference.addFunctionUrl(options);
1069
- }
1070
- addPermission(id, permission) {
1071
- this._reference.addPermission(id, permission);
1072
- }
1073
- addToRolePolicy(statement) {
1074
- this._reference.addToRolePolicy(statement);
1075
- }
1076
- configureAsyncInvoke(options) {
1077
- this._reference.configureAsyncInvoke(options);
1078
- }
1079
- grantInvoke(grantee) {
1080
- return this._reference.grantInvoke(grantee);
1081
- }
1082
- grantInvokeCompositePrincipal(compositePrincipal) {
1083
- return this._reference.grantInvokeCompositePrincipal(compositePrincipal);
1084
- }
1085
- grantInvokeUrl(grantee) {
1086
- return this._reference.grantInvokeUrl(grantee);
1087
- }
1088
- grantInvokeLatestVersion(grantee) {
1089
- return this._reference.grantInvokeLatestVersion(grantee);
1090
- }
1091
- grantInvokeVersion(grantee, version) {
1092
- return this._reference.grantInvokeVersion(grantee, version);
1093
- }
1094
- metric(metricName, props) {
1095
- return this._reference.metric(metricName, props);
1096
- }
1097
- metricDuration(props) {
1098
- return this._reference.metricDuration(props);
1099
- }
1100
- metricErrors(props) {
1101
- return this._reference.metricErrors(props);
1102
- }
1103
- metricInvocations(props) {
1104
- return this._reference.metricInvocations(props);
1105
- }
1106
- metricThrottles(props) {
1107
- return this._reference.metricThrottles(props);
1108
- }
1109
- get env() {
1110
- return {
1111
- account: cdk.Stack.of(this).account,
1112
- region: cdk.Stack.of(this).region,
1113
- };
1114
- }
1115
- get stack() {
1116
- return this._reference.stack;
1117
- }
1118
- applyRemovalPolicy(policy) {
1119
- this._reference.applyRemovalPolicy(policy);
1120
- }
1121
- addEnvironment(key, value) {
1122
- this._lambda.addEnvironment(key, value);
1123
- }
1124
- }
1125
-
1126
- class JaypieQueuedLambda extends constructs.Construct {
1127
- constructor(scope, id, props) {
1128
- super(scope, id);
1129
- 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 = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1130
- supportsInlineCode: true,
1131
- }), 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;
1132
- // Create SQS Queue
1133
- this._queue = new sqs__namespace.Queue(this, "Queue", {
1134
- fifo,
1135
- visibilityTimeout: typeof visibilityTimeout === "number"
1136
- ? cdk.Duration.seconds(visibilityTimeout)
1137
- : visibilityTimeout,
1138
- });
1139
- if (roleTag) {
1140
- cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
1141
- }
1142
- if (vendorTag) {
1143
- cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
1144
- }
1145
- // Create Lambda with JaypieLambda
1146
- this._lambdaConstruct = new JaypieLambda(this, "Function", {
1147
- allowAllOutbound,
1148
- allowPublicSubnet,
1149
- architecture,
1150
- code,
1151
- datadogApiKeyArn,
1152
- deadLetterQueue,
1153
- deadLetterQueueEnabled,
1154
- deadLetterTopic,
1155
- description,
1156
- environment: {
1157
- ...environment,
1158
- CDK_ENV_QUEUE_URL: this._queue.queueUrl,
1159
- },
1160
- envSecrets,
1161
- ephemeralStorageSize,
1162
- filesystem,
1163
- handler,
1164
- initialPolicy,
1165
- layers,
1166
- logGroup,
1167
- logRetention,
1168
- maxEventAge,
1169
- memorySize,
1170
- paramsAndSecrets,
1171
- paramsAndSecretsOptions,
1172
- profiling,
1173
- profilingGroup,
1174
- provisionedConcurrentExecutions,
1175
- reservedConcurrentExecutions,
1176
- retryAttempts,
1177
- roleTag,
1178
- runtime,
1179
- runtimeManagementMode,
1180
- secrets,
1181
- securityGroups,
1182
- timeout,
1183
- tracing,
1184
- vendorTag,
1185
- vpc,
1186
- vpcSubnets,
1187
- });
1188
- // Set up queue and lambda integration
1189
- this._queue.grantConsumeMessages(this._lambdaConstruct);
1190
- this._queue.grantSendMessages(this._lambdaConstruct);
1191
- this._lambdaConstruct.addEventSource(new lambdaEventSources__namespace.SqsEventSource(this._queue, {
1192
- batchSize,
1193
- }));
1194
- }
1195
- // Public accessors
1196
- get queue() {
1197
- return this._queue;
1198
- }
1199
- get lambda() {
1200
- return this._lambdaConstruct.lambda;
1201
- }
1202
- // IFunction implementation
1203
- get functionArn() {
1204
- return this._lambdaConstruct.functionArn;
1205
- }
1206
- get functionName() {
1207
- return this._lambdaConstruct.functionName;
1208
- }
1209
- get grantPrincipal() {
1210
- return this._lambdaConstruct.grantPrincipal;
1211
- }
1212
- get role() {
1213
- return this._lambdaConstruct.role;
1214
- }
1215
- get architecture() {
1216
- return this._lambdaConstruct.architecture;
1217
- }
1218
- get connections() {
1219
- return this._lambdaConstruct.connections;
1220
- }
1221
- get isBoundToVpc() {
1222
- return this._lambdaConstruct.isBoundToVpc;
1223
- }
1224
- get latestVersion() {
1225
- return this._lambdaConstruct.latestVersion;
1226
- }
1227
- get permissionsNode() {
1228
- return this._lambdaConstruct.permissionsNode;
1229
- }
1230
- get resourceArnsForGrantInvoke() {
1231
- return this._lambdaConstruct.resourceArnsForGrantInvoke;
1232
- }
1233
- get functionRef() {
1234
- return this._lambdaConstruct.functionRef;
1235
- }
1236
- addEventSource(source) {
1237
- this._lambdaConstruct.addEventSource(source);
1238
- }
1239
- addEventSourceMapping(id, options) {
1240
- return this._lambdaConstruct.addEventSourceMapping(id, options);
1241
- }
1242
- addFunctionUrl(options) {
1243
- return this._lambdaConstruct.addFunctionUrl(options);
1244
- }
1245
- addPermission(id, permission) {
1246
- this._lambdaConstruct.addPermission(id, permission);
1247
- }
1248
- addToRolePolicy(statement) {
1249
- this._lambdaConstruct.addToRolePolicy(statement);
1250
- }
1251
- configureAsyncInvoke(options) {
1252
- this._lambdaConstruct.configureAsyncInvoke(options);
1253
- }
1254
- grantInvoke(grantee) {
1255
- return this._lambdaConstruct.grantInvoke(grantee);
1256
- }
1257
- grantInvokeCompositePrincipal(compositePrincipal) {
1258
- return this._lambdaConstruct.grantInvokeCompositePrincipal(compositePrincipal);
1259
- }
1260
- grantInvokeUrl(grantee) {
1261
- return this._lambdaConstruct.grantInvokeUrl(grantee);
1262
- }
1263
- metric(metricName, props) {
1264
- return this._lambdaConstruct.metric(metricName, props);
1265
- }
1266
- metricDuration(props) {
1267
- return this._lambdaConstruct.metricDuration(props);
1268
- }
1269
- metricErrors(props) {
1270
- return this._lambdaConstruct.metricErrors(props);
1271
- }
1272
- metricInvocations(props) {
1273
- return this._lambdaConstruct.metricInvocations(props);
1274
- }
1275
- metricThrottles(props) {
1276
- return this._lambdaConstruct.metricThrottles(props);
1277
- }
1278
- // Additional IFunction implementation
1279
- grantInvokeLatestVersion(grantee) {
1280
- return this._lambdaConstruct.grantInvokeLatestVersion(grantee);
1281
- }
1282
- grantInvokeVersion(grantee, version) {
1283
- return this._lambdaConstruct.grantInvokeVersion(grantee, version);
1284
- }
1285
- get env() {
1286
- return {
1287
- account: cdk.Stack.of(this).account,
1288
- region: cdk.Stack.of(this).region,
1289
- };
1290
- }
1291
- get stack() {
1292
- return cdk.Stack.of(this);
1293
- }
1294
- applyRemovalPolicy(policy) {
1295
- this._lambdaConstruct.applyRemovalPolicy(policy);
1296
- this._queue.applyRemovalPolicy(policy);
1297
- }
1298
- // IQueue implementation
1299
- get queueRef() {
1300
- return {
1301
- queueUrl: this._queue.queueUrl,
1302
- queueArn: this._queue.queueArn,
1303
- };
1304
- }
1305
- get fifo() {
1306
- return this._queue.fifo;
1307
- }
1308
- get queueArn() {
1309
- return this._queue.queueArn;
1310
- }
1311
- get queueName() {
1312
- return this._queue.queueName;
1313
- }
1314
- get queueUrl() {
1315
- return this._queue.queueUrl;
1316
- }
1317
- get encryptionMasterKey() {
1318
- return this._queue.encryptionMasterKey;
1319
- }
1320
- addToResourcePolicy(statement) {
1321
- return this._queue.addToResourcePolicy(statement);
1322
- }
1323
- grant(grantee, ...actions) {
1324
- return this._queue.grant(grantee, ...actions);
1325
- }
1326
- grantConsumeMessages(grantee) {
1327
- return this._queue.grantConsumeMessages(grantee);
1328
- }
1329
- grantPurge(grantee) {
1330
- return this._queue.grantPurge(grantee);
1331
- }
1332
- grantSendMessages(grantee) {
1333
- return this._queue.grantSendMessages(grantee);
1334
- }
1335
- // Queue metrics
1336
- metricApproximateAgeOfOldestMessage(props) {
1337
- return this._queue.metricApproximateAgeOfOldestMessage(props);
1338
- }
1339
- metricApproximateNumberOfMessagesDelayed(props) {
1340
- return this._queue.metricApproximateNumberOfMessagesDelayed(props);
1341
- }
1342
- metricApproximateNumberOfMessagesNotVisible(props) {
1343
- return this._queue.metricApproximateNumberOfMessagesNotVisible(props);
1344
- }
1345
- metricApproximateNumberOfMessagesVisible(props) {
1346
- return this._queue.metricApproximateNumberOfMessagesVisible(props);
1347
- }
1348
- metricNumberOfEmptyReceives(props) {
1349
- return this._queue.metricNumberOfEmptyReceives(props);
1350
- }
1351
- metricNumberOfMessagesDeleted(props) {
1352
- return this._queue.metricNumberOfMessagesDeleted(props);
1353
- }
1354
- metricNumberOfMessagesReceived(props) {
1355
- return this._queue.metricNumberOfMessagesReceived(props);
1356
- }
1357
- metricNumberOfMessagesSent(props) {
1358
- return this._queue.metricNumberOfMessagesSent(props);
1359
- }
1360
- metricSentMessageSize(props) {
1361
- return this._queue.metricSentMessageSize(props);
1362
- }
1363
- addEnvironment(key, value) {
1364
- this._lambdaConstruct.addEnvironment(key, value);
1365
- }
1366
- }
1367
-
1368
- class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1369
- constructor(scope, id, props) {
1370
- props.fifo = false; // S3 event notifications are not supported for FIFO queues
1371
- super(scope, id, props);
1372
- const { bucketName, roleTag, vendorTag, bucketOptions = {} } = props;
1373
- // Create S3 Bucket
1374
- this._bucket = new s3__namespace.Bucket(this, "Bucket", {
1375
- bucketName: bucketOptions.bucketName || bucketName,
1376
- removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
1377
- ...bucketOptions,
1378
- });
1379
- // Add tags to bucket
1380
- if (roleTag) {
1381
- cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1382
- }
1383
- if (vendorTag) {
1384
- cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1385
- }
1386
- // Add an event notification from the bucket to the queue
1387
- this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
1388
- // Grant the lambda access to the bucket
1389
- this._bucket.grantReadWrite(this);
1390
- // Add environment variable for bucket name
1391
- this.lambda.addEnvironment("CDK_ENV_BUCKET_NAME", this._bucket.bucketName);
1392
- }
1393
- // Public accessors
1394
- get bucket() {
1395
- return this._bucket;
1396
- }
1397
- // IBucket implementation
1398
- get bucketArn() {
1399
- return this._bucket.bucketArn;
1400
- }
1401
- get bucketDomainName() {
1402
- return this._bucket.bucketDomainName;
1403
- }
1404
- get bucketDualStackDomainName() {
1405
- return this._bucket.bucketDualStackDomainName;
1406
- }
1407
- get bucketName() {
1408
- return this._bucket.bucketName;
1409
- }
1410
- get bucketRegionalDomainName() {
1411
- return this._bucket.bucketRegionalDomainName;
1412
- }
1413
- get bucketWebsiteDomainName() {
1414
- return this._bucket.bucketWebsiteDomainName;
1415
- }
1416
- get bucketWebsiteUrl() {
1417
- return this._bucket.bucketWebsiteUrl;
1418
- }
1419
- get encryptionKey() {
1420
- return this._bucket.encryptionKey;
1421
- }
1422
- get isWebsite() {
1423
- return this._bucket.isWebsite || false;
1424
- }
1425
- get policy() {
1426
- return this._bucket.policy;
1427
- }
1428
- addEventNotification(event, dest, ...filters) {
1429
- this._bucket.addEventNotification(event, dest, ...filters);
1430
- }
1431
- addObjectCreatedNotification(dest, ...filters) {
1432
- this._bucket.addObjectCreatedNotification(dest, ...filters);
1433
- }
1434
- addObjectRemovedNotification(dest, ...filters) {
1435
- this._bucket.addObjectRemovedNotification(dest, ...filters);
1436
- }
1437
- addToResourcePolicy(permission) {
1438
- return this._bucket.addToResourcePolicy(permission);
1439
- }
1440
- arnForObjects(objectKeyPattern) {
1441
- return this._bucket.arnForObjects(objectKeyPattern);
1442
- }
1443
- enableEventBridgeNotification() {
1444
- this._bucket.enableEventBridgeNotification();
1445
- }
1446
- grantDelete(grantee, objectsKeyPattern) {
1447
- return this._bucket.grantDelete(grantee, objectsKeyPattern);
1448
- }
1449
- grantPublicAccess(keyPrefix, ...allowedActions) {
1450
- return this._bucket.grantPublicAccess(keyPrefix, ...allowedActions);
1451
- }
1452
- grantPut(grantee, objectsKeyPattern) {
1453
- return this._bucket.grantPut(grantee, objectsKeyPattern);
1454
- }
1455
- grantPutAcl(grantee, objectsKeyPattern) {
1456
- return this._bucket.grantPutAcl(grantee, objectsKeyPattern);
1457
- }
1458
- grantRead(grantee, objectsKeyPattern) {
1459
- return this._bucket.grantRead(grantee, objectsKeyPattern);
1460
- }
1461
- grantReadWrite(grantee, objectsKeyPattern) {
1462
- return this._bucket.grantReadWrite(grantee, objectsKeyPattern);
1463
- }
1464
- grantWrite(grantee, objectsKeyPattern) {
1465
- return this._bucket.grantWrite(grantee, objectsKeyPattern);
1466
- }
1467
- onCloudTrailEvent(id, options) {
1468
- return this._bucket.onCloudTrailEvent(id, options);
1469
- }
1470
- onCloudTrailPutObject(id, options) {
1471
- return this._bucket.onCloudTrailPutObject(id, options);
1472
- }
1473
- onCloudTrailWriteObject(id, options) {
1474
- return this._bucket.onCloudTrailWriteObject(id, options);
1475
- }
1476
- s3UrlForObject(key) {
1477
- return this._bucket.s3UrlForObject(key);
1478
- }
1479
- transferAccelerationUrlForObject(key, options) {
1480
- return this._bucket.transferAccelerationUrlForObject(key, options);
1481
- }
1482
- urlForObject(key) {
1483
- return this._bucket.urlForObject(key);
1484
- }
1485
- virtualHostedUrlForObject(key, options) {
1486
- return this._bucket.virtualHostedUrlForObject(key, options);
1487
- }
1488
- grantReplicationPermission(identity, props) {
1489
- return this._bucket.grantReplicationPermission(identity, props);
1490
- }
1491
- addReplicationPolicy(policy) {
1492
- this._bucket.addReplicationPolicy(policy);
1493
- }
1494
- get bucketRef() {
1495
- return {
1496
- bucketArn: this._bucket.bucketArn,
1497
- bucketName: this._bucket.bucketName,
1498
- };
1499
- }
1500
- // Override applyRemovalPolicy to apply to all resources
1501
- applyRemovalPolicy(policy) {
1502
- super.applyRemovalPolicy(policy);
1503
- this._bucket.applyRemovalPolicy(policy);
1504
- }
1505
- }
1506
-
1507
- class JaypieDatadogBucket extends constructs.Construct {
1508
- /**
1509
- * Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
1510
- */
1511
- constructor(scope, idOrProps, propsOrUndefined) {
1512
- // Handle overloaded constructor signatures
1513
- let props;
1514
- let id;
1515
- if (typeof idOrProps === "string") {
1516
- // First param is ID, second is props
1517
- props = propsOrUndefined || {};
1518
- id = idOrProps;
1519
- }
1520
- else {
1521
- // First param is props
1522
- props = idOrProps || {};
1523
- id = props.id || "JaypieDatadogBucket";
1524
- }
1525
- super(scope, id);
1526
- // Extract Jaypie-specific options
1527
- const { bucketId = "DatadogArchiveBucket", bucketScope, grantDatadogAccess = true, project, service = CDK$2.SERVICE.DATADOG, ...bucketProps } = props;
1528
- // Create the bucket using bucketScope (defaults to this) and bucketId
1529
- const effectiveBucketScope = bucketScope || this;
1530
- this.bucket = new s3.Bucket(effectiveBucketScope, bucketId, bucketProps);
1531
- // Add tags to bucket
1532
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
1533
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1534
- if (project) {
1535
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
1536
- }
1537
- // Grant Datadog role access to bucket if enabled
1538
- if (grantDatadogAccess) {
1539
- this.policy = this.grantDatadogRoleBucketAccess({ project, service });
1540
- }
1541
- }
1542
- /**
1543
- * Grants the Datadog IAM role access to this bucket
1544
- *
1545
- * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
1546
- * If found, creates a custom policy with:
1547
- * - s3:ListBucket on bucket
1548
- * - s3:GetObject and s3:PutObject on bucket/*
1549
- *
1550
- * @param options - Configuration options
1551
- * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
1552
- */
1553
- grantDatadogRoleBucketAccess(options) {
1554
- const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
1555
- // Early return if no Datadog role ARN is configured
1556
- if (!datadogRoleArn) {
1557
- return undefined;
1558
- }
1559
- const { project, service = CDK$2.SERVICE.DATADOG } = options || {};
1560
- // Lookup the Datadog role
1561
- const datadogRole = awsIam.Role.fromRoleArn(this, "DatadogRole", datadogRoleArn);
1562
- // Build policy statements for bucket access
1563
- const statements = [
1564
- // Allow list bucket
1565
- new awsIam.PolicyStatement({
1566
- actions: ["s3:ListBucket"],
1567
- resources: [this.bucket.bucketArn],
1568
- }),
1569
- // Allow read and write to the bucket
1570
- new awsIam.PolicyStatement({
1571
- actions: ["s3:GetObject", "s3:PutObject"],
1572
- resources: [`${this.bucket.bucketArn}/*`],
1573
- }),
1574
- ];
1575
- // Create the custom policy
1576
- const datadogBucketPolicy = new awsIam.Policy(this, "DatadogBucketPolicy", {
1577
- roles: [datadogRole],
1578
- statements,
1579
- });
1580
- // Add tags
1581
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.SERVICE, service);
1582
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1583
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1584
- if (project) {
1585
- cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.PROJECT, project);
1586
- }
1587
- return datadogBucketPolicy;
1588
- }
1589
- }
1590
-
1591
- const DATADOG_FORWARDER_TEMPLATE_URL = "https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml";
1592
- const DEFAULT_RESERVED_CONCURRENCY = "10";
1593
- class JaypieDatadogForwarder extends constructs.Construct {
1594
- /**
1595
- * Create a new Datadog forwarder with CloudFormation nested stack
1596
- */
1597
- constructor(scope, idOrProps, propsOrUndefined) {
1598
- // Handle overloaded constructor signatures
1599
- let props;
1600
- let id;
1601
- if (typeof idOrProps === "string") {
1602
- // First param is ID, second is props
1603
- props = propsOrUndefined || {};
1604
- id = idOrProps;
1605
- }
1606
- else {
1607
- // First param is props
1608
- props = idOrProps || {};
1609
- id = props.id || "DatadogForwarder";
1610
- }
1611
- super(scope, id);
1612
- // Resolve options with defaults
1613
- 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;
1614
- // Validate required parameters
1615
- if (!datadogApiKey) {
1616
- throw new Error("Datadog API key is required. Provide via datadogApiKey prop or CDK_ENV_DATADOG_API_KEY environment variable.");
1617
- }
1618
- // Build Datadog tags
1619
- let ddTags = account ? `account:${account}` : "";
1620
- if (additionalTags) {
1621
- ddTags = ddTags ? `${ddTags},${additionalTags}` : additionalTags;
1622
- }
1623
- // Deploy Datadog CloudFormation stack
1624
- this.cfnStack = new cdk.CfnStack(this, "Stack", {
1625
- parameters: {
1626
- DdApiKey: datadogApiKey,
1627
- DdTags: ddTags,
1628
- ReservedConcurrency: reservedConcurrency,
1629
- },
1630
- templateUrl,
1631
- });
1632
- // Add tags to stack
1633
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1634
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.SERVICE, service);
1635
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1636
- if (project) {
1637
- cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.PROJECT, project);
1638
- }
1639
- // Extract forwarder function from stack outputs
1640
- this.forwarderFunction = lambda__namespace.Function.fromFunctionArn(this, "Function", this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString());
1641
- // Extend Datadog role with custom permissions if enabled
1642
- if (enableRoleExtension) {
1643
- extendDatadogRole(this, { project, service });
1644
- }
1645
- // Create CloudFormation events rule if enabled
1646
- if (enableCloudFormationEvents) {
1647
- this.eventsRule = new awsEvents.Rule(this, "CloudFormationEventsRule", {
1648
- eventPattern: {
1649
- source: ["aws.cloudformation"],
1650
- },
1651
- targets: [
1652
- new awsEventsTargets.LambdaFunction(this.forwarderFunction, {
1653
- event: awsEvents.RuleTargetInput.fromEventPath("$"),
1654
- }),
1655
- ],
1656
- });
1657
- // Add tags to events rule
1658
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1659
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.SERVICE, service);
1660
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1661
- if (project) {
1662
- cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.PROJECT, project);
1663
- }
1664
- }
1665
- // Create CloudFormation output if enabled
1666
- if (createOutput) {
1667
- new cdk__namespace.CfnOutput(this, "ForwarderArnOutput", {
1668
- description: "Datadog Log Forwarder Lambda ARN",
1669
- exportName,
1670
- value: this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString(),
1671
- });
1672
- }
1673
- }
1674
- }
1675
-
1676
- class JaypieDistribution extends constructs.Construct {
1677
- constructor(scope, id, props) {
1678
- super(scope, id);
1679
- 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;
1680
- // Validate environment variables
1681
- if (process.env.CDK_ENV_API_SUBDOMAIN &&
1682
- !isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
1683
- throw new Error("CDK_ENV_API_SUBDOMAIN is not a valid subdomain");
1684
- }
1685
- if (process.env.CDK_ENV_API_HOSTED_ZONE &&
1686
- !isValidHostname$1(process.env.CDK_ENV_API_HOSTED_ZONE)) {
1687
- throw new Error("CDK_ENV_API_HOSTED_ZONE is not a valid hostname");
1688
- }
1689
- if (process.env.CDK_ENV_HOSTED_ZONE &&
1690
- !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
1691
- throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
1692
- }
1693
- // Determine host from props or environment
1694
- let host = propsHost;
1695
- if (!host) {
1696
- try {
1697
- if (process.env.CDK_ENV_API_HOST_NAME) {
1698
- host = process.env.CDK_ENV_API_HOST_NAME;
1699
- }
1700
- else if (process.env.CDK_ENV_API_SUBDOMAIN) {
1701
- host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
1702
- process.env.CDK_ENV_HOSTED_ZONE ||
1703
- "");
1704
- }
1705
- }
1706
- catch {
1707
- host = undefined;
1708
- }
1709
- }
1710
- if (host && !isValidHostname$1(host)) {
1711
- throw new Error("Host is not a valid hostname");
1712
- }
1713
- this.host = host;
1714
- // Determine zone from props or environment
1715
- const zone = propsZone || process.env.CDK_ENV_HOSTED_ZONE;
1716
- // Resolve the origin from handler
1717
- // Check order matters: IFunctionUrl before IOrigin (FunctionUrl also has bind method)
1718
- // IFunction before IFunctionUrl (IFunction doesn't have functionUrlId)
1719
- let origin;
1720
- if (handler) {
1721
- if (this.isIFunction(handler)) {
1722
- // Create FunctionUrl for the Lambda function
1723
- const functionUrl = new lambda__namespace.FunctionUrl(this, "FunctionUrl", {
1724
- function: handler,
1725
- authType: lambda__namespace.FunctionUrlAuthType.NONE,
1726
- invokeMode,
1727
- });
1728
- this.functionUrl = functionUrl;
1729
- origin = new origins__namespace.FunctionUrlOrigin(functionUrl);
1730
- }
1731
- else if (this.isIFunctionUrl(handler)) {
1732
- origin = new origins__namespace.FunctionUrlOrigin(handler);
1733
- }
1734
- else if (this.isIOrigin(handler)) {
1735
- origin = handler;
1736
- }
1737
- }
1738
- // Build default behavior
1739
- let defaultBehavior;
1740
- if (propsDefaultBehavior) {
1741
- defaultBehavior = propsDefaultBehavior;
1742
- }
1743
- else if (origin) {
1744
- defaultBehavior = {
1745
- allowedMethods: cloudfront__namespace.AllowedMethods.ALLOW_ALL,
1746
- cachePolicy: cloudfront__namespace.CachePolicy.CACHING_DISABLED,
1747
- origin,
1748
- originRequestPolicy: cloudfront__namespace.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
1749
- viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
1750
- };
1751
- }
1752
- else {
1753
- throw new Error("Either handler or defaultBehavior must be provided to JaypieDistribution");
1754
- }
1755
- // Resolve hosted zone and certificate
1756
- // Only resolve zone when we need it (for certificate or DNS)
1757
- let hostedZone;
1758
- let certificateToUse;
1759
- if (host && zone && certificateProp !== false) {
1760
- hostedZone = resolveHostedZone(this, { zone });
1761
- if (certificateProp === true) {
1762
- certificateToUse = new acm__namespace.Certificate(this, constructEnvName("Certificate"), {
1763
- domainName: host,
1764
- validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
1765
- });
1766
- cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, roleTag);
1767
- }
1768
- else if (typeof certificateProp === "object") {
1769
- certificateToUse = certificateProp;
1770
- }
1771
- this.certificate = certificateToUse;
1772
- }
1773
- // Create log bucket if logging is enabled
1774
- let logBucket;
1775
- if (destinationProp !== false) {
1776
- logBucket = new s3__namespace.Bucket(this, constructEnvName("LogBucket"), {
1777
- objectOwnership: s3__namespace.ObjectOwnership.OBJECT_WRITER,
1778
- removalPolicy: cdk.RemovalPolicy.DESTROY,
1779
- autoDeleteObjects: true,
1780
- lifecycleRules: [
1781
- {
1782
- expiration: cdk.Duration.days(90),
1783
- transitions: [
1784
- {
1785
- storageClass: s3__namespace.StorageClass.INFREQUENT_ACCESS,
1786
- transitionAfter: cdk.Duration.days(30),
1787
- },
1788
- ],
1789
- },
1790
- ],
1791
- });
1792
- cdk.Tags.of(logBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
1793
- // Add S3 notification to Datadog forwarder
1794
- const lambdaDestination = destinationProp === true
1795
- ? new s3n.LambdaDestination(resolveDatadogForwarderFunction(this))
1796
- : destinationProp;
1797
- logBucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, lambdaDestination);
1798
- this.logBucket = logBucket;
1799
- }
1800
- // Create the CloudFront distribution
1801
- this.distribution = new cloudfront__namespace.Distribution(this, constructEnvName("Distribution"), {
1802
- defaultBehavior,
1803
- ...(host && certificateToUse
1804
- ? {
1805
- certificate: certificateToUse,
1806
- domainNames: [host],
1807
- }
1808
- : {}),
1809
- ...(logBucket
1810
- ? {
1811
- enableLogging: true,
1812
- logBucket,
1813
- logFilePrefix: "cloudfront-logs/",
1814
- }
1815
- : {}),
1816
- ...distributionProps,
1817
- });
1818
- cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
1819
- this.distributionArn = `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${this.distribution.distributionId}`;
1820
- this.distributionDomainName = this.distribution.distributionDomainName;
1821
- this.distributionId = this.distribution.distributionId;
1822
- this.domainName = this.distribution.domainName;
1823
- // Create DNS records if we have host and zone
1824
- if (host && hostedZone) {
1825
- const aRecord = new route53__namespace.ARecord(this, "AliasRecord", {
1826
- recordName: host,
1827
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
1828
- zone: hostedZone,
1829
- });
1830
- cdk.Tags.of(aRecord).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1831
- const aaaaRecord = new route53__namespace.AaaaRecord(this, "AaaaAliasRecord", {
1832
- recordName: host,
1833
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
1834
- zone: hostedZone,
1835
- });
1836
- cdk.Tags.of(aaaaRecord).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1837
- }
1838
- }
1839
- // Type guards for handler types
1840
- isIOrigin(handler) {
1841
- return (typeof handler === "object" &&
1842
- handler !== null &&
1843
- "bind" in handler &&
1844
- typeof handler.bind === "function");
1845
- }
1846
- isIFunctionUrl(handler) {
1847
- // FunctionUrl has 'url' property which is the function URL string
1848
- // IFunction does not have 'url' property
1849
- return (typeof handler === "object" &&
1850
- handler !== null &&
1851
- "url" in handler &&
1852
- "functionArn" in handler);
1853
- }
1854
- isIFunction(handler) {
1855
- // IFunction has functionArn and functionName but NOT 'url'
1856
- // (FunctionUrl also has functionArn but also has 'url')
1857
- return (typeof handler === "object" &&
1858
- handler !== null &&
1859
- "functionArn" in handler &&
1860
- "functionName" in handler &&
1861
- !("url" in handler));
1862
- }
1863
- // Implement IDistribution interface
1864
- get env() {
1865
- return {
1866
- account: cdk.Stack.of(this).account,
1867
- region: cdk.Stack.of(this).region,
1868
- };
1869
- }
1870
- get stack() {
1871
- return this.distribution.stack;
1872
- }
1873
- applyRemovalPolicy(policy) {
1874
- this.distribution.applyRemovalPolicy(policy);
1875
- }
1876
- grant(identity, ...actions) {
1877
- return this.distribution.grant(identity, ...actions);
1878
- }
1879
- grantCreateInvalidation(identity) {
1880
- return this.distribution.grantCreateInvalidation(identity);
1881
- }
1882
- get distributionRef() {
1883
- return {
1884
- distributionId: this.distribution.distributionId,
1885
- };
1886
- }
1887
- }
1888
-
1889
- // It is a consumer if the environment is ephemeral
1890
- function checkEnvIsConsumer(env = process.env) {
1891
- return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
1892
- !!env.CDK_ENV_PERSONAL ||
1893
- /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
1894
- /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
1895
- }
1896
- function checkEnvIsProvider(env = process.env) {
1897
- return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
1898
- }
1899
- function cleanName(name) {
1900
- return name.replace(/[^a-zA-Z0-9:-]/g, "");
1901
- }
1902
- function exportEnvName(name, env = process.env) {
1903
- let rawName;
1904
- if (checkEnvIsProvider(env)) {
1905
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1906
- // Clean the entire name to only allow alphanumeric, colons, and hyphens
1907
- return cleanName(rawName);
1908
- }
1909
- else {
1910
- if (checkEnvIsConsumer(env)) {
1911
- rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1912
- }
1913
- else {
1914
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1915
- }
1916
- }
1917
- return cleanName(rawName);
1918
- }
1919
- class JaypieEnvSecret extends constructs.Construct {
1920
- constructor(scope, idOrEnvKey, props) {
1921
- // Check if idOrEnvKey should be treated as envKey:
1922
- // - No props provided OR props.envKey is not set
1923
- // - AND idOrEnvKey exists as a non-empty string in process.env
1924
- const treatAsEnvKey = (!props || props.envKey === undefined) &&
1925
- typeof process.env[idOrEnvKey] === "string" &&
1926
- process.env[idOrEnvKey] !== "";
1927
- const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
1928
- super(scope, id);
1929
- const { consumer = checkEnvIsConsumer(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider(), roleTag, vendorTag, value, } = props || {};
1930
- const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
1931
- this._envKey = envKey;
1932
- let exportName;
1933
- if (!exportParam) {
1934
- exportName = exportEnvName(id);
1935
- }
1936
- else {
1937
- exportName = cleanName(exportParam);
1938
- }
1939
- if (consumer) {
1940
- const secretName = cdk.Fn.importValue(exportName);
1941
- this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1942
- // Add CfnOutput for consumer secrets
1943
- new cdk.CfnOutput(this, `ConsumedName`, {
1944
- value: this._secret.secretName,
1945
- });
1946
- }
1947
- else {
1948
- const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1949
- const secretProps = {
1950
- generateSecretString,
1951
- secretStringValue: !generateSecretString && secretValue
1952
- ? cdk.SecretValue.unsafePlainText(secretValue)
1953
- : undefined,
1954
- };
1955
- this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
1956
- if (roleTag) {
1957
- cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
1958
- }
1959
- if (vendorTag) {
1960
- cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
1961
- }
1962
- if (provider) {
1963
- new cdk.CfnOutput(this, `ProvidedName`, {
1964
- value: this._secret.secretName,
1965
- exportName,
1966
- });
1967
- }
1968
- else {
1969
- new cdk.CfnOutput(this, `CreatedName`, {
1970
- value: this._secret.secretName,
1971
- });
1972
- }
1973
- }
1974
- }
1975
- // IResource implementation
1976
- get stack() {
1977
- return cdk.Stack.of(this);
1978
- }
1979
- get env() {
1980
- return {
1981
- account: cdk.Stack.of(this).account,
1982
- region: cdk.Stack.of(this).region,
1983
- };
1984
- }
1985
- applyRemovalPolicy(policy) {
1986
- this._secret.applyRemovalPolicy(policy);
1987
- }
1988
- // ISecret implementation
1989
- get secretArn() {
1990
- return this._secret.secretArn;
1991
- }
1992
- get secretName() {
1993
- return this._secret.secretName;
1994
- }
1995
- get secretFullArn() {
1996
- return this._secret.secretFullArn;
1997
- }
1998
- get encryptionKey() {
1999
- return this._secret.encryptionKey;
2000
- }
2001
- get secretValue() {
2002
- return this._secret.secretValue;
2003
- }
2004
- secretValueFromJson(key) {
2005
- return this._secret.secretValueFromJson(key);
2006
- }
2007
- grantRead(grantee, versionStages) {
2008
- return this._secret.grantRead(grantee, versionStages);
2009
- }
2010
- grantWrite(grantee) {
2011
- return this._secret.grantWrite(grantee);
2012
- }
2013
- addRotationSchedule(id, options) {
2014
- return this._secret.addRotationSchedule(id, options);
2015
- }
2016
- addToResourcePolicy(statement) {
2017
- return this._secret.addToResourcePolicy(statement);
2018
- }
2019
- denyAccountRootDelete() {
2020
- this._secret.denyAccountRootDelete();
2021
- }
2022
- attach(target) {
2023
- return this._secret.attach(target);
2024
- }
2025
- cfnDynamicReferenceKey(options) {
2026
- return this._secret.cfnDynamicReferenceKey(options);
2027
- }
2028
- get envKey() {
2029
- return this._envKey;
2030
- }
2031
- }
2032
-
2033
- class JaypieDatadogSecret extends JaypieEnvSecret {
2034
- constructor(scope, id = "MongoConnectionString", props) {
2035
- const defaultProps = {
2036
- envKey: "DATADOG_API_KEY",
2037
- roleTag: CDK$2.ROLE.MONITORING,
2038
- vendorTag: CDK$2.VENDOR.DATADOG,
2039
- ...props,
2040
- };
2041
- super(scope, id, defaultProps);
2042
- }
2043
- }
2044
-
2045
- class JaypieDnsRecord extends constructs.Construct {
2046
- constructor(scope, id, props) {
2047
- super(scope, id);
2048
- const { comment, recordName, type, values } = props;
2049
- const ttl = props.ttl || cdk__namespace.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
2050
- // Resolve the hosted zone (supports both string and IHostedZone)
2051
- const zone = resolveHostedZone(scope, {
2052
- name: `${id}HostedZone`,
2053
- zone: props.zone,
2054
- });
2055
- // Common properties for all record types
2056
- const baseProps = {
2057
- comment,
2058
- recordName,
2059
- ttl,
2060
- zone,
2061
- };
2062
- // Create the appropriate record based on type
2063
- switch (type) {
2064
- case CDK$2.DNS.RECORD.A: {
2065
- if (!Array.isArray(values) || values.length === 0) {
2066
- throw new errors.ConfigurationError("A record requires at least one IP address");
2067
- }
2068
- this.record = new route53.ARecord(this, "Record", {
2069
- ...baseProps,
2070
- target: route53.RecordTarget.fromIpAddresses(...values),
2071
- });
2072
- break;
2073
- }
2074
- case CDK$2.DNS.RECORD.CNAME: {
2075
- if (!Array.isArray(values) || values.length === 0) {
2076
- throw new errors.ConfigurationError("CNAME record requires a domain name");
2077
- }
2078
- this.record = new route53.CnameRecord(this, "Record", {
2079
- ...baseProps,
2080
- domainName: values[0],
2081
- });
2082
- break;
2083
- }
2084
- case CDK$2.DNS.RECORD.MX: {
2085
- if (!Array.isArray(values) || values.length === 0) {
2086
- throw new errors.ConfigurationError("MX record requires at least one mail server");
2087
- }
2088
- this.record = new route53.MxRecord(this, "Record", {
2089
- ...baseProps,
2090
- values: values,
2091
- });
2092
- break;
2093
- }
2094
- case CDK$2.DNS.RECORD.NS: {
2095
- if (!Array.isArray(values) || values.length === 0) {
2096
- throw new errors.ConfigurationError("NS record requires at least one name server");
2097
- }
2098
- this.record = new route53.NsRecord(this, "Record", {
2099
- ...baseProps,
2100
- values: values,
2101
- });
2102
- break;
2103
- }
2104
- case CDK$2.DNS.RECORD.TXT: {
2105
- if (!Array.isArray(values) || values.length === 0) {
2106
- throw new errors.ConfigurationError("TXT record requires at least one value");
2107
- }
2108
- this.record = new route53.TxtRecord(this, "Record", {
2109
- ...baseProps,
2110
- values: values,
2111
- });
2112
- break;
2113
- }
2114
- default:
2115
- throw new errors.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
2116
- }
2117
- // Add standard tags to the DNS record
2118
- cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.INFRASTRUCTURE);
2119
- cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2120
- }
2121
- }
2122
-
2123
- class JaypieEventsRule extends constructs.Construct {
2124
- /**
2125
- * Create a new EventBridge rule that targets a Lambda function
2126
- */
2127
- constructor(scope, idOrSourceOrProps, propsOrUndefined) {
2128
- // Handle overloaded constructor signatures
2129
- let props;
2130
- let id;
2131
- if (typeof idOrSourceOrProps === "string") {
2132
- // Check if it looks like an AWS source (starts with "aws.")
2133
- if (idOrSourceOrProps.startsWith("aws.")) {
2134
- // First param is source, second is props
2135
- props = propsOrUndefined || {};
2136
- props.source = idOrSourceOrProps;
2137
- // Generate ID from source
2138
- const sourceName = idOrSourceOrProps
2139
- .replace("aws.", "")
2140
- .split(".")
2141
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2142
- .join("");
2143
- id = props.id || `${sourceName}EventsRule`;
2144
- }
2145
- else {
2146
- // First param is ID, second is props
2147
- props = propsOrUndefined || {};
2148
- id = idOrSourceOrProps;
2149
- }
2150
- }
2151
- else {
2152
- // First param is props
2153
- props = idOrSourceOrProps || {};
2154
- if (props.source) {
2155
- const sourceName = typeof props.source === "string"
2156
- ? props.source
2157
- .replace("aws.", "")
2158
- .split(".")
2159
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2160
- .join("")
2161
- : "Events";
2162
- id = props.id || `${sourceName}EventsRule`;
2163
- }
2164
- else {
2165
- id = props.id || "EventsRule";
2166
- }
2167
- }
2168
- super(scope, id);
2169
- // Extract Jaypie-specific options
2170
- const { id: _id, project, service = CDK$2.SERVICE.DATADOG, source, targetFunction, vendor = CDK$2.VENDOR.DATADOG, ...ruleProps } = props;
2171
- // Resolve target function
2172
- this.targetFunction =
2173
- targetFunction || resolveDatadogForwarderFunction(scope);
2174
- // Build event pattern if source is specified
2175
- const eventPattern = source
2176
- ? {
2177
- ...ruleProps.eventPattern,
2178
- source: Array.isArray(source) ? source : [source],
2179
- }
2180
- : ruleProps.eventPattern;
2181
- // Build rule props
2182
- const finalRuleProps = {
2183
- ...ruleProps,
2184
- eventPattern,
2185
- targets: [
2186
- new awsEventsTargets.LambdaFunction(this.targetFunction, {
2187
- event: awsEvents.RuleTargetInput.fromEventPath("$"),
2188
- }),
2189
- ],
2190
- };
2191
- // Create the rule
2192
- this.rule = new awsEvents.Rule(this, "Rule", finalRuleProps);
2193
- // Add tags
2194
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2195
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.SERVICE, service);
2196
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.VENDOR, vendor);
2197
- if (project) {
2198
- cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.PROJECT, project);
2199
- }
2200
- }
2201
- }
2202
-
2203
- class JaypieGitHubDeployRole extends constructs.Construct {
2204
- constructor(scope, id = "GitHubDeployRole", props = {}) {
2205
- super(scope, id);
2206
- const { oidcProviderArn = cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
2207
- // Extract account ID from the scope
2208
- const accountId = cdk.Stack.of(this).account;
2209
- // Resolve repoRestriction from props or environment variables
2210
- let repoRestriction = propsRepoRestriction;
2211
- if (!repoRestriction) {
2212
- const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
2213
- if (!envRepo) {
2214
- throw new errors.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
2215
- }
2216
- // Extract organization from owner/repo format and create org-wide restriction
2217
- const organization = envRepo.split("/")[0];
2218
- repoRestriction = `repo:${organization}/*:*`;
2219
- }
2220
- // Create the IAM role
2221
- this._role = new awsIam.Role(this, "GitHubActionsRole", {
2222
- assumedBy: new awsIam.FederatedPrincipal(oidcProviderArn, {
2223
- StringLike: {
2224
- "token.actions.githubusercontent.com:sub": repoRestriction,
2225
- },
2226
- }, "sts:AssumeRoleWithWebIdentity"),
2227
- maxSessionDuration: cdk.Duration.hours(1),
2228
- path: "/",
2229
- });
2230
- cdk.Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
2231
- // Allow the role to access the GitHub OIDC provider
2232
- this._role.addToPolicy(new awsIam.PolicyStatement({
2233
- actions: ["sts:AssumeRoleWithWebIdentity"],
2234
- resources: [`arn:aws:iam::${accountId}:oidc-provider/*`],
2235
- }));
2236
- // Allow the role to deploy CDK apps
2237
- this._role.addToPolicy(new awsIam.PolicyStatement({
2238
- actions: [
2239
- "cloudformation:CreateStack",
2240
- "cloudformation:DeleteStack",
2241
- "cloudformation:DescribeStackEvents",
2242
- "cloudformation:DescribeStackResource",
2243
- "cloudformation:DescribeStackResources",
2244
- "cloudformation:DescribeStacks",
2245
- "cloudformation:GetTemplate",
2246
- "cloudformation:SetStackPolicy",
2247
- "cloudformation:UpdateStack",
2248
- "cloudformation:ValidateTemplate",
2249
- "iam:PassRole",
2250
- "route53:ListHostedZones*",
2251
- "s3:GetObject",
2252
- "s3:ListBucket",
2253
- ],
2254
- effect: awsIam.Effect.ALLOW,
2255
- resources: ["*"],
2256
- }));
2257
- this._role.addToPolicy(new awsIam.PolicyStatement({
2258
- actions: ["iam:PassRole", "sts:AssumeRole"],
2259
- effect: awsIam.Effect.ALLOW,
2260
- resources: [
2261
- "arn:aws:iam::*:role/cdk-hnb659fds-deploy-role-*",
2262
- "arn:aws:iam::*:role/cdk-hnb659fds-file-publishing-*",
2263
- "arn:aws:iam::*:role/cdk-readOnlyRole",
2264
- ],
2265
- }));
2266
- // Export the ARN of the role
2267
- if (output !== false) {
2268
- const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
2269
- new cdk.CfnOutput(this, outputId, {
2270
- value: this._role.roleArn,
2271
- });
2272
- }
2273
- }
2274
- get role() {
2275
- return this._role;
2276
- }
2277
- get roleArn() {
2278
- return this._role.roleArn;
2279
- }
2280
- get roleName() {
2281
- return this._role.roleName;
2282
- }
2283
- }
2284
-
2285
- class JaypieExpressLambda extends JaypieLambda {
2286
- constructor(scope, id, props) {
2287
- super(scope, id, {
2288
- timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
2289
- roleTag: CDK$2.ROLE.API,
2290
- ...props,
2291
- });
2292
- }
2293
- }
2294
-
2295
- const SERVICE = {
2296
- ROUTE53: "route53.amazonaws.com",
2297
- };
2298
- /**
2299
- * Check if a string is a valid hostname
2300
- */
2301
- function isValidHostname(str) {
2302
- // Check if it contains a dot and matches hostname pattern
2303
- if (!str.includes("."))
2304
- return false;
2305
- // Basic hostname validation: alphanumeric, hyphens, dots
2306
- // Each label must start and end with alphanumeric
2307
- const hostnameRegex = /^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i;
2308
- return hostnameRegex.test(str);
2309
- }
2310
- class JaypieHostedZone extends constructs.Construct {
2311
- /**
2312
- * Create a new hosted zone with query logging and optional DNS records
2313
- */
2314
- constructor(scope, idOrProps, propsOrRecords) {
2315
- // Handle overloaded constructor signatures
2316
- let props;
2317
- let id;
2318
- if (typeof idOrProps === "string") {
2319
- // If it's a valid hostname, treat it as zoneName
2320
- if (isValidHostname(idOrProps)) {
2321
- // Third param can be props object or records array
2322
- if (Array.isArray(propsOrRecords)) {
2323
- props = { zoneName: idOrProps, records: propsOrRecords };
2324
- }
2325
- else {
2326
- props = propsOrRecords || { zoneName: idOrProps };
2327
- // Set zoneName if not already set
2328
- if (!props.zoneName) {
2329
- props = { ...props, zoneName: idOrProps };
2330
- }
2331
- }
2332
- // Use id from props if provided, otherwise derive from zoneName
2333
- id = props.id || `${idOrProps}-HostedZone`;
2334
- }
2335
- else {
2336
- // Otherwise treat it as an explicit id
2337
- props = propsOrRecords;
2338
- id = idOrProps;
2339
- }
2340
- }
2341
- else {
2342
- // idOrProps is props
2343
- props = idOrProps;
2344
- id = props.id || `${props.zoneName}-HostedZone`;
2345
- }
2346
- super(scope, id);
2347
- const { zoneName, project } = props;
2348
- const destination = props.destination ?? true;
2349
- const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
2350
- // Create the log group
2351
- this.logGroup = new logs.LogGroup(this, "LogGroup", {
2352
- logGroupName: process.env.PROJECT_NONCE
2353
- ? `/aws/route53/${zoneName}-${process.env.PROJECT_NONCE}`
2354
- : `/aws/route53/${zoneName}`,
2355
- retention: logs.RetentionDays.ONE_WEEK,
2356
- });
2357
- // Add tags
2358
- cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
2359
- cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2360
- if (project) {
2361
- cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
2362
- }
2363
- // Grant Route 53 permissions to write to the log group
2364
- this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
2365
- // Add destination based on configuration
2366
- if (destination !== false) {
2367
- const lambdaDestination = destination === true
2368
- ? resolveDatadogLoggingDestination(scope)
2369
- : destination;
2370
- this.logGroup.addSubscriptionFilter("DatadogLambdaDestination", {
2371
- destination: lambdaDestination,
2372
- filterPattern: logs.FilterPattern.allEvents(),
2373
- });
2374
- }
2375
- // Create the hosted zone
2376
- this.hostedZone = new route53.HostedZone(this, "HostedZone", {
2377
- queryLogsLogGroupArn: this.logGroup.logGroupArn,
2378
- zoneName,
2379
- });
2380
- // Add tags
2381
- cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
2382
- cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2383
- if (project) {
2384
- cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
2385
- }
2386
- // Create DNS records if provided
2387
- this.dnsRecords = [];
2388
- if (props.records) {
2389
- props.records.forEach((recordConfig, index) => {
2390
- const { id, ...recordProps } = recordConfig;
2391
- // Generate a default ID if not provided
2392
- const recordId = id ||
2393
- `${recordProps.type}${recordProps.recordName ? `-${recordProps.recordName}` : ""}-${index}`;
2394
- const dnsRecord = new JaypieDnsRecord(this, recordId, {
2395
- ...recordProps,
2396
- zone: this.hostedZone,
2397
- });
2398
- this.dnsRecords.push(dnsRecord);
2399
- });
2400
- }
2401
- }
2402
- }
2403
-
2404
- const CDK = {
2405
- TAG: {
2406
- STACK_SHA: "stackSha",
2407
- },
2408
- };
2409
- class JaypieInfrastructureStack extends JaypieStack {
2410
- constructor(scope, id, props = {}) {
2411
- const { key = "infra", ...stackProps } = props;
2412
- // Handle stackName
2413
- if (!stackProps.stackName) {
2414
- stackProps.stackName = constructStackName(key);
2415
- }
2416
- super(scope, id, { key, ...stackProps });
2417
- // Add infrastructure-specific tag
2418
- if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
2419
- cdk.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
2420
- }
2421
- }
2422
- }
2423
-
2424
- class JaypieMongoDbSecret extends JaypieEnvSecret {
2425
- constructor(scope, id = "MongoConnectionString", props) {
2426
- const defaultProps = {
2427
- envKey: "MONGODB_URI",
2428
- roleTag: CDK$2.ROLE.STORAGE,
2429
- vendorTag: CDK$2.VENDOR.MONGODB,
2430
- ...props,
2431
- };
2432
- super(scope, id, defaultProps);
2433
- }
2434
- }
2435
-
2436
- class JaypieNextJs extends constructs.Construct {
2437
- constructor(scope, id, props) {
2438
- super(scope, id);
2439
- const domainName = props?.domainName || envHostname();
2440
- this.domainName = domainName;
2441
- const domainNameSanitized = domainName
2442
- .replace(/\./g, "-")
2443
- .replace(/[^a-zA-Z0-9]/g, "_");
2444
- const envSecrets = props?.envSecrets || {};
2445
- const nextjsPath = props?.nextjsPath?.startsWith("..")
2446
- ? path__namespace.join(process.cwd(), props.nextjsPath)
2447
- : props?.nextjsPath || path__namespace.join(process.cwd(), "..", "nextjs");
2448
- const paramsAndSecrets = resolveParamsAndSecrets();
2449
- const secrets = props?.secrets || [];
2450
- // Process secrets environment variables
2451
- const secretsEnvironment = Object.entries(envSecrets).reduce((acc, [key, secret]) => ({
2452
- ...acc,
2453
- [`SECRET_${key}`]: secret.secretName,
2454
- }), {});
2455
- // Process JaypieEnvSecret array
2456
- const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
2457
- if (secret.envKey) {
2458
- return {
2459
- ...acc,
2460
- [`SECRET_${secret.envKey}`]: secret.secretName,
2461
- };
2462
- }
2463
- return acc;
2464
- }, {});
2465
- // Process NEXT_PUBLIC_ environment variables
2466
- const nextPublicEnv = Object.entries(process.env).reduce((acc, [key, value]) => {
2467
- if (key.startsWith("NEXT_PUBLIC_") && value) {
2468
- return {
2469
- ...acc,
2470
- [key]: value,
2471
- };
2472
- }
2473
- return acc;
2474
- }, {});
2475
- const nextjs = new cdkNextjsStandalone.Nextjs(this, "NextJsApp", {
2476
- nextjsPath,
2477
- domainProps: {
2478
- domainName,
2479
- hostedZone: resolveHostedZone(this, {
2480
- zone: props?.hostedZone,
2481
- }),
2482
- },
2483
- environment: {
2484
- ...jaypieLambdaEnv(),
2485
- ...secretsEnvironment,
2486
- ...jaypieSecretsEnvironment,
2487
- ...nextPublicEnv,
2488
- NEXT_PUBLIC_SITE_URL: `https://${domainName}`,
2489
- },
2490
- overrides: {
2491
- nextjsDistribution: {
2492
- imageCachePolicyProps: {
2493
- cachePolicyName: `NextJsImageCachePolicy-${domainNameSanitized}`,
2494
- },
2495
- serverCachePolicyProps: {
2496
- cachePolicyName: `NextJsServerCachePolicy-${domainNameSanitized}`,
2497
- },
2498
- },
2499
- nextjsImage: {
2500
- functionProps: {
2501
- paramsAndSecrets,
2502
- },
2503
- },
2504
- nextjsServer: {
2505
- functionProps: {
2506
- paramsAndSecrets,
2507
- },
2508
- },
2509
- },
2510
- });
2511
- addDatadogLayers(nextjs.imageOptimizationFunction);
2512
- addDatadogLayers(nextjs.serverFunction.lambdaFunction);
2513
- // Grant secret read permissions
2514
- Object.values(envSecrets).forEach((secret) => {
2515
- secret.grantRead(nextjs.serverFunction.lambdaFunction);
2516
- });
2517
- // Grant read permissions for JaypieEnvSecrets
2518
- secrets.forEach((secret) => {
2519
- secret.grantRead(nextjs.serverFunction.lambdaFunction);
2520
- });
2521
- }
2522
- }
2523
-
2524
- class JaypieOpenAiSecret extends JaypieEnvSecret {
2525
- constructor(scope, id = "OpenAiApiKey", props) {
2526
- const defaultProps = {
2527
- envKey: "OPENAI_API_KEY",
2528
- roleTag: CDK$2.ROLE.PROCESSING,
2529
- vendorTag: CDK$2.VENDOR.OPENAI,
2530
- ...props,
2531
- };
2532
- super(scope, id, defaultProps);
2533
- }
2534
- }
2535
-
2536
- class JaypieOrganizationTrail extends constructs.Construct {
2537
- /**
2538
- * Create a new organization CloudTrail with S3 bucket and lifecycle policies
2539
- */
2540
- constructor(scope, idOrProps, propsOrUndefined) {
2541
- // Handle overloaded constructor signatures
2542
- let props;
2543
- let id;
2544
- if (typeof idOrProps === "string") {
2545
- // First param is ID, second is props
2546
- props = propsOrUndefined || {};
2547
- id = idOrProps;
2548
- }
2549
- else {
2550
- // First param is props
2551
- props = idOrProps || {};
2552
- const defaultName = process.env.PROJECT_NONCE
2553
- ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2554
- : "organization-cloudtrail";
2555
- id = props.id || `${props.trailName || defaultName}-Trail`;
2556
- }
2557
- super(scope, id);
2558
- // Resolve options with defaults
2559
- const { bucketName = process.env.PROJECT_NONCE
2560
- ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2561
- : "organization-cloudtrail", enableDatadogNotifications = true, enableFileValidation = false, expirationDays = 365, glacierTransitionDays = 180, infrequentAccessTransitionDays = 30, project, service = CDK$2.SERVICE.INFRASTRUCTURE, trailName = process.env.PROJECT_NONCE
2562
- ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2563
- : "organization-cloudtrail", } = props;
2564
- // Create the S3 bucket for CloudTrail logs
2565
- this.bucket = new s3.Bucket(this, "Bucket", {
2566
- accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
2567
- bucketName,
2568
- lifecycleRules: [
2569
- {
2570
- expiration: cdk__namespace.Duration.days(expirationDays),
2571
- transitions: [
2572
- {
2573
- storageClass: s3.StorageClass.INFREQUENT_ACCESS,
2574
- transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
2575
- },
2576
- {
2577
- storageClass: s3.StorageClass.GLACIER,
2578
- transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
2579
- },
2580
- ],
2581
- },
2582
- ],
2583
- });
2584
- // Add CloudTrail bucket policies
2585
- this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2586
- actions: ["s3:GetBucketAcl"],
2587
- effect: awsIam.Effect.ALLOW,
2588
- principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2589
- resources: [this.bucket.bucketArn],
2590
- }));
2591
- this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2592
- actions: ["s3:PutObject"],
2593
- conditions: {
2594
- StringEquals: {
2595
- "s3:x-amz-acl": "bucket-owner-full-control",
2596
- },
2597
- },
2598
- effect: awsIam.Effect.ALLOW,
2599
- principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2600
- resources: [`${this.bucket.bucketArn}/*`],
2601
- }));
2602
- // Add tags to bucket
2603
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
2604
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2605
- if (project) {
2606
- cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
2607
- }
2608
- // Add Datadog notifications if enabled
2609
- if (enableDatadogNotifications) {
2610
- const datadogForwarderFunction = resolveDatadogForwarderFunction(scope);
2611
- this.bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(datadogForwarderFunction));
2612
- }
2613
- // Create the organization trail
2614
- this.trail = new awsCloudtrail.Trail(this, "Trail", {
2615
- bucket: this.bucket,
2616
- enableFileValidation,
2617
- isOrganizationTrail: true,
2618
- managementEvents: awsCloudtrail.ReadWriteType.ALL,
2619
- trailName,
2620
- });
2621
- // Add tags to trail
2622
- cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.SERVICE, service);
2623
- cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2624
- if (project) {
2625
- cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.PROJECT, project);
2626
- }
2627
- }
2628
- }
2629
-
2630
- /**
2631
- * JaypieSsoPermissions Construct
2632
- *
2633
- * Creates and manages AWS IAM Identity Center (SSO) permission sets and assignments
2634
- *
2635
- * @example
2636
- * const permissionSets = new JaypieSsoPermissions(this, "PermissionSets", {
2637
- * iamIdentityCenterArn: "arn:aws:sso:::instance/...",
2638
- * administratorGroupId: "b4c8b438-4031-7000-782d-5046945fb956",
2639
- * analystGroupId: "2488f4e8-d061-708e-abe1-c315f0e30005",
2640
- * developerGroupId: "b438a4f8-e0e1-707c-c6e8-21841daf9ad1",
2641
- * administratorAccountAssignments: {
2642
- * "211125635435": ["Administrator", "Analyst", "Developer"],
2643
- * "381492033431": ["Administrator", "Analyst"],
2644
- * },
2645
- * analystAccountAssignments: {
2646
- * "211125635435": ["Analyst", "Developer"],
2647
- * "381492033431": [],
2648
- * },
2649
- * developerAccountAssignments: {
2650
- * "211125635435": ["Analyst", "Developer"],
2651
- * "381492033431": [],
2652
- * },
2653
- * });
2654
- */
2655
- class JaypieSsoPermissions extends constructs.Construct {
2656
- constructor(scope, id, props) {
2657
- super(scope, id);
2658
- const { iamIdentityCenterArn: iamIdentityCenterArnProp, administratorGroupId, analystGroupId, developerGroupId, administratorAccountAssignments, analystAccountAssignments, developerAccountAssignments, } = props;
2659
- const iamIdentityCenterArn = iamIdentityCenterArnProp || process.env.CDK_ENV_IAM_IDENTITY_CENTER_ARN;
2660
- if (!iamIdentityCenterArn) {
2661
- // If no IAM Identity Center ARN provided, skip SSO setup
2662
- return;
2663
- }
2664
- //
2665
- // Permission Sets
2666
- //
2667
- this.administratorPermissionSet = new awsSso.CfnPermissionSet(this, "AdministratorPermissionSet", {
2668
- // Required
2669
- instanceArn: iamIdentityCenterArn,
2670
- name: "Administrator",
2671
- // Optional
2672
- description: "Unrestricted access",
2673
- inlinePolicy: {
2674
- Version: "2012-10-17",
2675
- Statement: [
2676
- {
2677
- Effect: "Allow",
2678
- Action: [
2679
- "aws-portal:ViewUsage",
2680
- "aws-portal:ViewBilling",
2681
- "budgets:*",
2682
- "cur:DescribeReportDefinitions",
2683
- "cur:PutReportDefinition",
2684
- "cur:DeleteReportDefinition",
2685
- "cur:ModifyReportDefinition",
2686
- ],
2687
- Resource: "*",
2688
- },
2689
- ],
2690
- },
2691
- managedPolicies: [
2692
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess")
2693
- .managedPolicyArn,
2694
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2695
- ],
2696
- sessionDuration: cdk.Duration.hours(1).toIsoString(),
2697
- tags: [
2698
- {
2699
- key: CDK$2.TAG.SERVICE,
2700
- value: CDK$2.SERVICE.SSO,
2701
- },
2702
- {
2703
- key: CDK$2.TAG.ROLE,
2704
- value: CDK$2.ROLE.SECURITY,
2705
- },
2706
- ],
2707
- });
2708
- this.analystPermissionSet = new awsSso.CfnPermissionSet(this, "AnalystPermissionSet", {
2709
- // Required
2710
- instanceArn: iamIdentityCenterArn,
2711
- name: "Analyst",
2712
- // Optional
2713
- description: "Read-only access; may expand to limited write access",
2714
- inlinePolicy: {
2715
- Version: "2012-10-17",
2716
- Statement: [
2717
- {
2718
- Effect: "Allow",
2719
- Action: [
2720
- "aws-portal:ViewUsage",
2721
- "aws-portal:ViewBilling",
2722
- "budgets:Describe*",
2723
- "budgets:View*",
2724
- "ce:Get*",
2725
- "ce:List*",
2726
- "cloudformation:Describe*",
2727
- "cloudformation:Get*",
2728
- "cloudformation:List*",
2729
- "cloudwatch:BatchGet*",
2730
- "cloudwatch:Get*",
2731
- "cloudwatch:List*",
2732
- "cost-optimization-hub:Get*",
2733
- "cost-optimization-hub:List*",
2734
- "ec2:Describe*",
2735
- "ec2:Get*",
2736
- "ec2:List*",
2737
- "ec2:Search*",
2738
- "iam:Get*",
2739
- "iam:List*",
2740
- "iam:PassRole",
2741
- "lambda:Get*",
2742
- "lambda:List*",
2743
- "logs:Describe*",
2744
- "logs:Get*",
2745
- "logs:List*",
2746
- "pipes:Describe*",
2747
- "pipes:List*",
2748
- "s3:Get*",
2749
- "s3:List*",
2750
- "secretsmanager:GetRandomPassword",
2751
- "secretsmanager:GetResourcePolicy",
2752
- "secretsmanager:List*",
2753
- "securityhub:Describe*",
2754
- "securityhub:Get*",
2755
- "securityhub:List*",
2756
- "servicecatalog:Describe*",
2757
- "sns:Get*",
2758
- "sns:List*",
2759
- "sqs:Get*",
2760
- "sqs:List*",
2761
- "states:Describe*",
2762
- "states:Get*",
2763
- "states:List*",
2764
- "tag:*",
2765
- "uxc:*",
2766
- "xray:*",
2767
- ],
2768
- Resource: "*",
2769
- },
2770
- ],
2771
- },
2772
- managedPolicies: [
2773
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
2774
- .managedPolicyArn,
2775
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2776
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
2777
- .managedPolicyArn,
2778
- ],
2779
- sessionDuration: cdk.Duration.hours(12).toIsoString(),
2780
- tags: [
2781
- {
2782
- key: CDK$2.TAG.SERVICE,
2783
- value: CDK$2.SERVICE.SSO,
2784
- },
2785
- {
2786
- key: CDK$2.TAG.ROLE,
2787
- value: CDK$2.ROLE.SECURITY,
2788
- },
2789
- ],
2790
- });
2791
- this.developerPermissionSet = new awsSso.CfnPermissionSet(this, "DeveloperPermissionSet", {
2792
- // Required
2793
- instanceArn: iamIdentityCenterArn,
2794
- name: "Developer",
2795
- // Optional
2796
- description: "Administrative access with limited restrictions",
2797
- inlinePolicy: {
2798
- Version: "2012-10-17",
2799
- Statement: [
2800
- {
2801
- Effect: "Allow",
2802
- Action: [
2803
- "budgets:*",
2804
- "ce:*",
2805
- "cloudformation:*",
2806
- "cloudwatch:*",
2807
- "cost-optimization-hub:*",
2808
- "ec2:*",
2809
- "iam:Get*",
2810
- "iam:List*",
2811
- "iam:PassRole",
2812
- "lambda:*",
2813
- "logs:*",
2814
- "pipes:*",
2815
- "s3:*",
2816
- "secretsmanager:*",
2817
- "securityhub:*",
2818
- "servicecatalog:*",
2819
- "sns:*",
2820
- "sqs:*",
2821
- "states:*",
2822
- "tag:*",
2823
- "uxc:*",
2824
- "xray:*",
2825
- ],
2826
- Resource: "*",
2827
- },
2828
- ],
2829
- },
2830
- managedPolicies: [
2831
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
2832
- .managedPolicyArn,
2833
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2834
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
2835
- .managedPolicyArn,
2836
- awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
2837
- ],
2838
- sessionDuration: cdk.Duration.hours(4).toIsoString(),
2839
- tags: [
2840
- {
2841
- key: CDK$2.TAG.SERVICE,
2842
- value: CDK$2.SERVICE.SSO,
2843
- },
2844
- {
2845
- key: CDK$2.TAG.ROLE,
2846
- value: CDK$2.ROLE.SECURITY,
2847
- },
2848
- ],
2849
- });
2850
- // Map permission set names to their ARNs and labels
2851
- const permissionSetMap = {
2852
- Administrator: {
2853
- arn: this.administratorPermissionSet.attrPermissionSetArn,
2854
- label: "Administrator",
2855
- },
2856
- Analyst: {
2857
- arn: this.analystPermissionSet.attrPermissionSetArn,
2858
- label: "Analyst",
2859
- },
2860
- Developer: {
2861
- arn: this.developerPermissionSet.attrPermissionSetArn,
2862
- label: "Developer",
2863
- },
2864
- };
2865
- //
2866
- // Assignments
2867
- //
2868
- // Helper function to create assignments for a group
2869
- const createAssignments = (groupId, accountAssignments) => {
2870
- if (!groupId || !accountAssignments) {
2871
- return; // Skip if group ID or assignments not provided
2872
- }
2873
- Object.keys(accountAssignments).forEach((accountId) => {
2874
- const permissionSetNames = accountAssignments[accountId];
2875
- permissionSetNames.forEach((permissionSetName) => {
2876
- const permissionSet = permissionSetMap[permissionSetName];
2877
- if (!permissionSet) {
2878
- throw new errors.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
2879
- }
2880
- const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
2881
- // Required
2882
- instanceArn: iamIdentityCenterArn,
2883
- permissionSetArn: permissionSet.arn,
2884
- principalId: groupId,
2885
- principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
2886
- targetId: accountId,
2887
- targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
2888
- });
2889
- cdk.Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
2890
- cdk.Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
2891
- });
2892
- });
2893
- };
2894
- // Create assignments for each group
2895
- createAssignments(administratorGroupId, administratorAccountAssignments);
2896
- createAssignments(analystGroupId, analystAccountAssignments);
2897
- createAssignments(developerGroupId, developerAccountAssignments);
2898
- }
2899
- }
2900
-
2901
- //
2902
- //
2903
- // Constants
2904
- //
2905
- const DEFAULT_APPLICATION_ID = "arn:aws:serverlessrepo:us-east-2:004480582608:applications/SSOSync";
2906
- const DEFAULT_APPLICATION_VERSION = "2.3.3";
2907
- const DEFAULT_GOOGLE_GROUP_MATCH = "name:AWS*";
2908
- //
2909
- //
2910
- // Class
2911
- //
2912
- class JaypieSsoSyncApplication extends constructs.Construct {
2913
- constructor(scope, id = "SsoSyncApplication", props = {}) {
2914
- super(scope, id);
2915
- 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;
2916
- // Resolve all values from props or environment variables
2917
- const resolvedGoogleAdminEmail = googleAdminEmail || process.env[googleAdminEmailEnvKey];
2918
- const resolvedGoogleCredentials = googleCredentials || process.env[googleCredentialsEnvKey];
2919
- const resolvedGoogleGroupMatch = googleGroupMatch ||
2920
- process.env[googleGroupMatchEnvKey] ||
2921
- DEFAULT_GOOGLE_GROUP_MATCH;
2922
- const resolvedIdentityStoreId = identityStoreId || process.env[identityStoreIdEnvKey];
2923
- const resolvedScimEndpointAccessToken = scimEndpointAccessToken || process.env[scimEndpointAccessTokenEnvKey];
2924
- const resolvedScimEndpointUrl = scimEndpointUrl || process.env[scimEndpointUrlEnvKey];
2925
- const resolvedSemanticVersion = semanticVersion ||
2926
- process.env[semanticVersionEnvKey] ||
2927
- DEFAULT_APPLICATION_VERSION;
2928
- // Validate required parameters
2929
- const missingParams = [];
2930
- if (!resolvedGoogleAdminEmail) {
2931
- missingParams.push(`googleAdminEmail or ${googleAdminEmailEnvKey} environment variable`);
2932
- }
2933
- if (!resolvedGoogleCredentials) {
2934
- missingParams.push(`googleCredentials or ${googleCredentialsEnvKey} environment variable`);
2935
- }
2936
- if (!resolvedIdentityStoreId) {
2937
- missingParams.push(`identityStoreId or ${identityStoreIdEnvKey} environment variable`);
2938
- }
2939
- if (!resolvedScimEndpointAccessToken) {
2940
- missingParams.push(`scimEndpointAccessToken or ${scimEndpointAccessTokenEnvKey} environment variable`);
2941
- }
2942
- if (!resolvedScimEndpointUrl) {
2943
- missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
2944
- }
2945
- if (missingParams.length > 0) {
2946
- throw new errors.ConfigurationError(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
2947
- }
2948
- // Create the SSO Sync Application
2949
- // Type assertion is safe because we validated all required values above
2950
- this._application = new awsSam.CfnApplication(this, "Application", {
2951
- location: {
2952
- applicationId: ssoSyncApplicationId,
2953
- semanticVersion: resolvedSemanticVersion,
2954
- },
2955
- parameters: {
2956
- GoogleAdminEmail: resolvedGoogleAdminEmail,
2957
- GoogleCredentials: resolvedGoogleCredentials,
2958
- GoogleGroupMatch: resolvedGoogleGroupMatch,
2959
- IdentityStoreID: resolvedIdentityStoreId,
2960
- Region: cdk.Stack.of(this).region,
2961
- SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
2962
- SCIMEndpointUrl: resolvedScimEndpointUrl,
2963
- },
2964
- });
2965
- // Add tags
2966
- const defaultTags = {
2967
- [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
2968
- };
2969
- const allTags = { ...defaultTags, ...tags };
2970
- Object.entries(allTags).forEach(([key, value]) => {
2971
- cdk.Tags.of(this._application).add(key, value);
2972
- });
2973
- }
2974
- get application() {
2975
- return this._application;
2976
- }
2977
- }
2978
-
2979
- class JaypieWebDeploymentBucket extends constructs.Construct {
2980
- constructor(scope, id, props = {}) {
2981
- super(scope, id);
2982
- const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
2983
- // Environment variable validation
2984
- if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
2985
- !isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
2986
- throw new errors.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
2987
- }
2988
- if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
2989
- !isValidHostname$1(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
2990
- throw new errors.ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
2991
- }
2992
- if (process.env.CDK_ENV_HOSTED_ZONE &&
2993
- !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
2994
- throw new errors.ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
2995
- }
2996
- // Determine host from props or environment
2997
- let host = props.host;
2998
- if (!host) {
2999
- try {
3000
- host =
3001
- process.env.CDK_ENV_WEB_HOST ||
3002
- mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
3003
- process.env.CDK_ENV_HOSTED_ZONE ||
3004
- "");
3005
- }
3006
- catch {
3007
- host = undefined;
3008
- }
3009
- }
3010
- if (host && !isValidHostname$1(host)) {
3011
- throw new errors.ConfigurationError("Host is not a valid hostname");
3012
- }
3013
- // Determine zone from props or environment
3014
- const zone = props.zone ||
3015
- process.env.CDK_ENV_WEB_HOSTED_ZONE ||
3016
- process.env.CDK_ENV_HOSTED_ZONE;
3017
- // Create the S3 bucket
3018
- this.bucket = new s3__namespace.Bucket(this, "DestinationBucket", {
3019
- accessControl: s3__namespace.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
3020
- autoDeleteObjects: true,
3021
- blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS_ONLY,
3022
- bucketName: props.name || constructEnvName("web"),
3023
- publicReadAccess: true,
3024
- removalPolicy: cdk.RemovalPolicy.DESTROY,
3025
- versioned: false,
3026
- websiteErrorDocument: "index.html",
3027
- websiteIndexDocument: "index.html",
3028
- ...props,
3029
- });
3030
- // Delegate IBucket properties to the bucket
3031
- this.bucketArn = this.bucket.bucketArn;
3032
- this.bucketDomainName = this.bucket.bucketDomainName;
3033
- this.bucketDualStackDomainName = this.bucket.bucketDualStackDomainName;
3034
- this.bucketName = this.bucket.bucketName;
3035
- this.bucketRegionalDomainName = this.bucket.bucketRegionalDomainName;
3036
- this.bucketWebsiteDomainName = this.bucket.bucketWebsiteDomainName;
3037
- this.bucketWebsiteUrl = this.bucket.bucketWebsiteUrl;
3038
- this.encryptionKey = this.bucket.encryptionKey;
3039
- this.isWebsite = this.bucket.isWebsite;
3040
- this.notificationsHandlerRole = undefined;
3041
- this.policy = this.bucket.policy;
3042
- cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
3043
- // Create deployment role if repository is configured
3044
- let repo;
3045
- if (process.env.CDK_ENV_REPO) {
3046
- repo = `repo:${process.env.CDK_ENV_REPO}:*`;
3047
- }
3048
- if (repo) {
3049
- const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
3050
- assumedBy: new awsIam.FederatedPrincipal(cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
3051
- StringLike: {
3052
- "token.actions.githubusercontent.com:sub": repo,
3053
- },
3054
- }, "sts:AssumeRoleWithWebIdentity"),
3055
- maxSessionDuration: cdk.Duration.hours(1),
3056
- });
3057
- cdk.Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
3058
- // Allow the role to write to the bucket
3059
- bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3060
- effect: awsIam.Effect.ALLOW,
3061
- actions: [
3062
- "s3:DeleteObject",
3063
- "s3:GetObject",
3064
- "s3:ListObjectsV2",
3065
- "s3:PutObject",
3066
- ],
3067
- resources: [`${this.bucket.bucketArn}/*`],
3068
- }));
3069
- bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3070
- effect: awsIam.Effect.ALLOW,
3071
- actions: ["s3:ListBucket"],
3072
- resources: [this.bucket.bucketArn],
3073
- }));
3074
- // Allow the role to describe the current stack
3075
- const stack = cdk.Stack.of(this);
3076
- bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3077
- actions: ["cloudformation:DescribeStacks"],
3078
- effect: awsIam.Effect.ALLOW,
3079
- resources: [
3080
- `arn:aws:cloudformation:${stack.region}:${stack.account}:stack/${stack.stackName}/*`,
3081
- ],
3082
- }));
3083
- this.deployRoleArn = bucketDeployRole.roleArn;
3084
- // Output the deploy role ARN
3085
- new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
3086
- value: bucketDeployRole.roleArn,
3087
- });
3088
- }
3089
- // Create CloudFront distribution and certificate if host and zone are provided
3090
- if (host && zone) {
3091
- let hostedZone;
3092
- if (typeof zone === "string") {
3093
- hostedZone = route53__namespace.HostedZone.fromLookup(this, "HostedZone", {
3094
- domainName: zone,
3095
- });
3096
- }
3097
- else if (zone instanceof JaypieHostedZone) {
3098
- hostedZone = zone.hostedZone;
3099
- }
3100
- else {
3101
- hostedZone = zone;
3102
- }
3103
- // Create certificate if not provided
3104
- if (props.certificate !== false) {
3105
- this.certificate =
3106
- typeof props.certificate === "object"
3107
- ? props.certificate
3108
- : new acm__namespace.Certificate(this, "Certificate", {
3109
- domainName: host,
3110
- validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
3111
- });
3112
- new cdk.CfnOutput(this, "CertificateArn", {
3113
- value: this.certificate.certificateArn,
3114
- });
3115
- cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
3116
- }
3117
- // Create CloudFront distribution
3118
- this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
3119
- defaultBehavior: {
3120
- cachePolicy: cloudfront__namespace.CachePolicy.CACHING_DISABLED,
3121
- origin: new origins__namespace.S3StaticWebsiteOrigin(this.bucket),
3122
- viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
3123
- },
3124
- certificate: this.certificate,
3125
- domainNames: [host],
3126
- });
3127
- cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
3128
- // If this is production, enable caching on everything but index.html
3129
- if (isProductionEnv()) {
3130
- this.distribution.addBehavior("/*", new origins__namespace.S3StaticWebsiteOrigin(this.bucket), {
3131
- viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
3132
- cachePolicy: cloudfront__namespace.CachePolicy.CACHING_OPTIMIZED,
3133
- });
3134
- }
3135
- // Create DNS record
3136
- const record = new route53__namespace.ARecord(this, "AliasRecord", {
3137
- recordName: host,
3138
- target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
3139
- zone: hostedZone,
3140
- });
3141
- cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
3142
- this.distributionDomainName = this.distribution.distributionDomainName;
3143
- }
3144
- }
3145
- // Implement remaining IBucket methods by delegating to the bucket
3146
- addEventNotification(event, dest, ...filters) {
3147
- this.bucket.addEventNotification(event, dest, ...filters);
3148
- }
3149
- addObjectCreatedNotification(dest, ...filters) {
3150
- this.bucket.addObjectCreatedNotification(dest, ...filters);
3151
- }
3152
- addObjectRemovedNotification(dest, ...filters) {
3153
- this.bucket.addObjectRemovedNotification(dest, ...filters);
3154
- }
3155
- addToResourcePolicy(permission) {
3156
- return this.bucket.addToResourcePolicy(permission);
3157
- }
3158
- arnForObjects(keyPattern) {
3159
- return this.bucket.arnForObjects(keyPattern);
3160
- }
3161
- grantDelete(identity, objectsKeyPattern) {
3162
- return this.bucket.grantDelete(identity, objectsKeyPattern);
3163
- }
3164
- grantPublicAccess(allowedActions, keyPrefix) {
3165
- return keyPrefix
3166
- ? this.bucket.grantPublicAccess(allowedActions, keyPrefix)
3167
- : this.bucket.grantPublicAccess(allowedActions);
3168
- }
3169
- grantPut(identity, objectsKeyPattern) {
3170
- return this.bucket.grantPut(identity, objectsKeyPattern);
3171
- }
3172
- grantPutAcl(identity, objectsKeyPattern) {
3173
- return this.bucket.grantPutAcl(identity, objectsKeyPattern);
3174
- }
3175
- grantRead(identity, objectsKeyPattern) {
3176
- return this.bucket.grantRead(identity, objectsKeyPattern);
3177
- }
3178
- grantReadWrite(identity, objectsKeyPattern) {
3179
- return this.bucket.grantReadWrite(identity, objectsKeyPattern);
3180
- }
3181
- grantWrite(identity, objectsKeyPattern) {
3182
- return this.bucket.grantWrite(identity, objectsKeyPattern);
3183
- }
3184
- grantReplicationPermission(identity, props) {
3185
- return this.bucket.grantReplicationPermission(identity, props);
3186
- }
3187
- s3UrlForObject(key) {
3188
- return this.bucket.s3UrlForObject(key);
3189
- }
3190
- urlForObject(key) {
3191
- return this.bucket.urlForObject(key);
3192
- }
3193
- virtualHostedUrlForObject(key, options) {
3194
- return this.bucket.virtualHostedUrlForObject(key, options);
3195
- }
3196
- transferAccelerationUrlForObject(key) {
3197
- return this.bucket.transferAccelerationUrlForObject(key);
3198
- }
3199
- onCloudTrailEvent(id, options) {
3200
- return this.bucket.onCloudTrailEvent(id, options);
3201
- }
3202
- onCloudTrailPutObject(id, options) {
3203
- return this.bucket.onCloudTrailPutObject(id, options);
3204
- }
3205
- onCloudTrailWriteObject(id, options) {
3206
- return this.bucket.onCloudTrailWriteObject(id, options);
3207
- }
3208
- addCorsRule(rule) {
3209
- this.bucket.addCorsRule(rule);
3210
- }
3211
- addInventory(inventory) {
3212
- this.bucket.addInventory(inventory);
3213
- }
3214
- addLifecycleRule(rule) {
3215
- this.bucket.addLifecycleRule(rule);
3216
- }
3217
- addMetric(metric) {
3218
- this.bucket.addMetric(metric);
3219
- }
3220
- enableEventBridgeNotification() {
3221
- this.bucket.enableEventBridgeNotification();
3222
- }
3223
- addReplicationPolicy(policy) {
3224
- this.bucket.addReplicationPolicy(policy);
3225
- }
3226
- get stack() {
3227
- return this.bucket.stack;
3228
- }
3229
- get env() {
3230
- return this.bucket.env;
3231
- }
3232
- applyRemovalPolicy(policy) {
3233
- this.bucket.applyRemovalPolicy(policy);
3234
- }
3235
- get bucketRef() {
3236
- return {
3237
- bucketArn: this.bucket.bucketArn,
3238
- bucketName: this.bucket.bucketName,
3239
- };
3240
- }
3241
- }
3242
-
3243
- class JaypieStaticWebBucket extends JaypieWebDeploymentBucket {
3244
- constructor(scope, id, props = {}) {
3245
- // Handle overloaded signatures: (scope), (scope, props), (scope, id, props)
3246
- let resolvedId;
3247
- let resolvedProps;
3248
- if (typeof id === "string") {
3249
- resolvedId = id;
3250
- resolvedProps = props;
3251
- }
3252
- else if (typeof id === "object") {
3253
- resolvedId = "JaypieStaticWebBucket";
3254
- resolvedProps = id;
3255
- }
3256
- else {
3257
- resolvedId = "JaypieStaticWebBucket";
3258
- resolvedProps = props;
3259
- }
3260
- const host = resolvedProps.host ?? envHostname({ subdomain: "static" });
3261
- const name = resolvedProps.name ?? constructEnvName("static");
3262
- const roleTag = resolvedProps.roleTag ?? CDK$2.ROLE.HOSTING;
3263
- // Only use default zone if zone is not explicitly provided (including undefined)
3264
- const zone = "zone" in resolvedProps
3265
- ? resolvedProps.zone
3266
- : process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
3267
- super(scope, resolvedId, {
3268
- ...resolvedProps,
3269
- host,
3270
- name,
3271
- roleTag,
3272
- zone,
3273
- });
3274
- }
3275
- }
3276
-
3277
- class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
3278
- constructor(scope, id = "TraceSigningKey", props) {
3279
- const defaultProps = {
3280
- envKey: "TRACE_SIGNING_KEY",
3281
- roleTag: CDK$2.ROLE.API,
3282
- vendorTag: CDK$2.VENDOR.KNOWTRACE,
3283
- ...props,
3284
- };
3285
- super(scope, id, defaultProps);
3286
- }
3287
- }
3288
-
3289
- exports.CDK = CDK$2;
3290
- exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
3291
- exports.JaypieApiGateway = JaypieApiGateway;
3292
- exports.JaypieAppStack = JaypieAppStack;
3293
- exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
3294
- exports.JaypieDatadogBucket = JaypieDatadogBucket;
3295
- exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
3296
- exports.JaypieDatadogSecret = JaypieDatadogSecret;
3297
- exports.JaypieDistribution = JaypieDistribution;
3298
- exports.JaypieDnsRecord = JaypieDnsRecord;
3299
- exports.JaypieEnvSecret = JaypieEnvSecret;
3300
- exports.JaypieEventsRule = JaypieEventsRule;
3301
- exports.JaypieExpressLambda = JaypieExpressLambda;
3302
- exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
3303
- exports.JaypieHostedZone = JaypieHostedZone;
3304
- exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
3305
- exports.JaypieLambda = JaypieLambda;
3306
- exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
3307
- exports.JaypieNextJs = JaypieNextJs;
3308
- exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
3309
- exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
3310
- exports.JaypieQueuedLambda = JaypieQueuedLambda;
3311
- exports.JaypieSsoPermissions = JaypieSsoPermissions;
3312
- exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
3313
- exports.JaypieStack = JaypieStack;
3314
- exports.JaypieStaticWebBucket = JaypieStaticWebBucket;
3315
- exports.JaypieTraceSigningKeySecret = JaypieTraceSigningKeySecret;
3316
- exports.JaypieWebDeploymentBucket = JaypieWebDeploymentBucket;
3317
- exports.addDatadogLayers = addDatadogLayers;
3318
- exports.constructEnvName = constructEnvName;
3319
- exports.constructStackName = constructStackName;
3320
- exports.constructTagger = constructTagger;
3321
- exports.envHostname = envHostname;
3322
- exports.extendDatadogRole = extendDatadogRole;
3323
- exports.isEnv = isEnv;
3324
- exports.isProductionEnv = isProductionEnv;
3325
- exports.isSandboxEnv = isSandboxEnv;
3326
- exports.isValidHostname = isValidHostname$1;
3327
- exports.isValidSubdomain = isValidSubdomain;
3328
- exports.jaypieLambdaEnv = jaypieLambdaEnv;
3329
- exports.mergeDomain = mergeDomain;
3330
- exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
3331
- exports.resolveDatadogLayers = resolveDatadogLayers;
3332
- exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;
3333
- exports.resolveHostedZone = resolveHostedZone;
3334
- exports.resolveParamsAndSecrets = resolveParamsAndSecrets;
3335
- //# sourceMappingURL=index.cjs.map