@jaypie/constructs 1.2.8 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/JaypieCertificate.d.ts +116 -0
- package/dist/cjs/JaypieDistribution.d.ts +2 -2
- package/dist/cjs/__tests__/JaypieCertificate.spec.d.ts +1 -0
- package/dist/cjs/__tests__/resolveCertificate.spec.d.ts +1 -0
- package/dist/cjs/helpers/index.d.ts +1 -0
- package/dist/cjs/helpers/resolveCertificate.d.ts +63 -0
- package/dist/cjs/index.cjs +377 -45
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/esm/JaypieCertificate.d.ts +116 -0
- package/dist/esm/JaypieDistribution.d.ts +2 -2
- package/dist/esm/__tests__/JaypieCertificate.spec.d.ts +1 -0
- package/dist/esm/__tests__/resolveCertificate.spec.d.ts +1 -0
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/resolveCertificate.d.ts +63 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +373 -45
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
var cdk = require('aws-cdk-lib');
|
|
4
4
|
var s3 = require('aws-cdk-lib/aws-s3');
|
|
5
5
|
var constructs = require('constructs');
|
|
6
|
-
var acm = require('aws-cdk-lib/aws-certificatemanager');
|
|
7
6
|
var apiGateway = require('aws-cdk-lib/aws-apigateway');
|
|
8
7
|
var route53 = require('aws-cdk-lib/aws-route53');
|
|
9
8
|
var route53Targets = require('aws-cdk-lib/aws-route53-targets');
|
|
@@ -11,6 +10,7 @@ var secretsmanager = require('aws-cdk-lib/aws-secretsmanager');
|
|
|
11
10
|
var datadogCdkConstructsV2 = require('datadog-cdk-constructs-v2');
|
|
12
11
|
var errors = require('@jaypie/errors');
|
|
13
12
|
var awsIam = require('aws-cdk-lib/aws-iam');
|
|
13
|
+
var acm = require('aws-cdk-lib/aws-certificatemanager');
|
|
14
14
|
var lambda = require('aws-cdk-lib/aws-lambda');
|
|
15
15
|
var logDestinations = require('aws-cdk-lib/aws-logs-destinations');
|
|
16
16
|
var s3n = require('aws-cdk-lib/aws-s3-notifications');
|
|
@@ -47,11 +47,11 @@ function _interopNamespaceDefault(e) {
|
|
|
47
47
|
|
|
48
48
|
var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
|
|
49
49
|
var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
|
|
50
|
-
var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
|
|
51
50
|
var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
|
|
52
51
|
var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
|
|
53
52
|
var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Targets);
|
|
54
53
|
var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
|
|
54
|
+
var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
|
|
55
55
|
var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
|
|
56
56
|
var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
|
|
57
57
|
var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
|
|
@@ -135,7 +135,7 @@ const CDK$2 = {
|
|
|
135
135
|
},
|
|
136
136
|
DURATION: {
|
|
137
137
|
EXPRESS_API: 30,
|
|
138
|
-
CLOUDFRONT_API:
|
|
138
|
+
CLOUDFRONT_API: 120,
|
|
139
139
|
LAMBDA_MAXIMUM: 900,
|
|
140
140
|
LAMBDA_WORKER: 900,
|
|
141
141
|
},
|
|
@@ -476,6 +476,111 @@ function extendDatadogRole(scope, options) {
|
|
|
476
476
|
return datadogCustomPolicy;
|
|
477
477
|
}
|
|
478
478
|
|
|
479
|
+
// Cache: Stack -> (domain -> certificate)
|
|
480
|
+
// Using WeakMap for automatic garbage collection when stacks are destroyed
|
|
481
|
+
const certificateCache = new WeakMap();
|
|
482
|
+
/**
|
|
483
|
+
* Resolves a certificate based on input type.
|
|
484
|
+
*
|
|
485
|
+
* Key behavior: When certificate is `true`, the certificate is created at the
|
|
486
|
+
* STACK level (not construct level) and cached by domain name. This allows
|
|
487
|
+
* swapping between constructs (e.g., JaypieDistribution to JaypieApiGateway)
|
|
488
|
+
* without recreating the certificate.
|
|
489
|
+
*
|
|
490
|
+
* @param scope - The construct scope (used to find the stack)
|
|
491
|
+
* @param options - Certificate resolution options
|
|
492
|
+
* @returns The resolved certificate, or undefined if certificate is false
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* // Create or get cached certificate at stack level
|
|
496
|
+
* const cert = resolveCertificate(this, {
|
|
497
|
+
* certificate: true,
|
|
498
|
+
* domainName: "api.example.com",
|
|
499
|
+
* zone: hostedZone,
|
|
500
|
+
* });
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* // Use existing certificate
|
|
504
|
+
* const cert = resolveCertificate(this, {
|
|
505
|
+
* certificate: existingCert,
|
|
506
|
+
* domainName: "api.example.com",
|
|
507
|
+
* zone: hostedZone,
|
|
508
|
+
* });
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* // Import certificate from ARN
|
|
512
|
+
* const cert = resolveCertificate(this, {
|
|
513
|
+
* certificate: "arn:aws:acm:us-east-1:123456789:certificate/abc-123",
|
|
514
|
+
* domainName: "api.example.com",
|
|
515
|
+
* zone: hostedZone,
|
|
516
|
+
* });
|
|
517
|
+
*/
|
|
518
|
+
function resolveCertificate(scope, options) {
|
|
519
|
+
const { certificate, domainName, name = "Certificate", roleTag = CDK$2.ROLE.API, zone, } = options;
|
|
520
|
+
// false = no certificate
|
|
521
|
+
if (certificate === false) {
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
524
|
+
// ICertificate passed directly - use as-is
|
|
525
|
+
if (typeof certificate === "object" && certificate !== null) {
|
|
526
|
+
return certificate;
|
|
527
|
+
}
|
|
528
|
+
// ARN string - import from ARN
|
|
529
|
+
if (typeof certificate === "string") {
|
|
530
|
+
// Sanitize domain for construct ID
|
|
531
|
+
const sanitizedDomain = sanitizeDomainForId(domainName);
|
|
532
|
+
return acm__namespace.Certificate.fromCertificateArn(scope, `${name}-${sanitizedDomain}`, certificate);
|
|
533
|
+
}
|
|
534
|
+
// true (default) = create at STACK level with caching
|
|
535
|
+
const stack = cdk.Stack.of(scope);
|
|
536
|
+
// Get or create cache for this stack
|
|
537
|
+
let stackCache = certificateCache.get(stack);
|
|
538
|
+
if (!stackCache) {
|
|
539
|
+
stackCache = new Map();
|
|
540
|
+
certificateCache.set(stack, stackCache);
|
|
541
|
+
}
|
|
542
|
+
// Return cached certificate if one exists for this domain
|
|
543
|
+
const cached = stackCache.get(domainName);
|
|
544
|
+
if (cached) {
|
|
545
|
+
return cached;
|
|
546
|
+
}
|
|
547
|
+
// Create certificate at STACK level (not construct level!)
|
|
548
|
+
// This is the key difference - the certificate's lifecycle is tied to the stack,
|
|
549
|
+
// not to the individual construct that requested it
|
|
550
|
+
const sanitizedDomain = sanitizeDomainForId(domainName);
|
|
551
|
+
const cert = new acm__namespace.Certificate(stack, `${name}-${sanitizedDomain}`, {
|
|
552
|
+
domainName,
|
|
553
|
+
validation: acm__namespace.CertificateValidation.fromDns(zone),
|
|
554
|
+
});
|
|
555
|
+
cdk.Tags.of(cert).add(CDK$2.TAG.ROLE, roleTag);
|
|
556
|
+
// Cache for future requests
|
|
557
|
+
stackCache.set(domainName, cert);
|
|
558
|
+
return cert;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Sanitizes a domain name for use in CDK construct IDs.
|
|
562
|
+
* CDK construct IDs can only contain alphanumeric characters and hyphens.
|
|
563
|
+
*/
|
|
564
|
+
function sanitizeDomainForId(domain) {
|
|
565
|
+
return domain.replace(/\./g, "-").replace(/[^a-zA-Z0-9-]/g, "");
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Clears the certificate cache for a specific stack.
|
|
569
|
+
* Primarily useful for testing.
|
|
570
|
+
*/
|
|
571
|
+
function clearCertificateCache(stack) {
|
|
572
|
+
certificateCache.delete(stack);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Clears all certificate caches.
|
|
576
|
+
* Primarily useful for testing.
|
|
577
|
+
*/
|
|
578
|
+
function clearAllCertificateCaches() {
|
|
579
|
+
// WeakMap doesn't have a clear() method, so we create a new one
|
|
580
|
+
// This is a no-op since we can't actually clear a WeakMap,
|
|
581
|
+
// but stacks going out of scope will be garbage collected anyway
|
|
582
|
+
}
|
|
583
|
+
|
|
479
584
|
/**
|
|
480
585
|
* Check if the current environment matches the given environment
|
|
481
586
|
*/
|
|
@@ -805,34 +910,34 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
|
|
|
805
910
|
};
|
|
806
911
|
|
|
807
912
|
// It is a consumer if the environment is ephemeral
|
|
808
|
-
function checkEnvIsConsumer(env = process.env) {
|
|
913
|
+
function checkEnvIsConsumer$1(env = process.env) {
|
|
809
914
|
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
810
915
|
!!env.CDK_ENV_PERSONAL ||
|
|
811
916
|
/** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
|
|
812
917
|
/** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
|
|
813
918
|
}
|
|
814
|
-
function checkEnvIsProvider(env = process.env) {
|
|
919
|
+
function checkEnvIsProvider$1(env = process.env) {
|
|
815
920
|
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
816
921
|
}
|
|
817
|
-
function cleanName(name) {
|
|
922
|
+
function cleanName$1(name) {
|
|
818
923
|
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
819
924
|
}
|
|
820
|
-
function exportEnvName(name, env = process.env) {
|
|
925
|
+
function exportEnvName$1(name, env = process.env) {
|
|
821
926
|
let rawName;
|
|
822
|
-
if (checkEnvIsProvider(env)) {
|
|
927
|
+
if (checkEnvIsProvider$1(env)) {
|
|
823
928
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
824
929
|
// Clean the entire name to only allow alphanumeric, colons, and hyphens
|
|
825
|
-
return cleanName(rawName);
|
|
930
|
+
return cleanName$1(rawName);
|
|
826
931
|
}
|
|
827
932
|
else {
|
|
828
|
-
if (checkEnvIsConsumer(env)) {
|
|
933
|
+
if (checkEnvIsConsumer$1(env)) {
|
|
829
934
|
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
830
935
|
}
|
|
831
936
|
else {
|
|
832
937
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
833
938
|
}
|
|
834
939
|
}
|
|
835
|
-
return cleanName(rawName);
|
|
940
|
+
return cleanName$1(rawName);
|
|
836
941
|
}
|
|
837
942
|
class JaypieEnvSecret extends constructs.Construct {
|
|
838
943
|
constructor(scope, idOrEnvKey, props) {
|
|
@@ -844,15 +949,15 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
844
949
|
process.env[idOrEnvKey] !== "";
|
|
845
950
|
const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
|
|
846
951
|
super(scope, id);
|
|
847
|
-
const { consumer = checkEnvIsConsumer(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider(), roleTag, vendorTag, value, } = props || {};
|
|
952
|
+
const { consumer = checkEnvIsConsumer$1(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), roleTag, vendorTag, value, } = props || {};
|
|
848
953
|
const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
|
|
849
954
|
this._envKey = envKey;
|
|
850
955
|
let exportName;
|
|
851
956
|
if (!exportParam) {
|
|
852
|
-
exportName = exportEnvName(id);
|
|
957
|
+
exportName = exportEnvName$1(id);
|
|
853
958
|
}
|
|
854
959
|
else {
|
|
855
|
-
exportName = cleanName(exportParam);
|
|
960
|
+
exportName = cleanName$1(exportParam);
|
|
856
961
|
}
|
|
857
962
|
if (consumer) {
|
|
858
963
|
const secretName = cdk.Fn.importValue(exportName);
|
|
@@ -1065,22 +1170,18 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
1065
1170
|
}
|
|
1066
1171
|
}
|
|
1067
1172
|
const apiGatewayName = name || constructEnvName("ApiGateway");
|
|
1068
|
-
const certificateName = constructEnvName("Certificate");
|
|
1069
1173
|
const apiDomainName = constructEnvName("ApiDomainName");
|
|
1070
1174
|
let hostedZone;
|
|
1071
1175
|
let certificateToUse;
|
|
1072
1176
|
if (host && zone) {
|
|
1073
1177
|
hostedZone = resolveHostedZone(this, { zone });
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
else if (typeof certificate === "object") {
|
|
1082
|
-
certificateToUse = certificate;
|
|
1083
|
-
}
|
|
1178
|
+
// Use resolveCertificate to create certificate at stack level (enables reuse when swapping constructs)
|
|
1179
|
+
certificateToUse = resolveCertificate(this, {
|
|
1180
|
+
certificate,
|
|
1181
|
+
domainName: host,
|
|
1182
|
+
roleTag: CDK$2.ROLE.HOSTING,
|
|
1183
|
+
zone: hostedZone,
|
|
1184
|
+
});
|
|
1084
1185
|
this._certificate = certificateToUse;
|
|
1085
1186
|
this._host = host;
|
|
1086
1187
|
}
|
|
@@ -1840,6 +1941,238 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
1840
1941
|
}
|
|
1841
1942
|
}
|
|
1842
1943
|
|
|
1944
|
+
// Check if environment is a consumer (personal/ephemeral builds import from sandbox)
|
|
1945
|
+
function checkEnvIsConsumer(env = process.env) {
|
|
1946
|
+
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
1947
|
+
!!env.CDK_ENV_PERSONAL ||
|
|
1948
|
+
env.PROJECT_ENV === "ephemeral" ||
|
|
1949
|
+
!!env.CDK_ENV_EPHEMERAL);
|
|
1950
|
+
}
|
|
1951
|
+
// Check if environment is a provider (sandbox exports for consumers)
|
|
1952
|
+
function checkEnvIsProvider(env = process.env) {
|
|
1953
|
+
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
1954
|
+
}
|
|
1955
|
+
// Sanitize export name to only allow alphanumeric, colons, and hyphens
|
|
1956
|
+
function cleanName(name) {
|
|
1957
|
+
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
1958
|
+
}
|
|
1959
|
+
// Generate export name based on environment
|
|
1960
|
+
function exportEnvName(name, env = process.env) {
|
|
1961
|
+
const projectKey = env.PROJECT_KEY || "default";
|
|
1962
|
+
let rawName;
|
|
1963
|
+
if (checkEnvIsProvider(env)) {
|
|
1964
|
+
rawName = `env-${env.PROJECT_ENV}-${projectKey}-cert-${name}`;
|
|
1965
|
+
}
|
|
1966
|
+
else if (checkEnvIsConsumer(env)) {
|
|
1967
|
+
rawName = `env-${CDK$2.ENV.SANDBOX}-${projectKey}-cert-${name}`;
|
|
1968
|
+
}
|
|
1969
|
+
else {
|
|
1970
|
+
rawName = `env-${env.PROJECT_ENV || "default"}-${projectKey}-cert-${name}`;
|
|
1971
|
+
}
|
|
1972
|
+
return cleanName(rawName);
|
|
1973
|
+
}
|
|
1974
|
+
// Resolve domain name from props or environment (called before super)
|
|
1975
|
+
function resolveDomainNameFromProps(props) {
|
|
1976
|
+
if (props?.domainName) {
|
|
1977
|
+
return props.domainName;
|
|
1978
|
+
}
|
|
1979
|
+
if (process.env.CDK_ENV_API_HOST_NAME) {
|
|
1980
|
+
return process.env.CDK_ENV_API_HOST_NAME;
|
|
1981
|
+
}
|
|
1982
|
+
if (process.env.CDK_ENV_API_SUBDOMAIN) {
|
|
1983
|
+
return mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
|
|
1984
|
+
process.env.CDK_ENV_HOSTED_ZONE ||
|
|
1985
|
+
"");
|
|
1986
|
+
}
|
|
1987
|
+
return undefined;
|
|
1988
|
+
}
|
|
1989
|
+
// Sanitize domain for construct ID
|
|
1990
|
+
function sanitizeDomain(domain) {
|
|
1991
|
+
return domain.replace(/\./g, "-");
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* A standalone certificate construct that can be shared across constructs.
|
|
1995
|
+
*
|
|
1996
|
+
* Key feature: Uses the same `resolveCertificate()` helper as JaypieDistribution,
|
|
1997
|
+
* JaypieApiGateway, etc. This means:
|
|
1998
|
+
* - Certificates are created at the stack level and cached by domain
|
|
1999
|
+
* - You can "take over" a certificate from another construct by using the same domain
|
|
2000
|
+
* - Swapping between JaypieDistribution and JaypieApiGateway won't recreate certs
|
|
2001
|
+
*
|
|
2002
|
+
* Supports flexible constructor signatures:
|
|
2003
|
+
* - `new JaypieCertificate(scope)` - uses environment defaults
|
|
2004
|
+
* - `new JaypieCertificate(scope, props)` - ID auto-generated from domain
|
|
2005
|
+
* - `new JaypieCertificate(scope, id, props)` - explicit ID
|
|
2006
|
+
*
|
|
2007
|
+
* @example
|
|
2008
|
+
* // Minimal - uses environment variables for domain/zone
|
|
2009
|
+
* const cert = new JaypieCertificate(this);
|
|
2010
|
+
*
|
|
2011
|
+
* @example
|
|
2012
|
+
* // With options - ID auto-generated as "JaypieCert-api-example-com"
|
|
2013
|
+
* const cert = new JaypieCertificate(this, {
|
|
2014
|
+
* domainName: "api.example.com",
|
|
2015
|
+
* zone: "example.com",
|
|
2016
|
+
* });
|
|
2017
|
+
*
|
|
2018
|
+
* @example
|
|
2019
|
+
* // Explicit ID - useful when you need a specific construct ID
|
|
2020
|
+
* const cert = new JaypieCertificate(this, "MyApiCert", {
|
|
2021
|
+
* domainName: "api.example.com",
|
|
2022
|
+
* zone: "example.com",
|
|
2023
|
+
* });
|
|
2024
|
+
*
|
|
2025
|
+
* @example
|
|
2026
|
+
* // Take over from JaypieDistribution (uses same ID format)
|
|
2027
|
+
* // After removing JaypieDistribution with certificate: true
|
|
2028
|
+
* const cert = new JaypieCertificate(this, {
|
|
2029
|
+
* domainName: "api.example.com",
|
|
2030
|
+
* zone: "example.com",
|
|
2031
|
+
* });
|
|
2032
|
+
*
|
|
2033
|
+
* @example
|
|
2034
|
+
* // Provider/consumer pattern for cross-stack sharing
|
|
2035
|
+
* // In sandbox stack:
|
|
2036
|
+
* new JaypieCertificate(this, { provider: true });
|
|
2037
|
+
*
|
|
2038
|
+
* // In personal build:
|
|
2039
|
+
* new JaypieCertificate(this, { consumer: true });
|
|
2040
|
+
*/
|
|
2041
|
+
class JaypieCertificate extends constructs.Construct {
|
|
2042
|
+
constructor(scope, idOrProps, maybeProps) {
|
|
2043
|
+
// Resolve constructor arguments
|
|
2044
|
+
let id;
|
|
2045
|
+
let props;
|
|
2046
|
+
if (typeof idOrProps === "string") {
|
|
2047
|
+
// (scope, id, props) pattern
|
|
2048
|
+
id = idOrProps;
|
|
2049
|
+
props = maybeProps || {};
|
|
2050
|
+
}
|
|
2051
|
+
else if (typeof idOrProps === "object" && idOrProps !== null) {
|
|
2052
|
+
// (scope, props) pattern - auto-generate id
|
|
2053
|
+
props = idOrProps;
|
|
2054
|
+
const domainName = resolveDomainNameFromProps(props);
|
|
2055
|
+
if (domainName) {
|
|
2056
|
+
// Use "JaypieCert-" prefix to avoid collision with internal certificate
|
|
2057
|
+
id = props.id || `JaypieCert-${sanitizeDomain(domainName)}`;
|
|
2058
|
+
}
|
|
2059
|
+
else {
|
|
2060
|
+
id = props.id || "JaypieCert";
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
else {
|
|
2064
|
+
// (scope) pattern - no id, no props
|
|
2065
|
+
props = {};
|
|
2066
|
+
const domainName = resolveDomainNameFromProps(props);
|
|
2067
|
+
if (domainName) {
|
|
2068
|
+
id = `JaypieCert-${sanitizeDomain(domainName)}`;
|
|
2069
|
+
}
|
|
2070
|
+
else {
|
|
2071
|
+
id = "JaypieCert";
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
super(scope, id);
|
|
2075
|
+
const { consumer = checkEnvIsConsumer(), domainName: propsDomainName, export: exportParam, provider = checkEnvIsProvider(), roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
|
|
2076
|
+
// Validate environment variables
|
|
2077
|
+
if (process.env.CDK_ENV_API_SUBDOMAIN &&
|
|
2078
|
+
!isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
|
|
2079
|
+
throw new Error("CDK_ENV_API_SUBDOMAIN is not a valid subdomain");
|
|
2080
|
+
}
|
|
2081
|
+
if (process.env.CDK_ENV_API_HOSTED_ZONE &&
|
|
2082
|
+
!isValidHostname$1(process.env.CDK_ENV_API_HOSTED_ZONE)) {
|
|
2083
|
+
throw new Error("CDK_ENV_API_HOSTED_ZONE is not a valid hostname");
|
|
2084
|
+
}
|
|
2085
|
+
if (process.env.CDK_ENV_HOSTED_ZONE &&
|
|
2086
|
+
!isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
|
|
2087
|
+
throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
|
|
2088
|
+
}
|
|
2089
|
+
// Determine domain name from props or environment
|
|
2090
|
+
let domainName = propsDomainName;
|
|
2091
|
+
if (!domainName) {
|
|
2092
|
+
if (process.env.CDK_ENV_API_HOST_NAME) {
|
|
2093
|
+
domainName = process.env.CDK_ENV_API_HOST_NAME;
|
|
2094
|
+
}
|
|
2095
|
+
else if (process.env.CDK_ENV_API_SUBDOMAIN) {
|
|
2096
|
+
domainName = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
|
|
2097
|
+
process.env.CDK_ENV_HOSTED_ZONE ||
|
|
2098
|
+
"");
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
if (!domainName) {
|
|
2102
|
+
throw new Error("domainName is required for JaypieCertificate (or set CDK_ENV_API_HOST_NAME / CDK_ENV_API_SUBDOMAIN)");
|
|
2103
|
+
}
|
|
2104
|
+
if (!isValidHostname$1(domainName)) {
|
|
2105
|
+
throw new Error("domainName is not a valid hostname");
|
|
2106
|
+
}
|
|
2107
|
+
this.domainName = domainName;
|
|
2108
|
+
// Determine zone from props or environment
|
|
2109
|
+
const zone = propsZone ||
|
|
2110
|
+
process.env.CDK_ENV_API_HOSTED_ZONE ||
|
|
2111
|
+
process.env.CDK_ENV_HOSTED_ZONE;
|
|
2112
|
+
// Generate export name
|
|
2113
|
+
const sanitizedDomain = domainName.replace(/\./g, "-");
|
|
2114
|
+
const exportName = exportParam
|
|
2115
|
+
? cleanName(exportParam)
|
|
2116
|
+
: exportEnvName(sanitizedDomain);
|
|
2117
|
+
if (consumer) {
|
|
2118
|
+
// Import certificate ARN from provider stack
|
|
2119
|
+
const certificateArn = cdk.Fn.importValue(exportName);
|
|
2120
|
+
this.certificate = acm__namespace.Certificate.fromCertificateArn(this, "ImportedCertificate", certificateArn);
|
|
2121
|
+
this.certificateArn = certificateArn;
|
|
2122
|
+
new cdk.CfnOutput(this, "ConsumedCertificateArn", {
|
|
2123
|
+
value: this.certificateArn,
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
else {
|
|
2127
|
+
// Create or get cached certificate at stack level
|
|
2128
|
+
if (!zone) {
|
|
2129
|
+
throw new Error("zone is required for JaypieCertificate when not consuming (or set CDK_ENV_API_HOSTED_ZONE / CDK_ENV_HOSTED_ZONE)");
|
|
2130
|
+
}
|
|
2131
|
+
const hostedZone = resolveHostedZone(this, { zone });
|
|
2132
|
+
// Use resolveCertificate to create at stack level (enables sharing)
|
|
2133
|
+
const cert = resolveCertificate(this, {
|
|
2134
|
+
certificate: true,
|
|
2135
|
+
domainName,
|
|
2136
|
+
roleTag,
|
|
2137
|
+
zone: hostedZone,
|
|
2138
|
+
});
|
|
2139
|
+
if (!cert) {
|
|
2140
|
+
throw new Error("Failed to create certificate");
|
|
2141
|
+
}
|
|
2142
|
+
this.certificate = cert;
|
|
2143
|
+
this.certificateArn = cert.certificateArn;
|
|
2144
|
+
if (provider) {
|
|
2145
|
+
new cdk.CfnOutput(this, "ProvidedCertificateArn", {
|
|
2146
|
+
value: this.certificateArn,
|
|
2147
|
+
exportName,
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
else {
|
|
2151
|
+
new cdk.CfnOutput(this, "CertificateArn", {
|
|
2152
|
+
value: this.certificateArn,
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
// IResource implementation
|
|
2158
|
+
get stack() {
|
|
2159
|
+
return cdk.Stack.of(this);
|
|
2160
|
+
}
|
|
2161
|
+
get env() {
|
|
2162
|
+
return {
|
|
2163
|
+
account: cdk.Stack.of(this).account,
|
|
2164
|
+
region: cdk.Stack.of(this).region,
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
applyRemovalPolicy(policy) {
|
|
2168
|
+
this.certificate.applyRemovalPolicy(policy);
|
|
2169
|
+
}
|
|
2170
|
+
// ICertificate implementation
|
|
2171
|
+
metricDaysToExpiry(props) {
|
|
2172
|
+
return this.certificate.metricDaysToExpiry(props);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
|
|
1843
2176
|
class JaypieDatadogBucket extends constructs.Construct {
|
|
1844
2177
|
/**
|
|
1845
2178
|
* Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
|
|
@@ -2117,16 +2450,13 @@ class JaypieDistribution extends constructs.Construct {
|
|
|
2117
2450
|
let certificateToUse;
|
|
2118
2451
|
if (host && zone && certificateProp !== false) {
|
|
2119
2452
|
hostedZone = resolveHostedZone(this, { zone });
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
}
|
|
2127
|
-
else if (typeof certificateProp === "object") {
|
|
2128
|
-
certificateToUse = certificateProp;
|
|
2129
|
-
}
|
|
2453
|
+
// Use resolveCertificate to create certificate at stack level (enables reuse when swapping constructs)
|
|
2454
|
+
certificateToUse = resolveCertificate(this, {
|
|
2455
|
+
certificate: certificateProp,
|
|
2456
|
+
domainName: host,
|
|
2457
|
+
roleTag,
|
|
2458
|
+
zone: hostedZone,
|
|
2459
|
+
});
|
|
2130
2460
|
this.certificate = certificateToUse;
|
|
2131
2461
|
}
|
|
2132
2462
|
// Create log bucket if logging is enabled
|
|
@@ -3613,19 +3943,17 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
3613
3943
|
else {
|
|
3614
3944
|
hostedZone = zone;
|
|
3615
3945
|
}
|
|
3616
|
-
//
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
});
|
|
3946
|
+
// Use resolveCertificate to create certificate at stack level (enables reuse when swapping constructs)
|
|
3947
|
+
this.certificate = resolveCertificate(this, {
|
|
3948
|
+
certificate: props.certificate,
|
|
3949
|
+
domainName: host,
|
|
3950
|
+
roleTag,
|
|
3951
|
+
zone: hostedZone,
|
|
3952
|
+
});
|
|
3953
|
+
if (this.certificate) {
|
|
3625
3954
|
new cdk.CfnOutput(this, "CertificateArn", {
|
|
3626
3955
|
value: this.certificate.certificateArn,
|
|
3627
3956
|
});
|
|
3628
|
-
cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
|
|
3629
3957
|
}
|
|
3630
3958
|
// Create CloudFront distribution
|
|
3631
3959
|
this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
|
|
@@ -3873,6 +4201,7 @@ exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
|
|
|
3873
4201
|
exports.JaypieApiGateway = JaypieApiGateway;
|
|
3874
4202
|
exports.JaypieAppStack = JaypieAppStack;
|
|
3875
4203
|
exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
|
|
4204
|
+
exports.JaypieCertificate = JaypieCertificate;
|
|
3876
4205
|
exports.JaypieDatadogBucket = JaypieDatadogBucket;
|
|
3877
4206
|
exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
|
|
3878
4207
|
exports.JaypieDatadogSecret = JaypieDatadogSecret;
|
|
@@ -3900,7 +4229,9 @@ exports.JaypieTraceSigningKeySecret = JaypieTraceSigningKeySecret;
|
|
|
3900
4229
|
exports.JaypieWebDeploymentBucket = JaypieWebDeploymentBucket;
|
|
3901
4230
|
exports.LAMBDA_WEB_ADAPTER = LAMBDA_WEB_ADAPTER;
|
|
3902
4231
|
exports.addDatadogLayers = addDatadogLayers;
|
|
4232
|
+
exports.clearAllCertificateCaches = clearAllCertificateCaches;
|
|
3903
4233
|
exports.clearAllSecretsCaches = clearAllSecretsCaches;
|
|
4234
|
+
exports.clearCertificateCache = clearCertificateCache;
|
|
3904
4235
|
exports.clearSecretsCache = clearSecretsCache;
|
|
3905
4236
|
exports.constructEnvName = constructEnvName;
|
|
3906
4237
|
exports.constructStackName = constructStackName;
|
|
@@ -3914,6 +4245,7 @@ exports.isValidHostname = isValidHostname$1;
|
|
|
3914
4245
|
exports.isValidSubdomain = isValidSubdomain;
|
|
3915
4246
|
exports.jaypieLambdaEnv = jaypieLambdaEnv;
|
|
3916
4247
|
exports.mergeDomain = mergeDomain;
|
|
4248
|
+
exports.resolveCertificate = resolveCertificate;
|
|
3917
4249
|
exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
|
|
3918
4250
|
exports.resolveDatadogLayers = resolveDatadogLayers;
|
|
3919
4251
|
exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;
|