canopycms-cdk 0.0.67-int.91 → 0.0.68-int.100
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/README.md +3 -1
- package/dist/constructs/asset-support.d.ts +80 -81
- package/dist/constructs/asset-support.js +173 -195
- package/dist/constructs/cms-distribution.d.ts +7 -7
- package/dist/constructs/cms-distribution.js +19 -67
- package/dist/constructs/cms-service.d.ts +55 -8
- package/dist/constructs/cms-service.js +67 -15
- package/dist/constructs/editor-routing.d.ts +131 -0
- package/dist/constructs/editor-routing.js +313 -0
- package/dist/index.d.ts +1 -0
- package/lambda/asset-transform/dist/handler.js +1021 -459
- package/package.json +4 -4
- package/worker/dist/index.js +1689 -792
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ AWS CDK constructs and EC2 worker for deploying CanopyCMS.
|
|
|
14
14
|
- Lambda Function URL (for CloudFront origin)
|
|
15
15
|
- EC2 Worker (t4g.nano spot in ASG, public subnet, EFS mount)
|
|
16
16
|
- Security groups and IAM roles (least-privilege)
|
|
17
|
+
- `attachTo(distribution)`: adds the editor's `/edit`, `/edit/*` and `/api/canopycms/*` behaviors (and, optionally, the CMS build's `assetPrefix` and the preview route) to a CloudFront distribution you already own
|
|
17
18
|
|
|
18
19
|
**`CanopyCmsDistribution`** — CloudFront + DNS (optional):
|
|
19
20
|
|
|
@@ -21,8 +22,9 @@ AWS CDK constructs and EC2 worker for deploying CanopyCMS.
|
|
|
21
22
|
- CloudFront distribution with Lambda Function URL origin
|
|
22
23
|
- Route53 A/AAAA alias records
|
|
23
24
|
- Cache policies: no-cache for API/editor, long-cache for static assets
|
|
25
|
+
- Response headers: framing protection (`frame-ancestors 'self'`), `nosniff`, HSTS, `X-Robots-Tag: noindex`
|
|
24
26
|
|
|
25
|
-
Use `CanopyCmsDistribution`
|
|
27
|
+
Use `CanopyCmsDistribution` when the CMS gets its own domain. To serve the editor from an existing distribution, call `cmsService.attachTo(distribution)` instead of wiring the `functionUrl` by hand; see [Serving the editor from a distribution you already own](../../docs/deploying-to-aws.md#serving-the-editor-from-a-distribution-you-already-own).
|
|
26
28
|
|
|
27
29
|
### EC2 Worker
|
|
28
30
|
|
|
@@ -128,50 +128,68 @@ export interface AssetSupportProps {
|
|
|
128
128
|
*/
|
|
129
129
|
readonly autoDeleteObjects?: boolean;
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
131
|
+
* A replica of the bucket's `assets/` prefix. Both public behaviors fail over
|
|
132
|
+
* to it on 500, 502, 503 or 504 from the primary, never on a miss: the
|
|
133
|
+
* replica only holds what the primary has. With `lazyPublicTransforms`,
|
|
134
|
+
* `/assets/t/*`'s one fallback slot is the transform Lambda, so only
|
|
135
|
+
* `/assets/*` gets the replica.
|
|
135
136
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
137
|
+
* It is read through its own OAC, whose grant CDK writes only on a bucket it
|
|
138
|
+
* owns: an imported replica's policy must allow this distribution itself.
|
|
139
|
+
* Import one in another region with `Bucket.fromBucketAttributes({ bucketName,
|
|
140
|
+
* region })` so its regional domain name is right. Replication must cover
|
|
141
|
+
* `assets/`; `materialize-assets` writes only the primary.
|
|
141
142
|
*
|
|
142
|
-
*
|
|
143
|
-
|
|
143
|
+
* @default - no failover
|
|
144
|
+
*/
|
|
145
|
+
readonly replicaBucket?: s3.IBucket;
|
|
146
|
+
/**
|
|
147
|
+
* Compute a missing `/assets/t/*` derivative on request with a transform
|
|
148
|
+
* Lambda, instead of serving only what `canopycms materialize-assets` wrote.
|
|
149
|
+
* Off, the public path computes nothing and an unmaterialized URL is a miss,
|
|
150
|
+
* which is also what a live preview on another origin than the editor shows
|
|
151
|
+
* for a draft's new crop.
|
|
144
152
|
*
|
|
145
|
-
*
|
|
153
|
+
* On, anyone can mint any allowlisted transform of a public asset - bounded
|
|
154
|
+
* per asset except crop, at ~10^16 rects - capped by reserved concurrency and
|
|
155
|
+
* an `assets/t/` expiry that applies only to objects the Lambda writes, which
|
|
156
|
+
* carry the tag `canopy-transform=lazy`: what `materialize-assets` writes is
|
|
157
|
+
* never expired. On a BYO `bucket` this construct cannot write that rule, so
|
|
158
|
+
* lazy mode there requires an explicit `transformOutputRetention`.
|
|
159
|
+
*
|
|
160
|
+
* @default false
|
|
146
161
|
*/
|
|
147
|
-
readonly
|
|
162
|
+
readonly lazyPublicTransforms?: boolean;
|
|
148
163
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
164
|
+
* Require the transform Lambda's code asset to carry the `.deployable`
|
|
165
|
+
* marker a full `build:lambda` writes, proving it has its native sharp
|
|
166
|
+
* binary. Checked only with `lazyPublicTransforms`. Set `false` ONLY in this
|
|
167
|
+
* package's own tests, which synth against the `--skip-native` fixture.
|
|
168
|
+
*
|
|
169
|
+
* @default true
|
|
151
170
|
*/
|
|
171
|
+
readonly requireDeployableBundle?: boolean;
|
|
172
|
+
/** Lazy mode only. Retention for the transform Lambda's log group (default 90 days). */
|
|
152
173
|
readonly transformLogRetention?: logs.RetentionDays;
|
|
153
174
|
/**
|
|
154
|
-
* Concurrency cap on the transform Lambda (default
|
|
155
|
-
*
|
|
156
|
-
* This is a reservation — a CAP carved from the account's concurrency pool,
|
|
157
|
-
* not pre-warmed capacity, so it costs nothing when idle. It bounds the
|
|
158
|
-
* blast radius of the anonymous `/assets/t/*` path; see the default
|
|
159
|
-
* constant's doc comment. Raise it for an unusually image-heavy site;
|
|
160
|
-
* setting it to 0 would disable transforms entirely.
|
|
175
|
+
* Lazy mode only. Concurrency cap on the transform Lambda (default 10), a
|
|
176
|
+
* reservation that costs nothing idle. 0 disables transforms.
|
|
161
177
|
*/
|
|
162
178
|
readonly transformReservedConcurrency?: number;
|
|
163
179
|
/**
|
|
164
|
-
* How long
|
|
165
|
-
* days)
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
180
|
+
* Lazy mode only. How long the Lambda's `assets/t/` outputs are kept
|
|
181
|
+
* (default 180 days), written as a lifecycle rule on a bucket this construct
|
|
182
|
+
* creates. On a BYO `bucket` nothing is written and the prop is required:
|
|
183
|
+
* passing it states that your bucket expires them itself, filtering on the
|
|
184
|
+
* tag `canopy-transform=lazy` so what `materialize-assets` writes survives,
|
|
185
|
+
* and on a versioned bucket also expiring noncurrent versions. Remove that
|
|
186
|
+
* rule before leaving lazy mode: a key the Lambda wrote first stays tagged
|
|
187
|
+
* after a build references it. A cross-account bucket's policy must also
|
|
188
|
+
* grant the transform role `s3:PutObjectTagging` on `assets/t/*`.
|
|
171
189
|
*/
|
|
172
190
|
readonly transformOutputRetention?: Duration;
|
|
173
191
|
/**
|
|
174
|
-
* Execution role for the transform Lambda (default: CDK creates one).
|
|
192
|
+
* Lazy mode only. Execution role for the transform Lambda (default: CDK creates one).
|
|
175
193
|
*
|
|
176
194
|
* Set this when the role's ARN has to be computable WITHOUT a reference to
|
|
177
195
|
* this construct - the motivating case is an asset bucket in a different AWS
|
|
@@ -192,11 +210,10 @@ export interface AssetSupportProps {
|
|
|
192
210
|
*/
|
|
193
211
|
readonly transformRole?: iam.Role;
|
|
194
212
|
/**
|
|
195
|
-
* Name for the transform Lambda's CloudWatch log group (default:
|
|
213
|
+
* Lazy mode only. Name for the transform Lambda's CloudWatch log group (default:
|
|
196
214
|
* `/canopycms/<stackName>/transform`). Deliberately NOT
|
|
197
|
-
* `/aws/lambda/<function-name>` - see
|
|
198
|
-
*
|
|
199
|
-
* the function has ever been deployed without one. Override to follow an
|
|
215
|
+
* `/aws/lambda/<function-name>` - see the log group's comment in
|
|
216
|
+
* `buildTransformLambda` for why. Override to follow an
|
|
200
217
|
* org naming convention, or when instantiating this construct twice in one
|
|
201
218
|
* stack (the default name would collide).
|
|
202
219
|
*/
|
|
@@ -249,10 +266,8 @@ export declare const ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS: readonly ["assets", "as
|
|
|
249
266
|
* ```ts
|
|
250
267
|
* const behaviors = assetSupport.assetBehaviors()
|
|
251
268
|
*
|
|
252
|
-
* // CloudFront
|
|
253
|
-
* //
|
|
254
|
-
* // '/assets/*' - otherwise the broader S3-only pattern swallows transform
|
|
255
|
-
* // requests and they 403 with no Lambda fallback.
|
|
269
|
+
* // CloudFront stops at the first matching pattern, so '/assets/t/*' MUST
|
|
270
|
+
* // come before '/assets/*', or lazy mode's misses never reach the Lambda.
|
|
256
271
|
* new cloudfront.Distribution(this, 'Dist', {
|
|
257
272
|
* defaultBehavior: ...,
|
|
258
273
|
* additionalBehaviors: {
|
|
@@ -264,18 +279,13 @@ export declare const ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS: readonly ["assets", "as
|
|
|
264
279
|
*/
|
|
265
280
|
export interface AssetCloudFrontBehaviors {
|
|
266
281
|
/**
|
|
267
|
-
* `/assets
|
|
268
|
-
* pipeline wrote, plus already-computed transform outputs under
|
|
269
|
-
* `assets/t/...`, which also live under this prefix). S3 origin only -
|
|
270
|
-
* nothing here is ever computed on demand.
|
|
282
|
+
* `/assets/*`: the S3 read origin (an origin group with `replicaBucket`).
|
|
271
283
|
*/
|
|
272
284
|
readonly assets: cloudfront.BehaviorOptions;
|
|
273
285
|
/**
|
|
274
|
-
* `/assets/t
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
* configuring both is defense-in-depth). CloudFront caches the failover
|
|
278
|
-
* response.
|
|
286
|
+
* `/assets/t/*`: the same read origin as `assets`, or with
|
|
287
|
+
* `lazyPublicTransforms` an origin group falling over to the transform
|
|
288
|
+
* Lambda on 403 or 404.
|
|
279
289
|
*/
|
|
280
290
|
readonly assetsTransform: cloudfront.BehaviorOptions;
|
|
281
291
|
}
|
|
@@ -312,15 +322,8 @@ export interface AssetUploadBehaviorRouteOptions extends AssetUploadBehaviorOpti
|
|
|
312
322
|
* ```
|
|
313
323
|
*
|
|
314
324
|
* Separate from `AssetSupport.uploadBehavior()` because the upload route needs
|
|
315
|
-
* the bucket and nothing else
|
|
316
|
-
*
|
|
317
|
-
* Those grants never reach the BUCKET policy: `Grant.addToPrincipalOrResource`
|
|
318
|
-
* stops after the identity statement for a same-account grantee, and
|
|
319
|
-
* `addToResourcePolicy` on a bucket CDK does not own is a no-op - an owned
|
|
320
|
-
* bucket, one imported by name, and one imported by ARN in another account all
|
|
321
|
-
* emit zero `AWS::S3::BucketPolicy`, so a cross-account adopter writes the
|
|
322
|
-
* resource half on the bucket's own side. Prefer the method when you already
|
|
323
|
-
* have an `AssetSupport`; both funnel into the same builder and cannot drift.
|
|
325
|
+
* the bucket and nothing else. Prefer the method when you already have an
|
|
326
|
+
* `AssetSupport`; both funnel into the same builder and cannot drift.
|
|
324
327
|
*
|
|
325
328
|
* No bucket CORS rule is written and none is needed: the edge supplies
|
|
326
329
|
* `Access-Control-Allow-Origin`, and S3's ACCEPTANCE of a presigned POST is
|
|
@@ -336,12 +339,12 @@ export declare function assetUploadBehavior(scope: Construct, options: AssetUplo
|
|
|
336
339
|
* Wires:
|
|
337
340
|
*
|
|
338
341
|
* - The bucket's asset-prefix lifecycle rule + CORS (standalone mode only).
|
|
339
|
-
* - The
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* OAC-locked Function URL
|
|
342
|
+
* - The public read path: `/assets/*` and `/assets/t/*` served from S3 (plus
|
|
343
|
+
* `replicaBucket` on 5xx). It computes nothing; derivatives come from
|
|
344
|
+
* `canopycms materialize-assets`.
|
|
345
|
+
* - With `lazyPublicTransforms`, the transform Lambda
|
|
346
|
+
* (`../../lambda/asset-transform/handler.ts`, built by `build:lambda`), its
|
|
347
|
+
* log group and its OAC-locked Function URL, behind `/assets/t/*`.
|
|
345
348
|
* - `attachTo(distribution)`, which attaches the two CloudFront behaviors a
|
|
346
349
|
* consuming distribution needs in the only safe order (see its doc comment
|
|
347
350
|
* for why the order is the whole point); `assetBehaviors()` is the escape
|
|
@@ -356,11 +359,12 @@ export declare function assetUploadBehavior(scope: Construct, options: AssetUplo
|
|
|
356
359
|
export declare class AssetSupport extends Construct {
|
|
357
360
|
/** The bucket in use (either created here, or the BYO `props.bucket`). */
|
|
358
361
|
readonly bucket: s3.IBucket;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
362
|
+
/** The lazy transform Lambda; undefined unless `lazyPublicTransforms`. */
|
|
363
|
+
readonly transformFunction: lambda.Function | undefined;
|
|
364
|
+
/** The transform Lambda's CloudWatch log group; undefined unless `lazyPublicTransforms`. */
|
|
365
|
+
readonly transformLogGroup: logs.LogGroup | undefined;
|
|
366
|
+
/** The transform Lambda's Function URL; undefined unless `lazyPublicTransforms`. */
|
|
367
|
+
readonly transformFunctionUrl: lambda.FunctionUrl | undefined;
|
|
364
368
|
/** Effective advisory upload-size cap (see `AssetSupportProps.maxUploadBytes`). */
|
|
365
369
|
readonly maxUploadBytes: number;
|
|
366
370
|
private readonly behaviors;
|
|
@@ -388,6 +392,7 @@ export declare class AssetSupport extends Construct {
|
|
|
388
392
|
*/
|
|
389
393
|
private uploadOrigin?;
|
|
390
394
|
constructor(scope: Construct, id: string, props: AssetSupportProps);
|
|
395
|
+
private buildTransformLambda;
|
|
391
396
|
private buildBehaviors;
|
|
392
397
|
/**
|
|
393
398
|
* The two CloudFront behavior configs this system needs.
|
|
@@ -424,9 +429,8 @@ export declare class AssetSupport extends Construct {
|
|
|
424
429
|
* site's cookies and any cached basic-auth credential are live hazards
|
|
425
430
|
* there, stripped by policy and function rather than never sent at all.
|
|
426
431
|
* - NOT one shared distribution serving asset reads AND writes for every
|
|
427
|
-
* environment. That means one `AssetSupport`,
|
|
428
|
-
*
|
|
429
|
-
* every environment's asset pipeline at once. Only the upload route moves;
|
|
432
|
+
* environment. That means one `AssetSupport`, so a `canopycms-cdk` bump
|
|
433
|
+
* would move every environment's asset pipeline at once. Only the upload route moves;
|
|
430
434
|
* it depends on the bucket and nothing else, the same property that lets
|
|
431
435
|
* `assetUploadBehavior` build it from a bucket alone.
|
|
432
436
|
*/
|
|
@@ -435,17 +439,12 @@ export declare class AssetSupport extends Construct {
|
|
|
435
439
|
* Attach both asset behaviors to a concrete CloudFront distribution, in the
|
|
436
440
|
* only safe order.
|
|
437
441
|
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
* keep working. `'/assets/*'` also sorts BEFORE `'/assets/t/*'`
|
|
445
|
-
* lexicographically (`*` = 0x2A, `t` = 0x74), so alphabetizing the keys
|
|
446
|
-
* reproduces exactly this failure with no synth or deploy error to catch it -
|
|
447
|
-
* and `assetBehaviors()` attaches no path pattern, so nothing there stops a
|
|
448
|
-
* caller getting it wrong.
|
|
442
|
+
* CloudFront stops at the first matching pattern, and `/assets/*` also
|
|
443
|
+
* matches every `/assets/t/*` request. With `lazyPublicTransforms`, attaching
|
|
444
|
+
* it first means no miss ever reaches the transform Lambda, with no synth or
|
|
445
|
+
* deploy error; `'/assets/*'` also sorts first alphabetically. The two
|
|
446
|
+
* behaviors are identical otherwise, and the order is kept so that switching
|
|
447
|
+
* modes changes nothing else.
|
|
449
448
|
*
|
|
450
449
|
* `overrides` is merged into BOTH behaviors. The motivating case is a
|
|
451
450
|
* distribution running a viewer-request function on every behavior - tier
|