canopycms-cdk 0.0.67-int.90 → 0.0.67
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/constructs/asset-support.d.ts +137 -269
- package/dist/constructs/asset-support.js +275 -455
- package/dist/constructs/cms-distribution.d.ts +23 -40
- package/dist/constructs/cms-distribution.js +61 -88
- package/dist/constructs/cms-service.d.ts +38 -60
- package/dist/constructs/cms-service.js +246 -390
- package/dist/constructs/lambda-execution-role.d.ts +23 -39
- package/dist/constructs/lambda-execution-role.js +37 -60
- package/dist/index.js +0 -2
- package/dist/worker.d.ts +0 -2
- package/dist/worker.js +0 -2
- package/lambda/asset-transform/dist/handler.js +33 -49
- package/lambda/asset-transform/dist/node_modules/.package-lock.json +38 -38
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/README.md +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/glib-2.0/include/glibconfig.h +2 -2
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/{libvips-cpp.so.8.18.6 → libvips-cpp.so.8.18.7} +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/package.json +2 -2
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/versions.json +11 -11
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/index.cjs +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/lib/{sharp-linux-arm64-0.35.4.node → sharp-linux-arm64-0.35.5.node} +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/package.json +2 -2
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/index.cjs +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/lib/sharp-wasm32-0.35.5.node.js +1 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/lib/{sharp-wasm32-0.35.4.node.wasm → sharp-wasm32-0.35.5.node.wasm} +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/package.json +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/versions.json +7 -7
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.cts +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.mts +3 -3
- package/lambda/asset-transform/dist/node_modules/sharp/dist/is.cjs +8 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/is.mjs +8 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.cjs +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.mjs +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/output.cjs +1 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/output.mjs +1 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.cjs +14 -14
- package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.mjs +14 -14
- package/lambda/asset-transform/dist/node_modules/sharp/dist/sharp.cjs +3 -3
- package/lambda/asset-transform/dist/node_modules/sharp/dist/sharp.mjs +3 -3
- package/lambda/asset-transform/dist/node_modules/sharp/lib/index.d.ts +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/package.json +35 -35
- package/lambda/asset-transform/dist/node_modules/sharp/src/binding.gyp +1 -1
- package/lambda/asset-transform/dist/node_modules/sharp/src/common.cc +4 -4
- package/lambda/asset-transform/dist/node_modules/sharp/src/common.h +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/src/pipeline.cc +31 -6
- package/lambda/asset-transform/dist/node_modules/sharp/src/utilities.cc +1 -1
- package/package.json +4 -4
- package/worker/dist/index.js +251 -364
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/lib/sharp-wasm32-0.35.4.node.js +0 -1
|
@@ -20,30 +20,26 @@ export interface CanopyCmsDistributionProps {
|
|
|
20
20
|
* timeout, so the two cannot drift.
|
|
21
21
|
*
|
|
22
22
|
* Left unset on the origin, CloudFront applies its service default of **30
|
|
23
|
-
* seconds
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* inside the request, so this is a real path.
|
|
23
|
+
* seconds**, silently capping a 60s Lambda at half its budget: every request
|
|
24
|
+
* in the 30-60s band is answered 504 at the edge while the invocation runs to
|
|
25
|
+
* completion behind it, with nothing in either log explaining the other half.
|
|
26
|
+
* First-touch branch provisioning does a full `git clone` onto EFS inside the
|
|
27
|
+
* request, so this is a real path.
|
|
29
28
|
*
|
|
30
29
|
* Capped at 60s: CloudFront rejects more without a service-quota increase.
|
|
31
30
|
*/
|
|
32
31
|
originReadTimeout?: Duration;
|
|
33
32
|
/**
|
|
34
|
-
* Extra CloudFront behaviors, merged with this construct's own.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `/assets/t/*`), and without a way to pass them in there was no route to
|
|
39
|
-
* attach them to the distribution the scaffold generates — making the
|
|
40
|
-
* scaffold's own "uncomment to enable media" path a dead end.
|
|
33
|
+
* Extra CloudFront behaviors, merged with this construct's own. This is how
|
|
34
|
+
* `AssetSupport.assetBehaviors()`'s two behaviors (`/assets/*` and
|
|
35
|
+
* `/assets/t/*`) reach the distribution the scaffold generates, when they are
|
|
36
|
+
* not attached through the `assetSupport` prop.
|
|
41
37
|
*
|
|
42
38
|
* ORDER MATTERS. CloudFront matches path patterns in the order given, so a
|
|
43
39
|
* more specific pattern must be listed before a more general one that also
|
|
44
40
|
* matches: `/assets/t/*` before `/assets/*`, or every transform request is
|
|
45
|
-
* served by the static S3-only behavior and never fails over to the
|
|
46
|
-
*
|
|
41
|
+
* served by the static S3-only behavior and never fails over to the transform
|
|
42
|
+
* Lambda.
|
|
47
43
|
*
|
|
48
44
|
* Keys here override this construct's own behaviors on collision, which is
|
|
49
45
|
* deliberate — the caller is more specific than the default — and an
|
|
@@ -52,14 +48,8 @@ export interface CanopyCmsDistributionProps {
|
|
|
52
48
|
* `mergeBehaviors`.)
|
|
53
49
|
*
|
|
54
50
|
* Prefer the `assetSupport` prop over wiring `AssetSupport`'s behaviors in
|
|
55
|
-
* here by hand
|
|
56
|
-
*
|
|
57
|
-
* listed before `/assets/t/*`; the literal keys `assets`/`assetsTransform`
|
|
58
|
-
* (from spreading `assetBehaviors()`'s return value directly into this
|
|
59
|
-
* object instead of keying it by path pattern); or an asset pattern listed
|
|
60
|
-
* here at all while the `assetSupport` prop is also passed (see that
|
|
61
|
-
* prop's own doc comment). See `assertNoAssetBehaviorOrderingHazards`'s
|
|
62
|
-
* doc comment for the full list.
|
|
51
|
+
* here by hand. If you do it by hand, `mergeBehaviors` throws at synth on any
|
|
52
|
+
* of the three hazards listed above `assertNoAssetBehaviorOrderingHazards`.
|
|
63
53
|
*/
|
|
64
54
|
additionalBehaviors?: Record<string, cloudfront.BehaviorOptions>;
|
|
65
55
|
/**
|
|
@@ -72,20 +62,14 @@ export interface CanopyCmsDistributionProps {
|
|
|
72
62
|
* If the asset behaviors need per-behavior options - a viewer-request
|
|
73
63
|
* function for tier auth being the motivating case, since without it
|
|
74
64
|
* `/assets/*` is anonymously readable on an authenticated tier - pass
|
|
75
|
-
* `assetBehaviorOverrides` alongside this prop
|
|
76
|
-
*
|
|
77
|
-
* sent exactly the adopter who most needs the ordering guarantee back to the
|
|
78
|
-
* manual path this prop exists to replace.
|
|
65
|
+
* `assetBehaviorOverrides` alongside this prop rather than dropping it and
|
|
66
|
+
* hand-calling `attachTo` after construction.
|
|
79
67
|
*
|
|
80
68
|
* Prefer this over passing `assetSupport.assetBehaviors()` through
|
|
81
|
-
* `additionalBehaviors` by hand
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* `additionalBehaviors`'s own doc comment.
|
|
86
|
-
*
|
|
87
|
-
* No ordering logic lives in this construct beyond calling this method -
|
|
88
|
-
* ordering is encoded exactly once, in `AssetSupport.attachTo()` itself.
|
|
69
|
+
* `additionalBehaviors` by hand; that stays available as an escape hatch, and
|
|
70
|
+
* `mergeBehaviors` still guards it. No ordering logic lives in this construct
|
|
71
|
+
* beyond calling this method - ordering is encoded exactly once, in
|
|
72
|
+
* `AssetSupport.attachTo()` itself.
|
|
89
73
|
*
|
|
90
74
|
* @default - no asset behaviors are attached
|
|
91
75
|
*/
|
|
@@ -102,11 +86,10 @@ export interface CanopyCmsDistributionProps {
|
|
|
102
86
|
*
|
|
103
87
|
* Useless without `assetSupport`, and silently so - there would be no
|
|
104
88
|
* behaviors to merge it into - so that combination throws from this
|
|
105
|
-
* construct's CONSTRUCTOR rather than being ignored
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* the overrides object.)
|
|
89
|
+
* construct's CONSTRUCTOR rather than being ignored; nothing later can make
|
|
90
|
+
* it valid, so an `addValidation` would be too late. An adopter sharing one
|
|
91
|
+
* props object across tiers, with `assetSupport` present on only some, should
|
|
92
|
+
* make the whole prop conditional rather than the overrides object.
|
|
110
93
|
*
|
|
111
94
|
* @default - the asset behaviors are attached with no overrides
|
|
112
95
|
*/
|
|
@@ -9,13 +9,11 @@ import { ASSETS_PATH_PATTERN, ASSETS_TRANSFORM_PATH_PATTERN, ASSET_BEHAVIOR_SPRE
|
|
|
9
9
|
* A plain `{ ...defaults, ...caller }` gets the values right and the ORDER
|
|
10
10
|
* wrong: JavaScript keeps an overridden key at its first-insertion index, so a
|
|
11
11
|
* caller who overrides `/_next/static/*` finds it pinned ahead of every other
|
|
12
|
-
* pattern they passed
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Dropping collided defaults FIRST means the caller's own object contributes
|
|
18
|
-
* its keys in its own order.
|
|
12
|
+
* pattern they passed. CloudFront matches path patterns in order, so that
|
|
13
|
+
* silently makes a more specific pattern listed after it unreachable - the
|
|
14
|
+
* failure the prop's doc comment warns about, introduced by the merge itself.
|
|
15
|
+
* Dropping collided defaults FIRST lets the caller's own object contribute its
|
|
16
|
+
* keys in its own order.
|
|
19
17
|
*/
|
|
20
18
|
function mergeBehaviors(defaults, caller, attachingAssetSupport) {
|
|
21
19
|
if (!caller)
|
|
@@ -40,49 +38,44 @@ function mergeBehaviors(defaults, caller, attachingAssetSupport) {
|
|
|
40
38
|
return merged;
|
|
41
39
|
}
|
|
42
40
|
/**
|
|
43
|
-
*
|
|
44
|
-
* assembled `additionalBehaviors` can introduce (see
|
|
45
|
-
* `AssetSupport.attachTo()`'s doc comment for the full mechanism):
|
|
46
|
-
*
|
|
47
|
-
* 1. The literal keys `assets`/`assetsTransform` present in `merged` mean
|
|
48
|
-
* `assetSupport.assetBehaviors()`'s return value was spread directly into
|
|
49
|
-
* `additionalBehaviors` (a `Record<pathPattern, BehaviorOptions>`) instead
|
|
50
|
-
* of being attached via `assetSupport.attachTo(distribution)` or this
|
|
51
|
-
* construct's `assetSupport` prop. This type-checks and deploys clean,
|
|
52
|
-
* synthesizing two behaviors matching the literal path patterns `assets`
|
|
53
|
-
* and `assetsTransform`, which nothing ever requests.
|
|
54
|
-
* 2. `/assets/*` listed before `/assets/t/*` means CloudFront's first-match-
|
|
55
|
-
* wins ordering serves every transform request off the broader, S3-only
|
|
56
|
-
* `/assets/*` behavior, permanently 403ing any derivative that has not
|
|
57
|
-
* already been computed (an OAC-signed S3 miss reports 403).
|
|
58
|
-
* 3. Either asset pattern present in `merged` WHILE the `assetSupport` prop is
|
|
59
|
-
* also passed means both wiring routes are active at once, so each pattern
|
|
60
|
-
* is attached twice. This is the migration mistake specifically - keeping a
|
|
61
|
-
* hand-wired block while adopting the prop - and hazard 2 cannot catch it,
|
|
62
|
-
* because the hand-written order is usually the correct one.
|
|
63
|
-
*
|
|
64
|
-
* `Object.keys` insertion order is spec-guaranteed for string keys like
|
|
65
|
-
* these (none are integer-like array-index strings), so comparing index
|
|
66
|
-
* order here is sound.
|
|
67
|
-
*
|
|
68
|
-
* SCOPE LIMIT: this protects only callers going through
|
|
69
|
-
* `CanopyCmsDistribution`'s own `additionalBehaviors` merge. It cannot help a
|
|
70
|
-
* bespoke `new cloudfront.Distribution(...)` assembled elsewhere -
|
|
71
|
-
* `AssetSupport.attachTo()` is the answer there.
|
|
72
|
-
*/
|
|
73
|
-
/**
|
|
74
|
-
* CloudFront treats the leading `/` on a path pattern as optional -- `assets/*`
|
|
41
|
+
* CloudFront treats the leading `/` on a path pattern as optional - `assets/*`
|
|
75
42
|
* and `/assets/*` match exactly the same requests, and AWS's own console and
|
|
76
|
-
* docs frequently show the slash-less spelling.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* modes here.
|
|
43
|
+
* docs frequently show the slash-less spelling. Every check here compares
|
|
44
|
+
* NORMALIZED keys: without this, `{ 'assets/*': ..., 'assets/t/*': ... }` walks
|
|
45
|
+
* past all three hazards below and synthesizes the broad-pattern-first order
|
|
46
|
+
* they exist to refuse - silently, the worst of the failure modes here.
|
|
81
47
|
*/
|
|
82
48
|
function normalizePathPattern(pattern) {
|
|
83
49
|
return pattern.startsWith('/') ? pattern.slice(1) : pattern;
|
|
84
50
|
}
|
|
85
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* The three AssetSupport CloudFront-behavior hazards a manually assembled
|
|
53
|
+
* `additionalBehaviors` can introduce (see `AssetSupport.attachTo()` for the
|
|
54
|
+
* mechanism):
|
|
55
|
+
*
|
|
56
|
+
* 1. The literal keys `assets`/`assetsTransform` in `merged` mean
|
|
57
|
+
* `assetBehaviors()`'s return value was spread straight into
|
|
58
|
+
* `additionalBehaviors` (a `Record<pathPattern, BehaviorOptions>`) instead of
|
|
59
|
+
* attached via `attachTo(distribution)` or the `assetSupport` prop. That
|
|
60
|
+
* type-checks and deploys clean, synthesizing two behaviors matching the
|
|
61
|
+
* literal patterns `assets` and `assetsTransform`, which nothing requests.
|
|
62
|
+
* 2. `/assets/*` listed before `/assets/t/*`: CloudFront's first-match-wins
|
|
63
|
+
* ordering serves every transform request off the broader, S3-only
|
|
64
|
+
* `/assets/*` behavior, permanently 403ing any derivative not already
|
|
65
|
+
* computed (an OAC-signed S3 miss reports 403).
|
|
66
|
+
* 3. Either asset pattern in `merged` WHILE the `assetSupport` prop is also
|
|
67
|
+
* passed: both wiring routes are active at once, so each pattern is attached
|
|
68
|
+
* twice. Hazard 2 cannot catch it, because the hand-written order is usually
|
|
69
|
+
* the correct one.
|
|
70
|
+
*
|
|
71
|
+
* `Object.keys` insertion order is spec-guaranteed for string keys like these
|
|
72
|
+
* (none are integer-like array-index strings), so comparing index order is
|
|
73
|
+
* sound.
|
|
74
|
+
*
|
|
75
|
+
* SCOPE LIMIT: this protects only callers going through this construct's own
|
|
76
|
+
* `additionalBehaviors` merge, not a bespoke `new cloudfront.Distribution(...)`
|
|
77
|
+
* assembled elsewhere - `AssetSupport.attachTo()` is the answer there.
|
|
78
|
+
*/
|
|
86
79
|
function assertNoAssetBehaviorOrderingHazards(merged, attachingAssetSupport) {
|
|
87
80
|
const keys = Object.keys(merged).map(normalizePathPattern);
|
|
88
81
|
const assetsPattern = normalizePathPattern(ASSETS_PATH_PATTERN);
|
|
@@ -163,27 +156,19 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
163
156
|
'a viewer-request function for tier auth. Pass `assetSupport` as well, or drop the ' +
|
|
164
157
|
'overrides.');
|
|
165
158
|
}
|
|
166
|
-
// ========================================================================
|
|
167
|
-
// DNS — Hosted Zone lookup
|
|
168
|
-
// ========================================================================
|
|
169
159
|
const hostedZone = props.hostedZone ??
|
|
170
160
|
route53.HostedZone.fromLookup(this, 'Zone', {
|
|
171
161
|
domainName: props.hostedZoneDomain,
|
|
172
162
|
});
|
|
173
|
-
// ========================================================================
|
|
174
|
-
// ACM Certificate
|
|
175
|
-
// ========================================================================
|
|
176
163
|
// CloudFront requires its ACM certificate to live in us-east-1, and this
|
|
177
|
-
// construct creates one in the STACK's region
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
// adopter elsewhere got an opaque region error and had to research both
|
|
181
|
-
// workarounds themselves. Fail with the answer instead.
|
|
164
|
+
// construct creates one in the STACK's region - so fail here with both
|
|
165
|
+
// workarounds rather than leave an adopter elsewhere with an opaque region
|
|
166
|
+
// error at deploy.
|
|
182
167
|
//
|
|
183
168
|
// Only when the region is actually known at synth: a region-agnostic stack
|
|
184
169
|
// resolves to a token, and rejecting that would break `cdk synth` for
|
|
185
|
-
// everyone (the token is not us-east-1 as a string). Those deploys
|
|
186
|
-
//
|
|
170
|
+
// everyone (the token is not us-east-1 as a string). Those deploys stay
|
|
171
|
+
// CloudFront's to reject at deploy time.
|
|
187
172
|
const stackRegion = Stack.of(this).region;
|
|
188
173
|
if (!props.certificate && !Token.isUnresolved(stackRegion) && stackRegion !== 'us-east-1') {
|
|
189
174
|
throw new Error(`CanopyCmsDistribution: CloudFront requires its ACM certificate in us-east-1, but this ` +
|
|
@@ -197,9 +182,6 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
197
182
|
domainName: props.domainName,
|
|
198
183
|
validation: acm.CertificateValidation.fromDns(hostedZone),
|
|
199
184
|
});
|
|
200
|
-
// ========================================================================
|
|
201
|
-
// CloudFront Distribution
|
|
202
|
-
// ========================================================================
|
|
203
185
|
// Origin: Lambda Function URL secured with Origin Access Control (OAC).
|
|
204
186
|
// CloudFront signs each origin request with SigV4 so the AWS_IAM-protected
|
|
205
187
|
// Function URL only accepts traffic from this distribution — direct hits to
|
|
@@ -221,18 +203,16 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
221
203
|
const origin = origins.FunctionUrlOrigin.withOriginAccessControl(props.functionUrl, {
|
|
222
204
|
readTimeout,
|
|
223
205
|
});
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
// the
|
|
232
|
-
//
|
|
233
|
-
// ALL_VIEWER_EXCEPT_HOST_HEADER origin request policy on the behavior.
|
|
206
|
+
// API/editor routes take AWS's managed CACHING_DISABLED policy as it is:
|
|
207
|
+
// CloudFront rejects ANY non-none cache-key setting on a caching-disabled
|
|
208
|
+
// policy - `Authorization` in a header allowlist, and equally
|
|
209
|
+
// cookieBehavior/queryStringBehavior `all()` ("The parameter CookieBehavior
|
|
210
|
+
// is invalid for policy with caching disabled"). With TTL 0 the cache key is
|
|
211
|
+
// meaningless anyway, and the origin still receives the full viewer request
|
|
212
|
+
// (headers/cookies/query string, minus Host - forwarding Host would break
|
|
213
|
+
// the OAC-signed Function URL) via the ALL_VIEWER_EXCEPT_HOST_HEADER origin
|
|
214
|
+
// request policy on the behavior.
|
|
234
215
|
const noCachePolicy = cloudfront.CachePolicy.CACHING_DISABLED;
|
|
235
|
-
// Cache policy for static assets
|
|
236
216
|
const staticCachePolicy = new cloudfront.CachePolicy(this, 'StaticCachePolicy', {
|
|
237
217
|
defaultTtl: Duration.days(365),
|
|
238
218
|
maxTtl: Duration.days(365),
|
|
@@ -243,15 +223,15 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
243
223
|
});
|
|
244
224
|
// CloudFront gives the origin the Function URL's own Host (forwarding the
|
|
245
225
|
// viewer Host would break the OAC SigV4 signature), and Lambda Web Adapter
|
|
246
|
-
// forwards no `x-forwarded-*` headers of its own - so without this
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
// unambiguous anyway
|
|
254
|
-
// REDIRECT_TO_HTTPS
|
|
226
|
+
// forwards no `x-forwarded-*` headers of its own - so without this function,
|
|
227
|
+
// Clerk/Next derive sign-in redirect URLs from the IAM-authed Function URL
|
|
228
|
+
// host instead of the public domain, and the redirect 403s (direct Function
|
|
229
|
+
// URL hits are rejected).
|
|
230
|
+
//
|
|
231
|
+
// ONLY x-forwarded-host: x-forwarded-proto is on CloudFront Functions'
|
|
232
|
+
// DISALLOWED header list and setting it fails every request with 502
|
|
233
|
+
// FunctionValidationError. Proto is unambiguous anyway - viewer-facing
|
|
234
|
+
// CloudFront is HTTPS-only via REDIRECT_TO_HTTPS.
|
|
255
235
|
const forwardedHostFunction = new cloudfront.Function(this, 'ForwardedHostFunction', {
|
|
256
236
|
code: cloudfront.FunctionCode.fromInline([
|
|
257
237
|
'function handler(event) {',
|
|
@@ -291,14 +271,7 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
291
271
|
// logic lives exactly once, inside attachTo() itself. Callers who instead
|
|
292
272
|
// pass `assetSupport.assetBehaviors()` through `additionalBehaviors` by
|
|
293
273
|
// hand are covered by `mergeBehaviors`'s synth-time guard above instead.
|
|
294
|
-
//
|
|
295
|
-
// `assetBehaviorOverrides` is forwarded so that needing per-behavior
|
|
296
|
-
// options is not a reason to leave this prop - and so the ordering
|
|
297
|
-
// guarantee survives the tier-auth case that most needs it.
|
|
298
274
|
props.assetSupport?.attachTo(this.distribution, props.assetBehaviorOverrides);
|
|
299
|
-
// ========================================================================
|
|
300
|
-
// DNS Records
|
|
301
|
-
// ========================================================================
|
|
302
275
|
new route53.ARecord(this, 'ARecord', {
|
|
303
276
|
zone: hostedZone,
|
|
304
277
|
recordName: props.domainName,
|
|
@@ -130,17 +130,14 @@ export interface CanopyCmsServiceProps {
|
|
|
130
130
|
/**
|
|
131
131
|
* Secrets Manager ARN for the App's PEM private key.
|
|
132
132
|
*
|
|
133
|
-
* **ARN-only: there is deliberately no plaintext prop for this key**,
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* `assertNotInlinePrivateKey`, and the note there about the generated stack
|
|
142
|
-
* reaching CDK's own ARN complaint first) rather than surfacing as a puzzling
|
|
143
|
-
* "an ARN must not contain a newline".
|
|
133
|
+
* **ARN-only: there is deliberately no plaintext prop for this key**, and the
|
|
134
|
+
* reason is mechanical. Every value the construct puts in the worker's
|
|
135
|
+
* environment is written into a `.env` file systemd reads as
|
|
136
|
+
* `EnvironmentFile=`, where a newline starts a new variable, so `assertEnvSafe`
|
|
137
|
+
* refuses one and a PEM is inherently multi-line: a plaintext key could not be
|
|
138
|
+
* delivered to the worker intact by this path at all. Passing the PEM itself
|
|
139
|
+
* here is caught by name at synth (`assertNotInlinePrivateKey`) rather than
|
|
140
|
+
* surfacing as a puzzling "an ARN must not contain a newline".
|
|
144
141
|
*
|
|
145
142
|
* The ARN is unioned into the worker's IAM policy alongside the other secret
|
|
146
143
|
* ARN props; you do not need to repeat it in `secretsArns`.
|
|
@@ -153,10 +150,7 @@ export interface CanopyCmsServiceProps {
|
|
|
153
150
|
* Stamped into the worker's
|
|
154
151
|
* `CANOPYCMS_GITHUB_APP_PRIVATE_KEY_SECRET_JSON_FIELD`. See
|
|
155
152
|
* `githubTokenSecretJsonField` above — same mechanism, same non-relationship
|
|
156
|
-
* to the ECS `:KEY::` ARN suffix.
|
|
157
|
-
* field support in the first place: an App private key is exactly the kind of
|
|
158
|
-
* material an organisation keeps inside one credential document per
|
|
159
|
-
* environment.
|
|
153
|
+
* to the ECS `:KEY::` ARN suffix.
|
|
160
154
|
*
|
|
161
155
|
* A PEM stored as a JSON string value carries its newlines as `\n` escapes,
|
|
162
156
|
* which `JSON.parse` turns back into real newlines — and the worker
|
|
@@ -190,25 +184,20 @@ export interface CanopyCmsServiceProps {
|
|
|
190
184
|
*
|
|
191
185
|
* Interpolated straight into a git ref (`refs/heads/{baseBranch}`) and into
|
|
192
186
|
* the worker's `.env` heredoc, so an invalid value is rejected at synth (see
|
|
193
|
-
* `assertValidGitBranchName` above)
|
|
194
|
-
*
|
|
195
|
-
* `
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* fatal error, `worker/index.ts` exits 1, and systemd's `Restart=always`
|
|
199
|
-
* repeats that forever — there is no working worker at all until the value
|
|
200
|
-
* is fixed and the instance replaced, and `rebaseActiveBranches` fetches and
|
|
201
|
-
* rebases against the wrong lineage in the meantime.
|
|
187
|
+
* `assertValidGitBranchName` above). A branch that git accepts but the repo
|
|
188
|
+
* does not have fails permanently at boot instead: `verifyBaseBranchExists`
|
|
189
|
+
* (packages/canopycms/src/worker/cms-worker.ts) throws, `worker/index.ts`
|
|
190
|
+
* exits 1, systemd's `Restart=always` repeats that forever, and
|
|
191
|
+
* `rebaseActiveBranches` rebases against the wrong lineage in the meantime.
|
|
202
192
|
*
|
|
203
193
|
* MUST match the shared repo's `canopycms.config.ts`'s `defaultBaseBranch`
|
|
204
194
|
* (both default to 'main' when unset) — the two are resolved by different
|
|
205
195
|
* processes (this stamps the worker's `.env`; the Lambda reads
|
|
206
|
-
* `config.defaultBaseBranch` at request time) with no automatic
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* whenever `defaultBaseBranch` is anything other than 'main'.
|
|
196
|
+
* `config.defaultBaseBranch` at request time) with no automatic reconciliation
|
|
197
|
+
* between them. `infrastructure/lib/cms-stack.ts`, as scaffolded by
|
|
198
|
+
* `canopycms init-deploy aws`, derives this prop FROM that config file so the
|
|
199
|
+
* two cannot drift; a hand-rolled stack must set it explicitly whenever
|
|
200
|
+
* `defaultBaseBranch` is anything other than 'main'.
|
|
212
201
|
*/
|
|
213
202
|
baseBranch?: string;
|
|
214
203
|
/**
|
|
@@ -218,22 +207,21 @@ export interface CanopyCmsServiceProps {
|
|
|
218
207
|
* `canopycms-settings-<deploymentName>` — see `deploymentName` below).
|
|
219
208
|
*
|
|
220
209
|
* Only set this to mirror an adopter-configured `config.settingsBranch` in
|
|
221
|
-
* `canopycms.config.ts`. Leaving both unset
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* who deploy through the generated stack.
|
|
210
|
+
* `canopycms.config.ts`. Leaving both unset is safe: Lambda and worker resolve
|
|
211
|
+
* the SAME computed name independently, with nothing to keep in sync. But if
|
|
212
|
+
* the shared config sets `settingsBranch` and this prop does not, the worker
|
|
213
|
+
* keeps resolving the computed name while the Lambda's `getSettingsBranchName`
|
|
214
|
+
* short-circuits on `config.settingsBranch`, so the two own different
|
|
215
|
+
* branches: the PRIMARY settings-push path still reaches GitHub (its task
|
|
216
|
+
* payload carries the Lambda-resolved name), but the worker's per-cycle
|
|
217
|
+
* backstop push (`pushSettingsBranches`) targets the wrong branch and its
|
|
218
|
+
* "foreign settings branch" warning misfires against the deployment's own.
|
|
219
|
+
*
|
|
220
|
+
* Interpolated into a git ref and the worker's `.env` heredoc, so validated at
|
|
221
|
+
* synth like `baseBranch` (see `assertValidGitBranchName`).
|
|
222
|
+
* `infrastructure/lib/cms-stack.ts`, as scaffolded by `canopycms init-deploy
|
|
223
|
+
* aws`, derives it from the same `canopycms.config.ts` so the two cannot
|
|
224
|
+
* drift.
|
|
237
225
|
*/
|
|
238
226
|
settingsBranch?: string;
|
|
239
227
|
/**
|
|
@@ -285,20 +273,10 @@ export interface CanopyCmsServiceProps {
|
|
|
285
273
|
* Execution role for the CMS Lambda (default: CDK creates one).
|
|
286
274
|
*
|
|
287
275
|
* Set this when the role's ARN has to be computable WITHOUT a reference to
|
|
288
|
-
* this construct -
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* account boundary does not give you that: CDK emits `Fn::GetStackOutput`, a
|
|
293
|
-
* CDK-CLI-only intrinsic resolved at deploy time, so the coupling is
|
|
294
|
-
* invisible to CloudFormation and unusable by any deploy path that is not
|
|
295
|
-
* `cdk deploy`. Create a deterministically NAMED role instead and both stacks
|
|
296
|
-
* can compute `arn:aws:iam::<account>:role/<name>` from literals, with
|
|
297
|
-
* nothing crossing between them.
|
|
298
|
-
*
|
|
299
|
-
* A named IAM role means the consuming stack needs `CAPABILITY_NAMED_IAM`,
|
|
300
|
-
* and cannot be replaced in place without a rename - that trade is yours to
|
|
301
|
-
* make here, which is the point of taking a role rather than a name.
|
|
276
|
+
* this construct - typically a cross-account asset bucket. See
|
|
277
|
+
* `AssetSupportProps.transformRole` for why `Fn::GetStackOutput` does not give
|
|
278
|
+
* you that, and what a deterministically NAMED role costs
|
|
279
|
+
* (`CAPABILITY_NAMED_IAM`, no in-place replacement without a rename).
|
|
302
280
|
*
|
|
303
281
|
* This Lambda is VPC-attached, which makes the compensation in
|
|
304
282
|
* `attachLambdaExecutionPolicies` (./lambda-execution-role) load-bearing
|