@venturekit/infra 0.0.0-dev.20260514025219 → 0.0.0-dev.20260515022321
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/cdk/app-stack.d.ts +330 -0
- package/dist/cdk/app-stack.d.ts.map +1 -0
- package/dist/cdk/app-stack.js +875 -0
- package/dist/cdk/app-stack.js.map +1 -0
- package/dist/cdk/config-stack.d.ts +104 -0
- package/dist/cdk/config-stack.d.ts.map +1 -0
- package/dist/cdk/config-stack.js +161 -0
- package/dist/cdk/config-stack.js.map +1 -0
- package/dist/cdk/data-stack.d.ts +198 -0
- package/dist/cdk/data-stack.d.ts.map +1 -0
- package/dist/cdk/data-stack.js +511 -0
- package/dist/cdk/data-stack.js.map +1 -0
- package/dist/cdk/edge-stack.d.ts +91 -0
- package/dist/cdk/edge-stack.d.ts.map +1 -0
- package/dist/cdk/edge-stack.js +364 -0
- package/dist/cdk/edge-stack.js.map +1 -0
- package/dist/cdk/identity-stack.d.ts +98 -0
- package/dist/cdk/identity-stack.d.ts.map +1 -0
- package/dist/cdk/identity-stack.js +254 -0
- package/dist/cdk/identity-stack.js.map +1 -0
- package/dist/cdk/index.d.ts +16 -0
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +17 -0
- package/dist/cdk/index.js.map +1 -1
- package/dist/cdk/messaging-stack.d.ts +109 -0
- package/dist/cdk/messaging-stack.d.ts.map +1 -0
- package/dist/cdk/messaging-stack.js +320 -0
- package/dist/cdk/messaging-stack.js.map +1 -0
- package/dist/cdk/network-stack.d.ts +87 -0
- package/dist/cdk/network-stack.d.ts.map +1 -0
- package/dist/cdk/network-stack.js +265 -0
- package/dist/cdk/network-stack.js.map +1 -0
- package/dist/cdk/shared/cross-stack-refs.d.ts +278 -0
- package/dist/cdk/shared/cross-stack-refs.d.ts.map +1 -0
- package/dist/cdk/shared/cross-stack-refs.js +326 -0
- package/dist/cdk/shared/cross-stack-refs.js.map +1 -0
- package/dist/cdk/shared/lambda-helpers.d.ts +82 -0
- package/dist/cdk/shared/lambda-helpers.d.ts.map +1 -0
- package/dist/cdk/shared/lambda-helpers.js +201 -0
- package/dist/cdk/shared/lambda-helpers.js.map +1 -0
- package/dist/cdk/shared/ssm-keys.d.ts +460 -0
- package/dist/cdk/shared/ssm-keys.d.ts.map +1 -0
- package/dist/cdk/shared/ssm-keys.js +291 -0
- package/dist/cdk/shared/ssm-keys.js.map +1 -0
- package/dist/cdk/stack.d.ts +3 -11
- package/dist/cdk/stack.d.ts.map +1 -1
- package/dist/cdk/stack.js +184 -41
- package/dist/cdk/stack.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier 5 — Edge stack.
|
|
3
|
+
*
|
|
4
|
+
* Owns the public-facing entry surface:
|
|
5
|
+
*
|
|
6
|
+
* - The `AWS::ApiGatewayV2::Api` (HttpApi) shell with stable
|
|
7
|
+
* ID + access logging configured. Routes (which target Lambdas)
|
|
8
|
+
* are added in the app stack via `HttpApi.fromHttpApiAttributes`
|
|
9
|
+
* + `addRoutes()`.
|
|
10
|
+
* - Custom domain(s) per `domains[]` intent: ACM certificate
|
|
11
|
+
* (existing ARN, Route53-validated, or DNS-validation-pending),
|
|
12
|
+
* `AWS::ApiGatewayV2::DomainName`, `ApiMapping` to the API's
|
|
13
|
+
* default stage, optional Route53 alias.
|
|
14
|
+
* - CloudFront distribution(s) over imported S3 buckets per
|
|
15
|
+
* `storage[]` intent that has `cdn: true`. Bucket lives in the
|
|
16
|
+
* data stack; distribution + Origin Access Control + alias DNS
|
|
17
|
+
* live here. The OAC's `aws:SourceArn`-scoped bucket policy
|
|
18
|
+
* statement is added to the imported `IBucket` automatically by
|
|
19
|
+
* `S3BucketOrigin.withOriginAccessControl(...)`.
|
|
20
|
+
*
|
|
21
|
+
* # Lifecycle position
|
|
22
|
+
*
|
|
23
|
+
* Updated occasionally (~quarterly): a new domain, a new CDN, an
|
|
24
|
+
* ACM cert rotation. The HttpApi ID itself is the most fragile:
|
|
25
|
+
* the `*.execute-api.{region}.amazonaws.com` URL embeds it, and
|
|
26
|
+
* mobile apps shipped with that URL baked in cannot be patched
|
|
27
|
+
* without a full update cycle. RETAIN under strict is therefore
|
|
28
|
+
* not just durability — it's external-contract preservation.
|
|
29
|
+
*
|
|
30
|
+
* # Public surface (via SSM)
|
|
31
|
+
*
|
|
32
|
+
* - `edgeKeys.apiId / apiEndpoint / apiCustomDomain /
|
|
33
|
+
* apiDefaultStageName` — the app stack reads these to import
|
|
34
|
+
* the API and add its discovered routes.
|
|
35
|
+
* - `edgeKeys.storageCdnDomain(storageId) /
|
|
36
|
+
* storageCdnDistributionId(storageId)` per CDN-fronted bucket.
|
|
37
|
+
*
|
|
38
|
+
* # What this stack does NOT do
|
|
39
|
+
*
|
|
40
|
+
* - **No routes / integrations.** `HttpApi.addRoutes(...)` runs
|
|
41
|
+
* in the app stack. CDK's `HttpApi.fromHttpApiAttributes` returns
|
|
42
|
+
* an `IHttpApi` that supports `addRoutes`, so the cross-stack
|
|
43
|
+
* pattern is the same as the in-stack one.
|
|
44
|
+
*
|
|
45
|
+
* - **No Lambda permissions.** API Gateway invoking a Lambda
|
|
46
|
+
* requires a `lambda:InvokeFunction` resource policy on the
|
|
47
|
+
* Lambda granted to `apigateway.amazonaws.com` with `aws:SourceArn`
|
|
48
|
+
* scoping. CDK's `HttpLambdaIntegration` adds that grant
|
|
49
|
+
* automatically when the route is created — which happens in the
|
|
50
|
+
* app stack.
|
|
51
|
+
*
|
|
52
|
+
* - **No CloudFront-Lambda@Edge wiring.** Edge Lambdas (request /
|
|
53
|
+
* response viewer functions) are extreme-rare in the VentureKit
|
|
54
|
+
* surface. If they're added later, they'd live here because their
|
|
55
|
+
* lifecycle is tied to the distribution, not to a single regional
|
|
56
|
+
* handler.
|
|
57
|
+
*/
|
|
58
|
+
import * as cdk from 'aws-cdk-lib';
|
|
59
|
+
import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
|
|
60
|
+
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
|
|
61
|
+
import * as route53 from 'aws-cdk-lib/aws-route53';
|
|
62
|
+
import * as route53Targets from 'aws-cdk-lib/aws-route53-targets';
|
|
63
|
+
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
|
|
64
|
+
import * as cloudfrontOrigins from 'aws-cdk-lib/aws-cloudfront-origins';
|
|
65
|
+
import * as s3 from 'aws-cdk-lib/aws-s3';
|
|
66
|
+
import * as logs from 'aws-cdk-lib/aws-logs';
|
|
67
|
+
import { DATA_SAFETY_CONFIG } from '@venturekit/core';
|
|
68
|
+
import { writeSsm } from './shared/cross-stack-refs.js';
|
|
69
|
+
import { edgeKeys } from './shared/ssm-keys.js';
|
|
70
|
+
function toLogRetention(days) {
|
|
71
|
+
if (days <= 1)
|
|
72
|
+
return logs.RetentionDays.ONE_DAY;
|
|
73
|
+
if (days <= 7)
|
|
74
|
+
return logs.RetentionDays.ONE_WEEK;
|
|
75
|
+
if (days <= 14)
|
|
76
|
+
return logs.RetentionDays.TWO_WEEKS;
|
|
77
|
+
if (days <= 30)
|
|
78
|
+
return logs.RetentionDays.ONE_MONTH;
|
|
79
|
+
if (days <= 90)
|
|
80
|
+
return logs.RetentionDays.THREE_MONTHS;
|
|
81
|
+
if (days <= 180)
|
|
82
|
+
return logs.RetentionDays.SIX_MONTHS;
|
|
83
|
+
if (days <= 365)
|
|
84
|
+
return logs.RetentionDays.ONE_YEAR;
|
|
85
|
+
return logs.RetentionDays.INFINITE;
|
|
86
|
+
}
|
|
87
|
+
export class VentureEdgeStack extends cdk.Stack {
|
|
88
|
+
api;
|
|
89
|
+
distributions = new Map();
|
|
90
|
+
isStrictRetain;
|
|
91
|
+
constructor(scope, id, props) {
|
|
92
|
+
super(scope, id, props);
|
|
93
|
+
const { projectName, stage, envConfig, infrastructure, storageBuckets } = props;
|
|
94
|
+
this.isStrictRetain =
|
|
95
|
+
DATA_SAFETY_CONFIG[envConfig.dataSafety].removalPolicy === 'retain';
|
|
96
|
+
const apiConfig = envConfig.api;
|
|
97
|
+
const obsConfig = envConfig.observability;
|
|
98
|
+
// ─── HttpApi shell ──────────────────────────────────────────
|
|
99
|
+
// Created with NO routes — routes live in the app stack.
|
|
100
|
+
// CORS preflight, throttling, and access logging are all
|
|
101
|
+
// edge-tier concerns and stay here.
|
|
102
|
+
this.api = new apigatewayv2.HttpApi(this, 'Api', {
|
|
103
|
+
apiName: `${projectName}-${stage}`,
|
|
104
|
+
corsPreflight: apiConfig.cors
|
|
105
|
+
? {
|
|
106
|
+
allowOrigins: apiConfig.cors.allowOrigins,
|
|
107
|
+
allowMethods: apiConfig.cors.allowMethods.map((m) => apigatewayv2.CorsHttpMethod[m.toUpperCase()] ??
|
|
108
|
+
apigatewayv2.CorsHttpMethod.ANY),
|
|
109
|
+
allowHeaders: apiConfig.cors.allowHeaders,
|
|
110
|
+
allowCredentials: apiConfig.cors.allowCredentials,
|
|
111
|
+
maxAge: cdk.Duration.seconds(apiConfig.cors.maxAgeSec),
|
|
112
|
+
}
|
|
113
|
+
: {
|
|
114
|
+
allowOrigins: ['*'],
|
|
115
|
+
allowMethods: [
|
|
116
|
+
apigatewayv2.CorsHttpMethod.GET,
|
|
117
|
+
apigatewayv2.CorsHttpMethod.POST,
|
|
118
|
+
apigatewayv2.CorsHttpMethod.PUT,
|
|
119
|
+
apigatewayv2.CorsHttpMethod.DELETE,
|
|
120
|
+
apigatewayv2.CorsHttpMethod.PATCH,
|
|
121
|
+
apigatewayv2.CorsHttpMethod.OPTIONS,
|
|
122
|
+
],
|
|
123
|
+
allowHeaders: ['*'],
|
|
124
|
+
},
|
|
125
|
+
disableExecuteApiEndpoint: false,
|
|
126
|
+
});
|
|
127
|
+
if (this.isStrictRetain) {
|
|
128
|
+
// The `*.execute-api.{region}.amazonaws.com` URL embeds the
|
|
129
|
+
// API ID. Mobile apps shipped with that URL baked in cannot
|
|
130
|
+
// be patched without a full update cycle. RETAIN keeps the
|
|
131
|
+
// API ID stable across accidental stack deletes.
|
|
132
|
+
const cfnApi = this.api.node.defaultChild;
|
|
133
|
+
cfnApi.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
134
|
+
cfnApi.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
135
|
+
}
|
|
136
|
+
// ─── Access logging on the default stage ─────────────────────
|
|
137
|
+
const apiLogGroup = new logs.LogGroup(this, 'ApiAccessLog', {
|
|
138
|
+
logGroupName: `/venturekit/${projectName}/${stage}/api-access`,
|
|
139
|
+
retention: toLogRetention(obsConfig.logs.retentionDays),
|
|
140
|
+
removalPolicy: this.isStrictRetain
|
|
141
|
+
? cdk.RemovalPolicy.RETAIN
|
|
142
|
+
: cdk.RemovalPolicy.DESTROY,
|
|
143
|
+
});
|
|
144
|
+
const defaultStage = this.api.defaultStage?.node.defaultChild;
|
|
145
|
+
if (defaultStage) {
|
|
146
|
+
defaultStage.accessLogSettings = {
|
|
147
|
+
destinationArn: apiLogGroup.logGroupArn,
|
|
148
|
+
// Compact JSON access-log format. Operators ingest these
|
|
149
|
+
// straight into CloudWatch Insights or stream to S3.
|
|
150
|
+
format: JSON.stringify({
|
|
151
|
+
requestId: '$context.requestId',
|
|
152
|
+
ip: '$context.identity.sourceIp',
|
|
153
|
+
method: '$context.httpMethod',
|
|
154
|
+
path: '$context.path',
|
|
155
|
+
status: '$context.status',
|
|
156
|
+
latency: '$context.responseLatency',
|
|
157
|
+
userAgent: '$context.identity.userAgent',
|
|
158
|
+
}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
// ─── Public surface ─────────────────────────────────────────
|
|
162
|
+
writeSsm(this, edgeKeys.apiId(projectName, stage), this.api.apiId, {
|
|
163
|
+
description: `HttpApi ID — app stack imports via fromHttpApiAttributes.`,
|
|
164
|
+
});
|
|
165
|
+
writeSsm(this, edgeKeys.apiEndpoint(projectName, stage), this.api.apiEndpoint, { description: `HttpApi default endpoint URL.` });
|
|
166
|
+
writeSsm(this, edgeKeys.apiDefaultStageName(projectName, stage),
|
|
167
|
+
// HttpApi always uses `$default`. Publishing it explicitly
|
|
168
|
+
// means future named-stage support doesn't break consumers.
|
|
169
|
+
'$default', { description: `HttpApi default stage name.` });
|
|
170
|
+
// ─── Custom domains ─────────────────────────────────────────
|
|
171
|
+
if (infrastructure.domains) {
|
|
172
|
+
// VentureKit currently supports a single primary custom
|
|
173
|
+
// domain — multi-domain projects map them all onto the same
|
|
174
|
+
// API. We publish only the FIRST domain under
|
|
175
|
+
// `apiCustomDomain` (the canonical one app code reads).
|
|
176
|
+
let primaryDomain;
|
|
177
|
+
for (const intent of infrastructure.domains) {
|
|
178
|
+
this.createDomain(intent, projectName, stage);
|
|
179
|
+
if (!primaryDomain)
|
|
180
|
+
primaryDomain = intent.domain;
|
|
181
|
+
}
|
|
182
|
+
if (primaryDomain) {
|
|
183
|
+
writeSsm(this, edgeKeys.apiCustomDomain(projectName, stage), primaryDomain, {
|
|
184
|
+
description: `Primary custom domain. App code building OAuth redirect ` +
|
|
185
|
+
`URIs / webhook URLs should prefer this when set.`,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// ─── CloudFront over storage buckets ────────────────────────
|
|
190
|
+
if (infrastructure.storage) {
|
|
191
|
+
for (const intent of infrastructure.storage) {
|
|
192
|
+
if (intent.cdn !== true)
|
|
193
|
+
continue;
|
|
194
|
+
if (!storageBuckets) {
|
|
195
|
+
throw new Error(`[venturekit] edge stack: storage intent '${intent.id}' has cdn=true ` +
|
|
196
|
+
`but storageBuckets was not passed. The CLI generator ` +
|
|
197
|
+
`must wire dataStack.buckets.get('${intent.id}').bucket into ` +
|
|
198
|
+
`VentureEdgeStackProps.storageBuckets.`);
|
|
199
|
+
}
|
|
200
|
+
const bucket = storageBuckets.get(intent.id);
|
|
201
|
+
if (!bucket) {
|
|
202
|
+
throw new Error(`[venturekit] edge stack: storage intent '${intent.id}' has cdn=true ` +
|
|
203
|
+
`but no bucket was passed in storageBuckets. The CLI generator ` +
|
|
204
|
+
`must wire dataStack.buckets.get('${intent.id}').bucket into ` +
|
|
205
|
+
`VentureEdgeStackProps.storageBuckets.`);
|
|
206
|
+
}
|
|
207
|
+
this.createStorageCdn(intent, projectName, stage, bucket);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
createDomain(intent, projectName, stage) {
|
|
212
|
+
const resourceName = `${projectName}-${intent.id}`;
|
|
213
|
+
// Three certificate paths:
|
|
214
|
+
// 1. Operator pre-issued an ACM cert: pass the ARN.
|
|
215
|
+
// 2. We own the Route53 zone: auto-issue with DNS validation.
|
|
216
|
+
// 3. Neither: create the cert without DNS validation — it
|
|
217
|
+
// will sit in PENDING until the operator publishes the
|
|
218
|
+
// validation CNAMEs from the AWS console.
|
|
219
|
+
let certificate;
|
|
220
|
+
if (intent.certificateArn) {
|
|
221
|
+
certificate = acm.Certificate.fromCertificateArn(this, `${resourceName}-cert`, intent.certificateArn);
|
|
222
|
+
}
|
|
223
|
+
else if (intent.hostedZoneId) {
|
|
224
|
+
const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, `${resourceName}-zone`, {
|
|
225
|
+
hostedZoneId: intent.hostedZoneId,
|
|
226
|
+
// `intent.domain` may be `api.example.com` (apex zone is
|
|
227
|
+
// `example.com`). Take the trailing two labels for the zone
|
|
228
|
+
// name — Route53 doesn't accept a subdomain here.
|
|
229
|
+
zoneName: intent.domain.split('.').slice(-2).join('.'),
|
|
230
|
+
});
|
|
231
|
+
certificate = new acm.Certificate(this, `${resourceName}-cert`, {
|
|
232
|
+
domainName: intent.domain,
|
|
233
|
+
validation: acm.CertificateValidation.fromDns(hostedZone),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
certificate = new acm.Certificate(this, `${resourceName}-cert`, {
|
|
238
|
+
domainName: intent.domain,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const domainName = new apigatewayv2.DomainName(this, `${resourceName}-domain`, {
|
|
242
|
+
domainName: intent.domain,
|
|
243
|
+
certificate,
|
|
244
|
+
});
|
|
245
|
+
if (this.isStrictRetain) {
|
|
246
|
+
// The custom domain is the public contract with the world;
|
|
247
|
+
// deleting it forces DNS reconfiguration on every client /
|
|
248
|
+
// integration. RETAIN keeps it claimed to this AWS account
|
|
249
|
+
// for `cdk import` recovery.
|
|
250
|
+
const cfnDomain = domainName.node.defaultChild;
|
|
251
|
+
cfnDomain.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
252
|
+
cfnDomain.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
253
|
+
}
|
|
254
|
+
// API mapping — what binds the custom domain to the API's
|
|
255
|
+
// default stage. Mappings re-create cleanly so RETAIN here is
|
|
256
|
+
// just a recovery convenience.
|
|
257
|
+
const apiMapping = new apigatewayv2.ApiMapping(this, `${resourceName}-mapping`, {
|
|
258
|
+
api: this.api,
|
|
259
|
+
domainName,
|
|
260
|
+
stage: this.api.defaultStage,
|
|
261
|
+
});
|
|
262
|
+
if (this.isStrictRetain) {
|
|
263
|
+
const cfnMapping = apiMapping.node.defaultChild;
|
|
264
|
+
cfnMapping.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
265
|
+
cfnMapping.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
266
|
+
}
|
|
267
|
+
// Route53 alias when we own the zone.
|
|
268
|
+
if (intent.hostedZoneId) {
|
|
269
|
+
const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, `${resourceName}-zone-record`, {
|
|
270
|
+
hostedZoneId: intent.hostedZoneId,
|
|
271
|
+
zoneName: intent.domain.split('.').slice(-2).join('.'),
|
|
272
|
+
});
|
|
273
|
+
new route53.ARecord(this, `${resourceName}-a-record`, {
|
|
274
|
+
zone: hostedZone,
|
|
275
|
+
recordName: intent.domain,
|
|
276
|
+
target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayv2DomainProperties(domainName.regionalDomainName, domainName.regionalHostedZoneId)),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
createStorageCdn(intent, projectName, stage, bucket) {
|
|
281
|
+
const logicalId = `storage-${intent.id}-cdn`;
|
|
282
|
+
// Optional alternate domain (CNAME) + ACM cert. CloudFront only
|
|
283
|
+
// accepts certs from us-east-1 regardless of which region the
|
|
284
|
+
// rest of the stack runs in.
|
|
285
|
+
//
|
|
286
|
+
// vk cert request --domain cdn.example.com --region us-east-1
|
|
287
|
+
//
|
|
288
|
+
// Empty `cdnCertificateArn` is the same "pending" sentinel as
|
|
289
|
+
// `DomainIntent.certificateArn`: skip the alias and let the
|
|
290
|
+
// distribution serve at its `*.cloudfront.net` hostname until
|
|
291
|
+
// the next deploy.
|
|
292
|
+
const aliasReady = typeof intent.cdnDomain === 'string' &&
|
|
293
|
+
intent.cdnDomain.length > 0 &&
|
|
294
|
+
typeof intent.cdnCertificateArn === 'string' &&
|
|
295
|
+
intent.cdnCertificateArn.length > 0;
|
|
296
|
+
let aliasProps = {};
|
|
297
|
+
if (aliasReady) {
|
|
298
|
+
aliasProps = {
|
|
299
|
+
domainNames: [intent.cdnDomain],
|
|
300
|
+
certificate: acm.Certificate.fromCertificateArn(this, `${logicalId}-cert`, intent.cdnCertificateArn),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
// Re-import the bucket as a *purely-attribute-based* IBucket so
|
|
304
|
+
// CDK's `S3BucketOrigin.withOriginAccessControl` does NOT call
|
|
305
|
+
// `addToResourcePolicy` on the original bucket construct. The
|
|
306
|
+
// policy that authorizes CloudFront-OAC reads is created by the
|
|
307
|
+
// data stack (see `VentureDataStack.createStorage` — `intent.cdn`
|
|
308
|
+
// branch). If we let `withOriginAccessControl` plumb the policy
|
|
309
|
+
// automatically here, CDK would add both a `data → edge` (policy
|
|
310
|
+
// wants the distribution ARN) and an `edge → data` (origin wants
|
|
311
|
+
// the bucket regional domain) cross-stack dependency, producing
|
|
312
|
+
// a synth-time cycle.
|
|
313
|
+
const importedBucket = s3.Bucket.fromBucketAttributes(this, `${logicalId}-imported-bucket`, {
|
|
314
|
+
bucketName: bucket.bucketName,
|
|
315
|
+
bucketRegionalDomainName: bucket.bucketRegionalDomainName,
|
|
316
|
+
});
|
|
317
|
+
const distribution = new cloudfront.Distribution(this, logicalId, {
|
|
318
|
+
comment: `${projectName}-${intent.id}-${stage}`,
|
|
319
|
+
defaultBehavior: {
|
|
320
|
+
// OAC mode: CloudFront accesses S3 via the service principal
|
|
321
|
+
// with an `aws:SourceAccount`-scoped bucket policy pinned at
|
|
322
|
+
// the data tier. Anonymous direct-S3 GETs stay forbidden;
|
|
323
|
+
// only the CDN's signed CloudFront request can read the
|
|
324
|
+
// object.
|
|
325
|
+
origin: cloudfrontOrigins.S3BucketOrigin.withOriginAccessControl(importedBucket),
|
|
326
|
+
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
327
|
+
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
|
|
328
|
+
cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
|
|
329
|
+
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
|
|
330
|
+
compress: true,
|
|
331
|
+
},
|
|
332
|
+
// PriceClass 100 = US/Canada/Europe edges only. Cheaper than
|
|
333
|
+
// the global default for projects whose audience matches.
|
|
334
|
+
priceClass: cloudfront.PriceClass.PRICE_CLASS_100,
|
|
335
|
+
enabled: true,
|
|
336
|
+
...aliasProps,
|
|
337
|
+
});
|
|
338
|
+
if (this.isStrictRetain) {
|
|
339
|
+
// Distributions take 15-30 minutes to roll out globally;
|
|
340
|
+
// their `*.cloudfront.net` hostname is often cached by
|
|
341
|
+
// upstream consumers. RETAIN keeps the distribution in place
|
|
342
|
+
// for `cdk import` recovery.
|
|
343
|
+
const cfn = distribution.node.defaultChild;
|
|
344
|
+
cfn.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
345
|
+
cfn.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
|
|
346
|
+
}
|
|
347
|
+
this.distributions.set(intent.id, distribution);
|
|
348
|
+
// The published `cdn-domain` is either the alias (when
|
|
349
|
+
// configured + cert is ready) or the bare CloudFront hostname.
|
|
350
|
+
// Consumers building public URLs must read this rather than
|
|
351
|
+
// hardcoding either one.
|
|
352
|
+
const publishedDomain = aliasReady
|
|
353
|
+
? intent.cdnDomain
|
|
354
|
+
: distribution.distributionDomainName;
|
|
355
|
+
writeSsm(this, edgeKeys.storageCdnDomain(projectName, stage, intent.id), publishedDomain, {
|
|
356
|
+
description: `CDN domain for storage '${intent.id}' — public URL host.`,
|
|
357
|
+
});
|
|
358
|
+
writeSsm(this, edgeKeys.storageCdnDistributionId(projectName, stage, intent.id), distribution.distributionId, {
|
|
359
|
+
description: `CloudFront distribution ID — for CreateInvalidation calls ` +
|
|
360
|
+
`from app-stack handlers after content rewrites.`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=edge-stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-stack.js","sourceRoot":"","sources":["../../src/cdk/edge-stack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier 2 — Identity stack.
|
|
3
|
+
*
|
|
4
|
+
* Owns:
|
|
5
|
+
* - Cognito User Pool(s) — one per declared `auth` intent
|
|
6
|
+
* - App Client(s) per pool (`auth.clients[]`, defaulting to one
|
|
7
|
+
* `'default'` client when omitted)
|
|
8
|
+
* - Federated provider Secrets Manager placeholder(s) holding
|
|
9
|
+
* `{clientId, clientSecret}` for Google / Apple / Meta /
|
|
10
|
+
* other OAuth providers declared in `auth.federated[]`
|
|
11
|
+
*
|
|
12
|
+
* # Lifecycle position
|
|
13
|
+
*
|
|
14
|
+
* Updated rarely (~quarterly): MFA toggle, password-policy bump,
|
|
15
|
+
* new federated provider, occasional new app client. Survives stack
|
|
16
|
+
* delete because losing a User Pool means losing every user
|
|
17
|
+
* identity + password hash — by far the most catastrophic
|
|
18
|
+
* recoverable state in a VentureKit deploy. Both `DeletionPolicy:
|
|
19
|
+
* Retain` AND Cognito's native `deletionProtection` flag must be
|
|
20
|
+
* flipped before the pool can be deleted, and every flip is
|
|
21
|
+
* CloudTrail-logged.
|
|
22
|
+
*
|
|
23
|
+
* # Public surface (via SSM)
|
|
24
|
+
*
|
|
25
|
+
* - `identityKeys.userPoolId(authId)` — the Cognito pool ID
|
|
26
|
+
* - `identityKeys.userPoolArn(authId)` — for IAM grant scoping
|
|
27
|
+
* in the app stack
|
|
28
|
+
* - `identityKeys.appClientId(authId)` — first declared client
|
|
29
|
+
* (the canonical one app code reads)
|
|
30
|
+
* - `identityKeys.federatedSecretArn(authId, provider)` per
|
|
31
|
+
* provider in `auth.federated[]`
|
|
32
|
+
*
|
|
33
|
+
* # What this stack does NOT do
|
|
34
|
+
*
|
|
35
|
+
* - **No IAM grants.** Lambdas need
|
|
36
|
+
* `cognito-idp:AdminCreateUser` / `AdminInitiateAuth` /
|
|
37
|
+
* `AdminConfirmSignUp` etc. on the pool, and `secretsmanager:Get
|
|
38
|
+
* SecretValue` on each federated provider Secret. Those grants
|
|
39
|
+
* produce `AWS::IAM::Policy` resources attached to the
|
|
40
|
+
* shared-Lambda-role — which lives in the app stack. The app
|
|
41
|
+
* stack imports the pool ARN + secret ARNs via SSM and applies
|
|
42
|
+
* the grants there.
|
|
43
|
+
*
|
|
44
|
+
* - **No env-var injection.** The legacy single-stack code
|
|
45
|
+
* side-effected `this.ventureBaseEnv['COGNITO_USER_POOL_ID'] =
|
|
46
|
+
* userPool.userPoolId` to broadcast the pool ID into every
|
|
47
|
+
* Lambda's environment at synth time. Cross-stack, the app
|
|
48
|
+
* stack pulls the pool ID via `readSsmString` and injects it
|
|
49
|
+
* into Lambda env-vars itself — same broadcast, lock-free.
|
|
50
|
+
*
|
|
51
|
+
* - **No Cognito Hosted UI / IdP wiring.** VentureKit deliberately
|
|
52
|
+
* doesn't enable Cognito's Hosted UI or wire the federated
|
|
53
|
+
* providers as Cognito Identity Providers. Your app owns the
|
|
54
|
+
* login screen; the SPA does the OAuth dance with the IdP
|
|
55
|
+
* directly and posts the resulting tokens to your API.
|
|
56
|
+
* `signInAsFederatedUser` from `@venturekit/auth/server`
|
|
57
|
+
* verifies them and mints Cognito session tokens via
|
|
58
|
+
* `AdminInitiateAuth` — which is why the federated providers
|
|
59
|
+
* need `ALLOW_ADMIN_USER_PASSWORD_AUTH` enabled on the app
|
|
60
|
+
* client below.
|
|
61
|
+
*/
|
|
62
|
+
import * as cdk from 'aws-cdk-lib';
|
|
63
|
+
import * as cognito from 'aws-cdk-lib/aws-cognito';
|
|
64
|
+
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
|
65
|
+
import { Construct } from 'constructs';
|
|
66
|
+
import { type EnvConfig, type VentureIntent } from '@venturekit/core';
|
|
67
|
+
export interface VentureIdentityStackProps extends cdk.StackProps {
|
|
68
|
+
/** Project name — feeds resource names + SSM keys. */
|
|
69
|
+
projectName: string;
|
|
70
|
+
/** Stage. */
|
|
71
|
+
stage: string;
|
|
72
|
+
/** Fully-resolved environment config. */
|
|
73
|
+
envConfig: EnvConfig;
|
|
74
|
+
/**
|
|
75
|
+
* Infrastructure intent. Read for `auth[]`. All other slices are
|
|
76
|
+
* ignored.
|
|
77
|
+
*/
|
|
78
|
+
infrastructure: VentureIntent;
|
|
79
|
+
}
|
|
80
|
+
export declare class VentureIdentityStack extends cdk.Stack {
|
|
81
|
+
/**
|
|
82
|
+
* In-process accessors for sibling stacks (the app stack reads
|
|
83
|
+
* the pool reference for IAM grants when deployed in the same
|
|
84
|
+
* `cdk.App`). Keyed by `auth.id`.
|
|
85
|
+
*/
|
|
86
|
+
readonly userPools: Map<string, cognito.UserPool>;
|
|
87
|
+
readonly appClients: Map<string, cognito.UserPoolClient>;
|
|
88
|
+
/**
|
|
89
|
+
* Federated provider secrets, keyed by `${authId}:${provider}`.
|
|
90
|
+
* Order-stable for tests; the actual app stack consumer reads via
|
|
91
|
+
* SSM rather than this in-memory map.
|
|
92
|
+
*/
|
|
93
|
+
readonly federatedSecrets: Map<string, secretsmanager.Secret>;
|
|
94
|
+
private readonly isStrictRetain;
|
|
95
|
+
constructor(scope: Construct, id: string, props: VentureIdentityStackProps);
|
|
96
|
+
private createAuth;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=identity-stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-stack.d.ts","sourceRoot":"","sources":["../../src/cdk/identity-stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AACnC,OAAO,KAAK,OAAO,MAAM,yBAAyB,CAAC;AACnD,OAAO,KAAK,cAAc,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAI1F,MAAM,WAAW,yBAA0B,SAAQ,GAAG,CAAC,UAAU;IAC/D,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IAEpB,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IAEd,yCAAyC;IACzC,SAAS,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,cAAc,EAAE,aAAa,CAAC;CAC/B;AAED,qBAAa,oBAAqB,SAAQ,GAAG,CAAC,KAAK;IACjD;;;;OAIG;IACH,SAAgB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAa;IAErE,SAAgB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAa;IAE5E;;;;OAIG;IACH,SAAgB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAa;IAEjF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;gBAE7B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,yBAAyB;IAY1E,OAAO,CAAC,UAAU;CA8LnB"}
|