@jaypie/constructs 1.2.9 → 1.2.11
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 +117 -0
- 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 +376 -44
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/esm/JaypieCertificate.d.ts +117 -0
- 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 +372 -44
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import { Tags, Stack, Fn, CfnOutput, SecretValue, Duration, RemovalPolicy, CfnSt
|
|
|
3
3
|
import * as s3 from 'aws-cdk-lib/aws-s3';
|
|
4
4
|
import { Bucket, StorageClass, BucketAccessControl, EventType } from 'aws-cdk-lib/aws-s3';
|
|
5
5
|
import { Construct } from 'constructs';
|
|
6
|
-
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
|
|
7
6
|
import * as apiGateway from 'aws-cdk-lib/aws-apigateway';
|
|
8
7
|
import * as route53 from 'aws-cdk-lib/aws-route53';
|
|
9
8
|
import { TxtRecord, NsRecord, MxRecord, CnameRecord, ARecord, RecordTarget, HostedZone } from 'aws-cdk-lib/aws-route53';
|
|
@@ -12,6 +11,7 @@ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
|
|
12
11
|
import { DatadogLambda } from 'datadog-cdk-constructs-v2';
|
|
13
12
|
import { ConfigurationError } from '@jaypie/errors';
|
|
14
13
|
import { Role, PolicyStatement, Policy, FederatedPrincipal, Effect, ServicePrincipal, ManagedPolicy } from 'aws-cdk-lib/aws-iam';
|
|
14
|
+
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
|
|
15
15
|
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|
16
16
|
import * as logDestinations from 'aws-cdk-lib/aws-logs-destinations';
|
|
17
17
|
import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
|
|
@@ -444,6 +444,111 @@ function extendDatadogRole(scope, options) {
|
|
|
444
444
|
return datadogCustomPolicy;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
+
// Cache: Stack -> (domain -> certificate)
|
|
448
|
+
// Using WeakMap for automatic garbage collection when stacks are destroyed
|
|
449
|
+
const certificateCache = new WeakMap();
|
|
450
|
+
/**
|
|
451
|
+
* Resolves a certificate based on input type.
|
|
452
|
+
*
|
|
453
|
+
* Key behavior: When certificate is `true`, the certificate is created at the
|
|
454
|
+
* STACK level (not construct level) and cached by domain name. This allows
|
|
455
|
+
* swapping between constructs (e.g., JaypieDistribution to JaypieApiGateway)
|
|
456
|
+
* without recreating the certificate.
|
|
457
|
+
*
|
|
458
|
+
* @param scope - The construct scope (used to find the stack)
|
|
459
|
+
* @param options - Certificate resolution options
|
|
460
|
+
* @returns The resolved certificate, or undefined if certificate is false
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* // Create or get cached certificate at stack level
|
|
464
|
+
* const cert = resolveCertificate(this, {
|
|
465
|
+
* certificate: true,
|
|
466
|
+
* domainName: "api.example.com",
|
|
467
|
+
* zone: hostedZone,
|
|
468
|
+
* });
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* // Use existing certificate
|
|
472
|
+
* const cert = resolveCertificate(this, {
|
|
473
|
+
* certificate: existingCert,
|
|
474
|
+
* domainName: "api.example.com",
|
|
475
|
+
* zone: hostedZone,
|
|
476
|
+
* });
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* // Import certificate from ARN
|
|
480
|
+
* const cert = resolveCertificate(this, {
|
|
481
|
+
* certificate: "arn:aws:acm:us-east-1:123456789:certificate/abc-123",
|
|
482
|
+
* domainName: "api.example.com",
|
|
483
|
+
* zone: hostedZone,
|
|
484
|
+
* });
|
|
485
|
+
*/
|
|
486
|
+
function resolveCertificate(scope, options) {
|
|
487
|
+
const { certificate, domainName, name = "Certificate", roleTag = CDK$2.ROLE.API, zone, } = options;
|
|
488
|
+
// false = no certificate
|
|
489
|
+
if (certificate === false) {
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
// ICertificate passed directly - use as-is
|
|
493
|
+
if (typeof certificate === "object" && certificate !== null) {
|
|
494
|
+
return certificate;
|
|
495
|
+
}
|
|
496
|
+
// ARN string - import from ARN
|
|
497
|
+
if (typeof certificate === "string") {
|
|
498
|
+
// Sanitize domain for construct ID
|
|
499
|
+
const sanitizedDomain = sanitizeDomainForId(domainName);
|
|
500
|
+
return acm.Certificate.fromCertificateArn(scope, `${name}-${sanitizedDomain}`, certificate);
|
|
501
|
+
}
|
|
502
|
+
// true (default) = create at STACK level with caching
|
|
503
|
+
const stack = Stack.of(scope);
|
|
504
|
+
// Get or create cache for this stack
|
|
505
|
+
let stackCache = certificateCache.get(stack);
|
|
506
|
+
if (!stackCache) {
|
|
507
|
+
stackCache = new Map();
|
|
508
|
+
certificateCache.set(stack, stackCache);
|
|
509
|
+
}
|
|
510
|
+
// Return cached certificate if one exists for this domain
|
|
511
|
+
const cached = stackCache.get(domainName);
|
|
512
|
+
if (cached) {
|
|
513
|
+
return cached;
|
|
514
|
+
}
|
|
515
|
+
// Create certificate at STACK level (not construct level!)
|
|
516
|
+
// This is the key difference - the certificate's lifecycle is tied to the stack,
|
|
517
|
+
// not to the individual construct that requested it
|
|
518
|
+
const sanitizedDomain = sanitizeDomainForId(domainName);
|
|
519
|
+
const cert = new acm.Certificate(stack, `${name}-${sanitizedDomain}`, {
|
|
520
|
+
domainName,
|
|
521
|
+
validation: acm.CertificateValidation.fromDns(zone),
|
|
522
|
+
});
|
|
523
|
+
Tags.of(cert).add(CDK$2.TAG.ROLE, roleTag);
|
|
524
|
+
// Cache for future requests
|
|
525
|
+
stackCache.set(domainName, cert);
|
|
526
|
+
return cert;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Sanitizes a domain name for use in CDK construct IDs.
|
|
530
|
+
* CDK construct IDs can only contain alphanumeric characters and hyphens.
|
|
531
|
+
*/
|
|
532
|
+
function sanitizeDomainForId(domain) {
|
|
533
|
+
return domain.replace(/\./g, "-").replace(/[^a-zA-Z0-9-]/g, "");
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Clears the certificate cache for a specific stack.
|
|
537
|
+
* Primarily useful for testing.
|
|
538
|
+
*/
|
|
539
|
+
function clearCertificateCache(stack) {
|
|
540
|
+
certificateCache.delete(stack);
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Clears all certificate caches.
|
|
544
|
+
* Primarily useful for testing.
|
|
545
|
+
*/
|
|
546
|
+
function clearAllCertificateCaches() {
|
|
547
|
+
// WeakMap doesn't have a clear() method, so we create a new one
|
|
548
|
+
// This is a no-op since we can't actually clear a WeakMap,
|
|
549
|
+
// but stacks going out of scope will be garbage collected anyway
|
|
550
|
+
}
|
|
551
|
+
|
|
447
552
|
/**
|
|
448
553
|
* Check if the current environment matches the given environment
|
|
449
554
|
*/
|
|
@@ -773,34 +878,34 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
|
|
|
773
878
|
};
|
|
774
879
|
|
|
775
880
|
// It is a consumer if the environment is ephemeral
|
|
776
|
-
function checkEnvIsConsumer(env = process.env) {
|
|
881
|
+
function checkEnvIsConsumer$1(env = process.env) {
|
|
777
882
|
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
778
883
|
!!env.CDK_ENV_PERSONAL ||
|
|
779
884
|
/** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
|
|
780
885
|
/** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
|
|
781
886
|
}
|
|
782
|
-
function checkEnvIsProvider(env = process.env) {
|
|
887
|
+
function checkEnvIsProvider$1(env = process.env) {
|
|
783
888
|
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
784
889
|
}
|
|
785
|
-
function cleanName(name) {
|
|
890
|
+
function cleanName$1(name) {
|
|
786
891
|
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
787
892
|
}
|
|
788
|
-
function exportEnvName(name, env = process.env) {
|
|
893
|
+
function exportEnvName$1(name, env = process.env) {
|
|
789
894
|
let rawName;
|
|
790
|
-
if (checkEnvIsProvider(env)) {
|
|
895
|
+
if (checkEnvIsProvider$1(env)) {
|
|
791
896
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
792
897
|
// Clean the entire name to only allow alphanumeric, colons, and hyphens
|
|
793
|
-
return cleanName(rawName);
|
|
898
|
+
return cleanName$1(rawName);
|
|
794
899
|
}
|
|
795
900
|
else {
|
|
796
|
-
if (checkEnvIsConsumer(env)) {
|
|
901
|
+
if (checkEnvIsConsumer$1(env)) {
|
|
797
902
|
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
798
903
|
}
|
|
799
904
|
else {
|
|
800
905
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
801
906
|
}
|
|
802
907
|
}
|
|
803
|
-
return cleanName(rawName);
|
|
908
|
+
return cleanName$1(rawName);
|
|
804
909
|
}
|
|
805
910
|
class JaypieEnvSecret extends Construct {
|
|
806
911
|
constructor(scope, idOrEnvKey, props) {
|
|
@@ -812,15 +917,15 @@ class JaypieEnvSecret extends Construct {
|
|
|
812
917
|
process.env[idOrEnvKey] !== "";
|
|
813
918
|
const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
|
|
814
919
|
super(scope, id);
|
|
815
|
-
const { consumer = checkEnvIsConsumer(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider(), roleTag, vendorTag, value, } = props || {};
|
|
920
|
+
const { consumer = checkEnvIsConsumer$1(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), roleTag, vendorTag, value, } = props || {};
|
|
816
921
|
const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
|
|
817
922
|
this._envKey = envKey;
|
|
818
923
|
let exportName;
|
|
819
924
|
if (!exportParam) {
|
|
820
|
-
exportName = exportEnvName(id);
|
|
925
|
+
exportName = exportEnvName$1(id);
|
|
821
926
|
}
|
|
822
927
|
else {
|
|
823
|
-
exportName = cleanName(exportParam);
|
|
928
|
+
exportName = cleanName$1(exportParam);
|
|
824
929
|
}
|
|
825
930
|
if (consumer) {
|
|
826
931
|
const secretName = Fn.importValue(exportName);
|
|
@@ -1033,22 +1138,18 @@ class JaypieApiGateway extends Construct {
|
|
|
1033
1138
|
}
|
|
1034
1139
|
}
|
|
1035
1140
|
const apiGatewayName = name || constructEnvName("ApiGateway");
|
|
1036
|
-
const certificateName = constructEnvName("Certificate");
|
|
1037
1141
|
const apiDomainName = constructEnvName("ApiDomainName");
|
|
1038
1142
|
let hostedZone;
|
|
1039
1143
|
let certificateToUse;
|
|
1040
1144
|
if (host && zone) {
|
|
1041
1145
|
hostedZone = resolveHostedZone(this, { zone });
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
}
|
|
1049
|
-
else if (typeof certificate === "object") {
|
|
1050
|
-
certificateToUse = certificate;
|
|
1051
|
-
}
|
|
1146
|
+
// Use resolveCertificate to create certificate at stack level (enables reuse when swapping constructs)
|
|
1147
|
+
certificateToUse = resolveCertificate(this, {
|
|
1148
|
+
certificate,
|
|
1149
|
+
domainName: host,
|
|
1150
|
+
roleTag: CDK$2.ROLE.HOSTING,
|
|
1151
|
+
zone: hostedZone,
|
|
1152
|
+
});
|
|
1052
1153
|
this._certificate = certificateToUse;
|
|
1053
1154
|
this._host = host;
|
|
1054
1155
|
}
|
|
@@ -1808,6 +1909,238 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
1808
1909
|
}
|
|
1809
1910
|
}
|
|
1810
1911
|
|
|
1912
|
+
// Check if environment is a consumer (personal/ephemeral builds import from sandbox)
|
|
1913
|
+
function checkEnvIsConsumer(env = process.env) {
|
|
1914
|
+
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
1915
|
+
!!env.CDK_ENV_PERSONAL ||
|
|
1916
|
+
env.PROJECT_ENV === "ephemeral" ||
|
|
1917
|
+
!!env.CDK_ENV_EPHEMERAL);
|
|
1918
|
+
}
|
|
1919
|
+
// Check if environment is a provider (sandbox exports for consumers)
|
|
1920
|
+
function checkEnvIsProvider(env = process.env) {
|
|
1921
|
+
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
1922
|
+
}
|
|
1923
|
+
// Sanitize export name to only allow alphanumeric, colons, and hyphens
|
|
1924
|
+
function cleanName(name) {
|
|
1925
|
+
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
1926
|
+
}
|
|
1927
|
+
// Generate export name based on environment
|
|
1928
|
+
function exportEnvName(name, env = process.env) {
|
|
1929
|
+
const projectKey = env.PROJECT_KEY || "default";
|
|
1930
|
+
let rawName;
|
|
1931
|
+
if (checkEnvIsProvider(env)) {
|
|
1932
|
+
rawName = `env-${env.PROJECT_ENV}-${projectKey}-cert-${name}`;
|
|
1933
|
+
}
|
|
1934
|
+
else if (checkEnvIsConsumer(env)) {
|
|
1935
|
+
rawName = `env-${CDK$2.ENV.SANDBOX}-${projectKey}-cert-${name}`;
|
|
1936
|
+
}
|
|
1937
|
+
else {
|
|
1938
|
+
rawName = `env-${env.PROJECT_ENV || "default"}-${projectKey}-cert-${name}`;
|
|
1939
|
+
}
|
|
1940
|
+
return cleanName(rawName);
|
|
1941
|
+
}
|
|
1942
|
+
// Resolve domain name from props or environment (called before super)
|
|
1943
|
+
function resolveDomainNameFromProps(props) {
|
|
1944
|
+
if (props?.domainName) {
|
|
1945
|
+
return props.domainName;
|
|
1946
|
+
}
|
|
1947
|
+
if (process.env.CDK_ENV_API_HOST_NAME) {
|
|
1948
|
+
return process.env.CDK_ENV_API_HOST_NAME;
|
|
1949
|
+
}
|
|
1950
|
+
if (process.env.CDK_ENV_API_SUBDOMAIN) {
|
|
1951
|
+
return mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
|
|
1952
|
+
process.env.CDK_ENV_HOSTED_ZONE ||
|
|
1953
|
+
"");
|
|
1954
|
+
}
|
|
1955
|
+
return undefined;
|
|
1956
|
+
}
|
|
1957
|
+
// Sanitize domain for construct ID
|
|
1958
|
+
function sanitizeDomain(domain) {
|
|
1959
|
+
return domain.replace(/\./g, "-");
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* A standalone certificate construct that can be shared across constructs.
|
|
1963
|
+
*
|
|
1964
|
+
* Key feature: Uses the same `resolveCertificate()` helper as JaypieDistribution,
|
|
1965
|
+
* JaypieApiGateway, etc. This means:
|
|
1966
|
+
* - Certificates are created at the stack level and cached by domain
|
|
1967
|
+
* - You can "take over" a certificate from another construct by using the same domain
|
|
1968
|
+
* - Swapping between JaypieDistribution and JaypieApiGateway won't recreate certs
|
|
1969
|
+
*
|
|
1970
|
+
* Supports flexible constructor signatures:
|
|
1971
|
+
* - `new JaypieCertificate(scope)` - uses environment defaults
|
|
1972
|
+
* - `new JaypieCertificate(scope, props)` - ID auto-generated from domain
|
|
1973
|
+
* - `new JaypieCertificate(scope, id, props)` - explicit ID
|
|
1974
|
+
*
|
|
1975
|
+
* @example
|
|
1976
|
+
* // Minimal - uses environment variables for domain/zone
|
|
1977
|
+
* const cert = new JaypieCertificate(this);
|
|
1978
|
+
*
|
|
1979
|
+
* @example
|
|
1980
|
+
* // With options - ID auto-generated as "JaypieCert-api-example-com"
|
|
1981
|
+
* const cert = new JaypieCertificate(this, {
|
|
1982
|
+
* domainName: "api.example.com",
|
|
1983
|
+
* zone: "example.com",
|
|
1984
|
+
* });
|
|
1985
|
+
*
|
|
1986
|
+
* @example
|
|
1987
|
+
* // Explicit ID - useful when you need a specific construct ID
|
|
1988
|
+
* const cert = new JaypieCertificate(this, "MyApiCert", {
|
|
1989
|
+
* domainName: "api.example.com",
|
|
1990
|
+
* zone: "example.com",
|
|
1991
|
+
* });
|
|
1992
|
+
*
|
|
1993
|
+
* @example
|
|
1994
|
+
* // Take over from JaypieDistribution (uses same ID format)
|
|
1995
|
+
* // After removing JaypieDistribution with certificate: true
|
|
1996
|
+
* const cert = new JaypieCertificate(this, {
|
|
1997
|
+
* domainName: "api.example.com",
|
|
1998
|
+
* zone: "example.com",
|
|
1999
|
+
* });
|
|
2000
|
+
*
|
|
2001
|
+
* @example
|
|
2002
|
+
* // Optional: Provider/consumer pattern for cross-stack sharing
|
|
2003
|
+
* // In sandbox stack (explicitly export):
|
|
2004
|
+
* new JaypieCertificate(this, { provider: true });
|
|
2005
|
+
*
|
|
2006
|
+
* // In personal build (explicitly import):
|
|
2007
|
+
* new JaypieCertificate(this, { consumer: true });
|
|
2008
|
+
*/
|
|
2009
|
+
class JaypieCertificate extends Construct {
|
|
2010
|
+
constructor(scope, idOrProps, maybeProps) {
|
|
2011
|
+
// Resolve constructor arguments
|
|
2012
|
+
let id;
|
|
2013
|
+
let props;
|
|
2014
|
+
if (typeof idOrProps === "string") {
|
|
2015
|
+
// (scope, id, props) pattern
|
|
2016
|
+
id = idOrProps;
|
|
2017
|
+
props = maybeProps || {};
|
|
2018
|
+
}
|
|
2019
|
+
else if (typeof idOrProps === "object" && idOrProps !== null) {
|
|
2020
|
+
// (scope, props) pattern - auto-generate id
|
|
2021
|
+
props = idOrProps;
|
|
2022
|
+
const domainName = resolveDomainNameFromProps(props);
|
|
2023
|
+
if (domainName) {
|
|
2024
|
+
// Use "JaypieCert-" prefix to avoid collision with internal certificate
|
|
2025
|
+
id = props.id || `JaypieCert-${sanitizeDomain(domainName)}`;
|
|
2026
|
+
}
|
|
2027
|
+
else {
|
|
2028
|
+
id = props.id || "JaypieCert";
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
else {
|
|
2032
|
+
// (scope) pattern - no id, no props
|
|
2033
|
+
props = {};
|
|
2034
|
+
const domainName = resolveDomainNameFromProps(props);
|
|
2035
|
+
if (domainName) {
|
|
2036
|
+
id = `JaypieCert-${sanitizeDomain(domainName)}`;
|
|
2037
|
+
}
|
|
2038
|
+
else {
|
|
2039
|
+
id = "JaypieCert";
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
super(scope, id);
|
|
2043
|
+
const { consumer = false, domainName: propsDomainName, export: exportParam, provider = false, roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
|
|
2044
|
+
// Validate environment variables
|
|
2045
|
+
if (process.env.CDK_ENV_API_SUBDOMAIN &&
|
|
2046
|
+
!isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
|
|
2047
|
+
throw new Error("CDK_ENV_API_SUBDOMAIN is not a valid subdomain");
|
|
2048
|
+
}
|
|
2049
|
+
if (process.env.CDK_ENV_API_HOSTED_ZONE &&
|
|
2050
|
+
!isValidHostname$1(process.env.CDK_ENV_API_HOSTED_ZONE)) {
|
|
2051
|
+
throw new Error("CDK_ENV_API_HOSTED_ZONE is not a valid hostname");
|
|
2052
|
+
}
|
|
2053
|
+
if (process.env.CDK_ENV_HOSTED_ZONE &&
|
|
2054
|
+
!isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
|
|
2055
|
+
throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
|
|
2056
|
+
}
|
|
2057
|
+
// Determine domain name from props or environment
|
|
2058
|
+
let domainName = propsDomainName;
|
|
2059
|
+
if (!domainName) {
|
|
2060
|
+
if (process.env.CDK_ENV_API_HOST_NAME) {
|
|
2061
|
+
domainName = process.env.CDK_ENV_API_HOST_NAME;
|
|
2062
|
+
}
|
|
2063
|
+
else if (process.env.CDK_ENV_API_SUBDOMAIN) {
|
|
2064
|
+
domainName = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
|
|
2065
|
+
process.env.CDK_ENV_HOSTED_ZONE ||
|
|
2066
|
+
"");
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
if (!domainName) {
|
|
2070
|
+
throw new Error("domainName is required for JaypieCertificate (or set CDK_ENV_API_HOST_NAME / CDK_ENV_API_SUBDOMAIN)");
|
|
2071
|
+
}
|
|
2072
|
+
if (!isValidHostname$1(domainName)) {
|
|
2073
|
+
throw new Error("domainName is not a valid hostname");
|
|
2074
|
+
}
|
|
2075
|
+
this.domainName = domainName;
|
|
2076
|
+
// Determine zone from props or environment
|
|
2077
|
+
const zone = propsZone ||
|
|
2078
|
+
process.env.CDK_ENV_API_HOSTED_ZONE ||
|
|
2079
|
+
process.env.CDK_ENV_HOSTED_ZONE;
|
|
2080
|
+
// Generate export name
|
|
2081
|
+
const sanitizedDomain = domainName.replace(/\./g, "-");
|
|
2082
|
+
const exportName = exportParam
|
|
2083
|
+
? cleanName(exportParam)
|
|
2084
|
+
: exportEnvName(sanitizedDomain);
|
|
2085
|
+
if (consumer) {
|
|
2086
|
+
// Import certificate ARN from provider stack
|
|
2087
|
+
const certificateArn = Fn.importValue(exportName);
|
|
2088
|
+
this.certificate = acm.Certificate.fromCertificateArn(this, "ImportedCertificate", certificateArn);
|
|
2089
|
+
this.certificateArn = certificateArn;
|
|
2090
|
+
new CfnOutput(this, "ConsumedCertificateArn", {
|
|
2091
|
+
value: this.certificateArn,
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
else {
|
|
2095
|
+
// Create or get cached certificate at stack level
|
|
2096
|
+
if (!zone) {
|
|
2097
|
+
throw new Error("zone is required for JaypieCertificate when not consuming (or set CDK_ENV_API_HOSTED_ZONE / CDK_ENV_HOSTED_ZONE)");
|
|
2098
|
+
}
|
|
2099
|
+
const hostedZone = resolveHostedZone(this, { zone });
|
|
2100
|
+
// Use resolveCertificate to create at stack level (enables sharing)
|
|
2101
|
+
const cert = resolveCertificate(this, {
|
|
2102
|
+
certificate: true,
|
|
2103
|
+
domainName,
|
|
2104
|
+
roleTag,
|
|
2105
|
+
zone: hostedZone,
|
|
2106
|
+
});
|
|
2107
|
+
if (!cert) {
|
|
2108
|
+
throw new Error("Failed to create certificate");
|
|
2109
|
+
}
|
|
2110
|
+
this.certificate = cert;
|
|
2111
|
+
this.certificateArn = cert.certificateArn;
|
|
2112
|
+
if (provider) {
|
|
2113
|
+
new CfnOutput(this, "ProvidedCertificateArn", {
|
|
2114
|
+
value: this.certificateArn,
|
|
2115
|
+
exportName,
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
new CfnOutput(this, "CertificateArn", {
|
|
2120
|
+
value: this.certificateArn,
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
// IResource implementation
|
|
2126
|
+
get stack() {
|
|
2127
|
+
return Stack.of(this);
|
|
2128
|
+
}
|
|
2129
|
+
get env() {
|
|
2130
|
+
return {
|
|
2131
|
+
account: Stack.of(this).account,
|
|
2132
|
+
region: Stack.of(this).region,
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
applyRemovalPolicy(policy) {
|
|
2136
|
+
this.certificate.applyRemovalPolicy(policy);
|
|
2137
|
+
}
|
|
2138
|
+
// ICertificate implementation
|
|
2139
|
+
metricDaysToExpiry(props) {
|
|
2140
|
+
return this.certificate.metricDaysToExpiry(props);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
1811
2144
|
class JaypieDatadogBucket extends Construct {
|
|
1812
2145
|
/**
|
|
1813
2146
|
* Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
|
|
@@ -2085,16 +2418,13 @@ class JaypieDistribution extends Construct {
|
|
|
2085
2418
|
let certificateToUse;
|
|
2086
2419
|
if (host && zone && certificateProp !== false) {
|
|
2087
2420
|
hostedZone = resolveHostedZone(this, { zone });
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
}
|
|
2095
|
-
else if (typeof certificateProp === "object") {
|
|
2096
|
-
certificateToUse = certificateProp;
|
|
2097
|
-
}
|
|
2421
|
+
// Use resolveCertificate to create certificate at stack level (enables reuse when swapping constructs)
|
|
2422
|
+
certificateToUse = resolveCertificate(this, {
|
|
2423
|
+
certificate: certificateProp,
|
|
2424
|
+
domainName: host,
|
|
2425
|
+
roleTag,
|
|
2426
|
+
zone: hostedZone,
|
|
2427
|
+
});
|
|
2098
2428
|
this.certificate = certificateToUse;
|
|
2099
2429
|
}
|
|
2100
2430
|
// Create log bucket if logging is enabled
|
|
@@ -3581,19 +3911,17 @@ class JaypieWebDeploymentBucket extends Construct {
|
|
|
3581
3911
|
else {
|
|
3582
3912
|
hostedZone = zone;
|
|
3583
3913
|
}
|
|
3584
|
-
//
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
});
|
|
3914
|
+
// Use resolveCertificate to create certificate at stack level (enables reuse when swapping constructs)
|
|
3915
|
+
this.certificate = resolveCertificate(this, {
|
|
3916
|
+
certificate: props.certificate,
|
|
3917
|
+
domainName: host,
|
|
3918
|
+
roleTag,
|
|
3919
|
+
zone: hostedZone,
|
|
3920
|
+
});
|
|
3921
|
+
if (this.certificate) {
|
|
3593
3922
|
new CfnOutput(this, "CertificateArn", {
|
|
3594
3923
|
value: this.certificate.certificateArn,
|
|
3595
3924
|
});
|
|
3596
|
-
Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
|
|
3597
3925
|
}
|
|
3598
3926
|
// Create CloudFront distribution
|
|
3599
3927
|
this.distribution = new cloudfront.Distribution(this, "Distribution", {
|
|
@@ -3836,5 +4164,5 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
|
|
|
3836
4164
|
}
|
|
3837
4165
|
}
|
|
3838
4166
|
|
|
3839
|
-
export { CDK$2 as CDK, JaypieAccountLoggingBucket, JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieDatadogBucket, JaypieDatadogForwarder, JaypieDatadogSecret, JaypieDistribution, JaypieDnsRecord, JaypieDynamoDb, JaypieEnvSecret, JaypieEventsRule, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieNextJs, JaypieOpenAiSecret, JaypieOrganizationTrail, JaypieQueuedLambda, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieStaticWebBucket, JaypieStreamingLambda, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, LAMBDA_WEB_ADAPTER, addDatadogLayers, clearAllSecretsCaches, clearSecretsCache, constructEnvName, constructStackName, constructTagger, envHostname, extendDatadogRole, isEnv, isProductionEnv, isSandboxEnv, isValidHostname$1 as isValidHostname, isValidSubdomain, jaypieLambdaEnv, mergeDomain, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveEnvironment, resolveHostedZone, resolveParamsAndSecrets, resolveSecrets };
|
|
4167
|
+
export { CDK$2 as CDK, JaypieAccountLoggingBucket, JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieCertificate, JaypieDatadogBucket, JaypieDatadogForwarder, JaypieDatadogSecret, JaypieDistribution, JaypieDnsRecord, JaypieDynamoDb, JaypieEnvSecret, JaypieEventsRule, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieNextJs, JaypieOpenAiSecret, JaypieOrganizationTrail, JaypieQueuedLambda, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieStaticWebBucket, JaypieStreamingLambda, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, LAMBDA_WEB_ADAPTER, addDatadogLayers, clearAllCertificateCaches, clearAllSecretsCaches, clearCertificateCache, clearSecretsCache, constructEnvName, constructStackName, constructTagger, envHostname, extendDatadogRole, isEnv, isProductionEnv, isSandboxEnv, isValidHostname$1 as isValidHostname, isValidSubdomain, jaypieLambdaEnv, mergeDomain, resolveCertificate, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveEnvironment, resolveHostedZone, resolveParamsAndSecrets, resolveSecrets };
|
|
3840
4168
|
//# sourceMappingURL=index.js.map
|