canopycms-cdk 0.0.57 → 0.0.59
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 +143 -0
- package/dist/constructs/asset-support.d.ts.map +1 -0
- package/dist/constructs/asset-support.js +208 -0
- package/dist/constructs/asset-support.js.map +1 -0
- package/dist/constructs/cms-distribution.d.ts +2 -0
- package/dist/constructs/cms-distribution.d.ts.map +1 -1
- package/dist/constructs/cms-distribution.js +39 -11
- package/dist/constructs/cms-distribution.js.map +1 -1
- package/dist/constructs/cms-service.d.ts +37 -2
- package/dist/constructs/cms-service.d.ts.map +1 -1
- package/dist/constructs/cms-service.js +122 -11
- package/dist/constructs/cms-service.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/lambda/asset-transform/dist/handler.js +30452 -0
- package/lambda/asset-transform/dist/node_modules/.package-lock.json +129 -0
- package/lambda/asset-transform/dist/node_modules/@img/colour/LICENSE.md +82 -0
- package/lambda/asset-transform/dist/node_modules/@img/colour/README.md +15 -0
- package/lambda/asset-transform/dist/node_modules/@img/colour/color.cjs +1596 -0
- package/lambda/asset-transform/dist/node_modules/@img/colour/index.cjs +1 -0
- package/lambda/asset-transform/dist/node_modules/@img/colour/index.d.ts +929 -0
- package/lambda/asset-transform/dist/node_modules/@img/colour/package.json +58 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/README.md +46 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/glib-2.0/include/glibconfig.h +218 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/index.js +1 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/libvips-cpp.so.8.18.3 +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/package.json +43 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/versions.json +30 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/LICENSE +191 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/README.md +18 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/index.cjs +2 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/lib/sharp-linux-arm64-0.35.3.node +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/package.json +47 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/LICENSE +201 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/README.md +163 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/index.d.ts +14 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/lib/detect-libc.js +313 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/lib/elf.js +39 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/lib/filesystem.js +51 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/lib/process.js +24 -0
- package/lambda/asset-transform/dist/node_modules/detect-libc/package.json +44 -0
- package/lambda/asset-transform/dist/node_modules/semver/LICENSE +15 -0
- package/lambda/asset-transform/dist/node_modules/semver/README.md +680 -0
- package/lambda/asset-transform/dist/node_modules/semver/bin/semver.js +195 -0
- package/lambda/asset-transform/dist/node_modules/semver/classes/comparator.js +143 -0
- package/lambda/asset-transform/dist/node_modules/semver/classes/index.js +7 -0
- package/lambda/asset-transform/dist/node_modules/semver/classes/range.js +577 -0
- package/lambda/asset-transform/dist/node_modules/semver/classes/semver.js +350 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/clean.js +8 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/cmp.js +54 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/coerce.js +62 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/compare-build.js +9 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/compare-loose.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/compare.js +7 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/diff.js +60 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/eq.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/gt.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/gte.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/inc.js +21 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/lt.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/lte.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/major.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/minor.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/neq.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/parse.js +18 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/patch.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/prerelease.js +8 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/rcompare.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/rsort.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/satisfies.js +12 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/sort.js +5 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/truncate.js +48 -0
- package/lambda/asset-transform/dist/node_modules/semver/functions/valid.js +8 -0
- package/lambda/asset-transform/dist/node_modules/semver/index.js +93 -0
- package/lambda/asset-transform/dist/node_modules/semver/internal/constants.js +37 -0
- package/lambda/asset-transform/dist/node_modules/semver/internal/debug.js +11 -0
- package/lambda/asset-transform/dist/node_modules/semver/internal/identifiers.js +29 -0
- package/lambda/asset-transform/dist/node_modules/semver/internal/lrucache.js +42 -0
- package/lambda/asset-transform/dist/node_modules/semver/internal/parse-options.js +17 -0
- package/lambda/asset-transform/dist/node_modules/semver/internal/re.js +223 -0
- package/lambda/asset-transform/dist/node_modules/semver/package.json +78 -0
- package/lambda/asset-transform/dist/node_modules/semver/preload.js +4 -0
- package/lambda/asset-transform/dist/node_modules/semver/range.bnf +17 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/gtr.js +6 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/intersects.js +9 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/ltr.js +6 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/max-satisfying.js +27 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/min-satisfying.js +26 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/min-version.js +63 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/outside.js +82 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/simplify.js +49 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/subset.js +249 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/to-comparators.js +10 -0
- package/lambda/asset-transform/dist/node_modules/semver/ranges/valid.js +13 -0
- package/lambda/asset-transform/dist/node_modules/sharp/LICENSE +191 -0
- package/lambda/asset-transform/dist/node_modules/sharp/README.md +112 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/channel.cjs +177 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/channel.mjs +177 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/colour.cjs +199 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/colour.mjs +199 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/composite.cjs +213 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/composite.mjs +213 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/constructor.cjs +511 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/constructor.mjs +511 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.cjs +25 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.cts +1999 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.mts +2046 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.mjs +25 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/input.cjs +814 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/input.mjs +814 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/is.cjs +143 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/is.mjs +143 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/libvips.cjs +212 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/libvips.mjs +212 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.cjs +998 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.mjs +998 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/output.cjs +1788 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/output.mjs +1788 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.cjs +617 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.mjs +617 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/sharp.cjs +174 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/sharp.mjs +174 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/utility.cjs +301 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/utility.mjs +301 -0
- package/lambda/asset-transform/dist/node_modules/sharp/install/build.js +38 -0
- package/lambda/asset-transform/dist/node_modules/sharp/lib/index.d.ts +1999 -0
- package/lambda/asset-transform/dist/node_modules/sharp/package.json +227 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/binding.gyp +303 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/common.cc +1183 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/common.h +421 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/metadata.cc +404 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/metadata.h +95 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/operations.cc +516 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/operations.h +137 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/pipeline.cc +1948 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/pipeline.h +422 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/sharp.cc +43 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/stats.cc +186 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/stats.h +62 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/utilities.cc +289 -0
- package/lambda/asset-transform/dist/node_modules/sharp/src/utilities.h +22 -0
- package/lambda/asset-transform/dist/package.json +5 -0
- package/package.json +15 -8
- package/worker/canopy-worker.service +16 -2
- package/worker/dist/index.js +50573 -0
- package/LICENSE +0 -21
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import { RemovalPolicy, aws_cloudfront as cloudfront, aws_iam as iam, aws_lambda as lambda, aws_s3 as s3 } from 'aws-cdk-lib';
|
|
3
|
+
export interface AssetSupportProps {
|
|
4
|
+
/**
|
|
5
|
+
* Use an existing bucket (BYO mode - e.g. a site's existing content
|
|
6
|
+
* bucket) instead of creating one. The caller owns that bucket's
|
|
7
|
+
* lifecycle rules and CORS configuration in this mode: `IBucket` (an
|
|
8
|
+
* imported bucket reference) has no CDK-level `addLifecycleRule`/
|
|
9
|
+
* `addCorsRule` - only a bucket this construct creates itself does. See
|
|
10
|
+
* `.claude/future-tasks/docs-site-assets-wiring.md` for the BYO-mode
|
|
11
|
+
* wiring this is designed for.
|
|
12
|
+
*
|
|
13
|
+
* @default - a new private bucket is created (standalone mode)
|
|
14
|
+
*/
|
|
15
|
+
readonly bucket?: s3.IBucket;
|
|
16
|
+
/**
|
|
17
|
+
* Origins allowed to presigned-POST/PUT/GET upload directly to the bucket
|
|
18
|
+
* (the editor's own origin(s) - e.g. `http://localhost:3000` in dev, or
|
|
19
|
+
* the deployed editor's domain). Only applied in standalone mode (see
|
|
20
|
+
* `bucket`).
|
|
21
|
+
*/
|
|
22
|
+
readonly editorOrigins: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Advisory upload-size cap in bytes. Not enforced by this construct -
|
|
25
|
+
* the actual cap comes from the presigned POST's `content-length-range`
|
|
26
|
+
* condition, set by `S3AssetStoreOptions.maxUploadBytes` at runtime
|
|
27
|
+
* (packages/canopycms/src/assets/store-s3.ts). Exposed here only so the
|
|
28
|
+
* consuming stack can wire the SAME number into the CMS Lambda's
|
|
29
|
+
* environment (e.g. as `MEDIA_MAX_UPLOAD_BYTES`) and keep infra and app
|
|
30
|
+
* config from silently drifting apart.
|
|
31
|
+
*
|
|
32
|
+
* @default 52428800 (50 MiB)
|
|
33
|
+
*/
|
|
34
|
+
readonly maxUploadBytes?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Enable S3 versioning on the created bucket. Standalone mode only.
|
|
37
|
+
*
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
readonly versioned?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Removal policy for the created bucket. Standalone mode only.
|
|
43
|
+
*
|
|
44
|
+
* @default RemovalPolicy.RETAIN
|
|
45
|
+
*/
|
|
46
|
+
readonly removalPolicy?: RemovalPolicy;
|
|
47
|
+
/**
|
|
48
|
+
* Auto-delete objects when the created bucket is destroyed. Only takes
|
|
49
|
+
* effect alongside `removalPolicy: RemovalPolicy.DESTROY` - useful for
|
|
50
|
+
* ephemeral canary/test stacks. Standalone mode only.
|
|
51
|
+
*
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
readonly autoDeleteObjects?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The two CloudFront behavior configs the asset system needs, keyed by the
|
|
58
|
+
* path pattern they belong under. Each value is a full `BehaviorOptions`
|
|
59
|
+
* (origin included), so a consumer can use it either way:
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const behaviors = assetSupport.assetBehaviors()
|
|
63
|
+
*
|
|
64
|
+
* // Building a new distribution. CloudFront matches path patterns in the
|
|
65
|
+
* // order they're listed and stops at the first match, so the more
|
|
66
|
+
* // specific '/assets/t/*' MUST come before '/assets/*' - otherwise the
|
|
67
|
+
* // broader S3-only pattern swallows transform requests first and they
|
|
68
|
+
* // 403 with no Lambda fallback.
|
|
69
|
+
* new cloudfront.Distribution(this, 'Dist', {
|
|
70
|
+
* defaultBehavior: ...,
|
|
71
|
+
* additionalBehaviors: {
|
|
72
|
+
* '/assets/t/*': behaviors.assetsTransform,
|
|
73
|
+
* '/assets/*': behaviors.assets,
|
|
74
|
+
* },
|
|
75
|
+
* })
|
|
76
|
+
*
|
|
77
|
+
* // Adding to an existing distribution: the same first-match-wins ordering
|
|
78
|
+
* // applies to the resulting CacheBehaviors list, so call addBehavior for
|
|
79
|
+
* // '/assets/t/*' before '/assets/*' here too.
|
|
80
|
+
* distribution.addBehavior('/assets/t/*', behaviors.assetsTransform.origin, behaviors.assetsTransform)
|
|
81
|
+
* distribution.addBehavior('/assets/*', behaviors.assets.origin, behaviors.assets)
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export interface AssetCloudFrontBehaviors {
|
|
85
|
+
/**
|
|
86
|
+
* `/assets/*` - static objects only (sanitized SVG/PDF the finalize
|
|
87
|
+
* pipeline wrote, plus already-computed transform outputs under
|
|
88
|
+
* `assets/t/...`, which also live under this prefix). S3 origin only -
|
|
89
|
+
* nothing here is ever computed on demand.
|
|
90
|
+
*/
|
|
91
|
+
readonly assets: cloudfront.BehaviorOptions;
|
|
92
|
+
/**
|
|
93
|
+
* `/assets/t/*` - transform outputs specifically. Origin group: the same
|
|
94
|
+
* S3 origin as `assets` primary, falling over to the transform Lambda's
|
|
95
|
+
* Function URL on 403 OR 404 (a signed OAC origin reports a miss as 403;
|
|
96
|
+
* configuring both is defense-in-depth). See the SPIKE RESULT in
|
|
97
|
+
* `.claude/future-tasks/assets-media-system.md` - this design is
|
|
98
|
+
* confirmed working with CloudFront caching the failover response.
|
|
99
|
+
*/
|
|
100
|
+
readonly assetsTransform: cloudfront.BehaviorOptions;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Per-site CDK construct for the asset/media delivery system (see
|
|
104
|
+
* `.claude/future-tasks/assets-media-system.md` for the full design record).
|
|
105
|
+
* Wires:
|
|
106
|
+
*
|
|
107
|
+
* - The bucket's asset-prefix lifecycle rule + CORS (standalone mode only).
|
|
108
|
+
* - The transform Lambda (`../../lambda/asset-transform/handler.ts`, built
|
|
109
|
+
* via `pnpm run build:lambda` - see that script's doc comment for the
|
|
110
|
+
* no-Docker sharp bundling approach) and its OAC-locked Function URL.
|
|
111
|
+
* - `assetBehaviors()`, the two CloudFront behavior configs a consuming
|
|
112
|
+
* distribution attaches (see `AssetCloudFrontBehaviors`'s doc comment for
|
|
113
|
+
* both attachment shapes).
|
|
114
|
+
* - `grantUploadAccess()`, the exact prefix-scoped grants the CMS/editor
|
|
115
|
+
* principal needs to run `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts).
|
|
116
|
+
*/
|
|
117
|
+
export declare class AssetSupport extends Construct {
|
|
118
|
+
/** The bucket in use (either created here, or the BYO `props.bucket`). */
|
|
119
|
+
readonly bucket: s3.IBucket;
|
|
120
|
+
/** The transform Lambda function. */
|
|
121
|
+
readonly transformFunction: lambda.Function;
|
|
122
|
+
/** The transform Lambda's Function URL - use as a CloudFront origin (see `assetBehaviors()`). */
|
|
123
|
+
readonly transformFunctionUrl: lambda.FunctionUrl;
|
|
124
|
+
/** Effective advisory upload-size cap (see `AssetSupportProps.maxUploadBytes`). */
|
|
125
|
+
readonly maxUploadBytes: number;
|
|
126
|
+
private readonly behaviors;
|
|
127
|
+
constructor(scope: Construct, id: string, props: AssetSupportProps);
|
|
128
|
+
private buildBehaviors;
|
|
129
|
+
/** The two CloudFront behavior configs this system needs - see `AssetCloudFrontBehaviors`'s doc comment for both attachment shapes. */
|
|
130
|
+
assetBehaviors(): AssetCloudFrontBehaviors;
|
|
131
|
+
/**
|
|
132
|
+
* Grant the CMS/editor principal (the CMS Lambda's role, typically) the
|
|
133
|
+
* exact permissions `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts)
|
|
134
|
+
* calls for:
|
|
135
|
+
*
|
|
136
|
+
* - put on `asset-staging/` (presigned POST + proxied `writeStaging`)
|
|
137
|
+
* - get on `asset-staging/` + `asset-originals/` + `asset-meta/` + `assets/`
|
|
138
|
+
* - put on `asset-originals/` + `asset-meta/` + `assets/`
|
|
139
|
+
* - delete on `asset-staging/` + `asset-meta/`
|
|
140
|
+
*/
|
|
141
|
+
grantUploadAccess(grantee: iam.IGrantable): void;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=asset-support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-support.d.ts","sourceRoot":"","sources":["../../src/constructs/asset-support.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAEL,aAAa,EACb,cAAc,IAAI,UAAU,EAE5B,OAAO,IAAI,GAAG,EACd,UAAU,IAAI,MAAM,EACpB,MAAM,IAAI,EAAE,EACb,MAAM,aAAa,CAAA;AAwDpB,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,OAAO,CAAA;IAE5B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,EAAE,CAAA;IAEhC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAEhC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAA;IAEtC;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,eAAe,CAAA;IAE3C;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC,eAAe,CAAA;CACrD;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,0EAA0E;IAC1E,SAAgB,MAAM,EAAE,EAAE,CAAC,OAAO,CAAA;IAElC,qCAAqC;IACrC,SAAgB,iBAAiB,EAAE,MAAM,CAAC,QAAQ,CAAA;IAElD,iGAAiG;IACjG,SAAgB,oBAAoB,EAAE,MAAM,CAAC,WAAW,CAAA;IAExD,mFAAmF;IACnF,SAAgB,cAAc,EAAE,MAAM,CAAA;IAEtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;gBAExC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB;IAgFlE,OAAO,CAAC,cAAc;IA+CtB,uIAAuI;IAChI,cAAc,IAAI,wBAAwB;IAIjD;;;;;;;;;OASG;IACI,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,UAAU,GAAG,IAAI;CAexD"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { Construct } from 'constructs';
|
|
4
|
+
import { Duration, RemovalPolicy, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, aws_lambda as lambda, aws_s3 as s3, } from 'aws-cdk-lib';
|
|
5
|
+
// This package (`canopycms-cdk`) is `"type": "module"`, so its compiled
|
|
6
|
+
// output is real ESM - `__dirname` is not a global there. Derive it from
|
|
7
|
+
// `import.meta.url` instead (mirrors ../../lambda/asset-transform/build.mjs).
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
/**
|
|
10
|
+
* The four S3 key prefixes the asset system uses, mirrored from
|
|
11
|
+
* `packages/canopycms/src/assets/asset-prefixes.ts` (the source of truth).
|
|
12
|
+
* Duplicated as plain string literals - not imported from `canopycms` -
|
|
13
|
+
* because this construct must synth cleanly for ANY consumer's CDK app
|
|
14
|
+
* (e.g. a site's own `infrastructure/` package), which has no reason to
|
|
15
|
+
* have `canopycms` itself resolvable from wherever its CDK code runs. The
|
|
16
|
+
* transform Lambda (../../lambda/asset-transform/handler.ts), by contrast,
|
|
17
|
+
* is bundled at build time from WITHIN this package (where `canopycms` is a
|
|
18
|
+
* real workspace devDependency), so it imports the canonical constants
|
|
19
|
+
* directly from `canopycms/server` instead of duplicating them - see that
|
|
20
|
+
* file's doc comment.
|
|
21
|
+
*/
|
|
22
|
+
const PREFIXES = {
|
|
23
|
+
originals: 'asset-originals',
|
|
24
|
+
staging: 'asset-staging',
|
|
25
|
+
meta: 'asset-meta',
|
|
26
|
+
public: 'assets',
|
|
27
|
+
transform: 'assets/t',
|
|
28
|
+
};
|
|
29
|
+
/** S3 CORS preflight cache duration for presigned-POST uploads from the editor. */
|
|
30
|
+
const CORS_MAX_AGE_SECONDS = 3000;
|
|
31
|
+
/** Matches packages/canopycms/src/assets/store-s3.ts's `DEFAULT_MAX_UPLOAD_BYTES`. */
|
|
32
|
+
const DEFAULT_MAX_UPLOAD_BYTES = 50 * 1024 * 1024;
|
|
33
|
+
const TRANSFORM_LAMBDA_MEMORY_MB = 1536;
|
|
34
|
+
const TRANSFORM_LAMBDA_TIMEOUT = Duration.seconds(30);
|
|
35
|
+
/**
|
|
36
|
+
* `/assets/t/*`-specific cache TTLs. The managed `CachePolicy.CACHING_OPTIMIZED`
|
|
37
|
+
* (used for the plain `/assets/*` static behavior) has a 1-second MIN TTL,
|
|
38
|
+
* which is exactly the bug this policy exists to avoid: the transform
|
|
39
|
+
* Lambda's oversized-output path (handler.ts) returns a `Cache-Control:
|
|
40
|
+
* no-store` 302 redirect specifically so CloudFront never caches it - but a
|
|
41
|
+
* managed policy with ANY nonzero min TTL still caches that response for at
|
|
42
|
+
* least that long regardless of the origin's own `no-store`, so the redirect
|
|
43
|
+
* (to the now-written canonical S3 key) gets cached and re-served, and
|
|
44
|
+
* CloudFront's next hit for that same canonical key 404s/403s off S3 (or
|
|
45
|
+
* hasn't propagated yet), falling back to the Lambda again - a self-sustaining
|
|
46
|
+
* redirect loop. `minTtl: 0` lets an origin's own `Cache-Control` (including
|
|
47
|
+
* `no-store`) be honored immediately; `maxTtl`/`defaultTtl` stay generous so
|
|
48
|
+
* the normal case (an immutable 200 with a real `max-age`) still caches well.
|
|
49
|
+
*/
|
|
50
|
+
const TRANSFORM_CACHE_MIN_TTL = Duration.seconds(0);
|
|
51
|
+
const TRANSFORM_CACHE_DEFAULT_TTL = Duration.days(1);
|
|
52
|
+
const TRANSFORM_CACHE_MAX_TTL = Duration.days(365);
|
|
53
|
+
/**
|
|
54
|
+
* Per-site CDK construct for the asset/media delivery system (see
|
|
55
|
+
* `.claude/future-tasks/assets-media-system.md` for the full design record).
|
|
56
|
+
* Wires:
|
|
57
|
+
*
|
|
58
|
+
* - The bucket's asset-prefix lifecycle rule + CORS (standalone mode only).
|
|
59
|
+
* - The transform Lambda (`../../lambda/asset-transform/handler.ts`, built
|
|
60
|
+
* via `pnpm run build:lambda` - see that script's doc comment for the
|
|
61
|
+
* no-Docker sharp bundling approach) and its OAC-locked Function URL.
|
|
62
|
+
* - `assetBehaviors()`, the two CloudFront behavior configs a consuming
|
|
63
|
+
* distribution attaches (see `AssetCloudFrontBehaviors`'s doc comment for
|
|
64
|
+
* both attachment shapes).
|
|
65
|
+
* - `grantUploadAccess()`, the exact prefix-scoped grants the CMS/editor
|
|
66
|
+
* principal needs to run `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts).
|
|
67
|
+
*/
|
|
68
|
+
export class AssetSupport extends Construct {
|
|
69
|
+
constructor(scope, id, props) {
|
|
70
|
+
super(scope, id);
|
|
71
|
+
this.maxUploadBytes = props.maxUploadBytes ?? DEFAULT_MAX_UPLOAD_BYTES;
|
|
72
|
+
if (props.bucket) {
|
|
73
|
+
this.bucket = props.bucket;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.bucket = new s3.Bucket(this, 'Bucket', {
|
|
77
|
+
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
|
|
78
|
+
encryption: s3.BucketEncryption.S3_MANAGED,
|
|
79
|
+
versioned: props.versioned ?? false,
|
|
80
|
+
removalPolicy: props.removalPolicy ?? RemovalPolicy.RETAIN,
|
|
81
|
+
autoDeleteObjects: props.autoDeleteObjects ?? false,
|
|
82
|
+
// Only `asset-staging/` expires - originals/meta/public/transform
|
|
83
|
+
// outputs are all kept forever by design (content-addressed,
|
|
84
|
+
// immutable - see the design record's "Storage" section).
|
|
85
|
+
lifecycleRules: [
|
|
86
|
+
{
|
|
87
|
+
id: 'expire-asset-staging',
|
|
88
|
+
enabled: true,
|
|
89
|
+
prefix: `${PREFIXES.staging}/`,
|
|
90
|
+
expiration: Duration.days(1),
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
cors: [
|
|
94
|
+
{
|
|
95
|
+
id: 'editor-presigned-upload',
|
|
96
|
+
allowedOrigins: props.editorOrigins,
|
|
97
|
+
allowedMethods: [s3.HttpMethods.POST, s3.HttpMethods.PUT, s3.HttpMethods.GET],
|
|
98
|
+
allowedHeaders: ['*'],
|
|
99
|
+
maxAge: CORS_MAX_AGE_SECONDS,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
this.transformFunction = new lambda.Function(this, 'TransformFunction', {
|
|
105
|
+
// Built by `pnpm run build:lambda` (lambda/asset-transform/build.mjs) -
|
|
106
|
+
// esbuild bundle + a real linux/arm64 `npm install sharp` alongside it,
|
|
107
|
+
// no Docker. `cdk synth`/`deploy` need that script run first; it is
|
|
108
|
+
// NOT run automatically here (kept explicit rather than magic - see
|
|
109
|
+
// this construct's class doc comment).
|
|
110
|
+
code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'lambda', 'asset-transform', 'dist')),
|
|
111
|
+
handler: 'handler.handler',
|
|
112
|
+
// nodejs20.x was deprecated 2026-04-30; CDK's CloudFormation validation
|
|
113
|
+
// now fails synth on it. The esbuild bundle targets node20 and runs
|
|
114
|
+
// unchanged on the node22 runtime.
|
|
115
|
+
runtime: lambda.Runtime.NODEJS_22_X,
|
|
116
|
+
architecture: lambda.Architecture.ARM_64,
|
|
117
|
+
memorySize: TRANSFORM_LAMBDA_MEMORY_MB,
|
|
118
|
+
timeout: TRANSFORM_LAMBDA_TIMEOUT,
|
|
119
|
+
environment: {
|
|
120
|
+
ASSET_BUCKET: this.bucket.bucketName,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
// Read access to originals (what it transforms) - deviation from the PR
|
|
124
|
+
// spec's literal "read asset-originals/, write assets/" grant list: the
|
|
125
|
+
// handler must also read `asset-meta/{hash32}.json` to look up kind/ext
|
|
126
|
+
// before it can transform anything, so meta read access is granted too.
|
|
127
|
+
this.bucket.grantRead(this.transformFunction, `${PREFIXES.originals}/*`);
|
|
128
|
+
this.bucket.grantRead(this.transformFunction, `${PREFIXES.meta}/*`);
|
|
129
|
+
// Write access to `assets/*` covers both the public prefix and
|
|
130
|
+
// `assets/t/*` (transform outputs nest under it) in one grant.
|
|
131
|
+
this.bucket.grantPut(this.transformFunction, `${PREFIXES.public}/*`);
|
|
132
|
+
// AWS_IAM (not NONE): only reachable through CloudFront's Origin Access
|
|
133
|
+
// Control, matching CanopyCmsService/CanopyCmsDistribution's existing
|
|
134
|
+
// Function URL pattern (see cms-service.ts / cms-distribution.ts) -
|
|
135
|
+
// direct hits to the Function URL are rejected.
|
|
136
|
+
this.transformFunctionUrl = this.transformFunction.addFunctionUrl({
|
|
137
|
+
authType: lambda.FunctionUrlAuthType.AWS_IAM,
|
|
138
|
+
});
|
|
139
|
+
this.behaviors = this.buildBehaviors();
|
|
140
|
+
}
|
|
141
|
+
buildBehaviors() {
|
|
142
|
+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(this.bucket);
|
|
143
|
+
const transformLambdaOrigin = origins.FunctionUrlOrigin.withOriginAccessControl(this.transformFunctionUrl);
|
|
144
|
+
const assets = {
|
|
145
|
+
origin: s3Origin,
|
|
146
|
+
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
147
|
+
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
|
|
148
|
+
compress: true,
|
|
149
|
+
};
|
|
150
|
+
// Custom (not managed CACHING_OPTIMIZED) - see TRANSFORM_CACHE_MIN_TTL's
|
|
151
|
+
// doc comment for why this behavior specifically needs minTtl: 0.
|
|
152
|
+
// Directives live entirely in the path (no query string), and the
|
|
153
|
+
// response never varies by cookie/request header, so nothing is
|
|
154
|
+
// forwarded into the cache key.
|
|
155
|
+
const transformCachePolicy = new cloudfront.CachePolicy(this, 'AssetsTransformCachePolicy', {
|
|
156
|
+
minTtl: TRANSFORM_CACHE_MIN_TTL,
|
|
157
|
+
defaultTtl: TRANSFORM_CACHE_DEFAULT_TTL,
|
|
158
|
+
maxTtl: TRANSFORM_CACHE_MAX_TTL,
|
|
159
|
+
enableAcceptEncodingGzip: true,
|
|
160
|
+
enableAcceptEncodingBrotli: true,
|
|
161
|
+
queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(),
|
|
162
|
+
headerBehavior: cloudfront.CacheHeaderBehavior.none(),
|
|
163
|
+
cookieBehavior: cloudfront.CacheCookieBehavior.none(),
|
|
164
|
+
});
|
|
165
|
+
const assetsTransform = {
|
|
166
|
+
origin: new origins.OriginGroup({
|
|
167
|
+
primaryOrigin: s3Origin,
|
|
168
|
+
fallbackOrigin: transformLambdaOrigin,
|
|
169
|
+
// A signed OAC origin reports a miss as 403 (not 404, since it
|
|
170
|
+
// never reveals object existence) - CloudFront's own S3 origin
|
|
171
|
+
// handling can still surface 404 in some paths, so both are
|
|
172
|
+
// configured. Confirmed working end-to-end by the sandbox spike
|
|
173
|
+
// (.claude/future-tasks/assets-media-system.md's SPIKE RESULT).
|
|
174
|
+
fallbackStatusCodes: [403, 404],
|
|
175
|
+
}),
|
|
176
|
+
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
177
|
+
cachePolicy: transformCachePolicy,
|
|
178
|
+
};
|
|
179
|
+
return { assets, assetsTransform };
|
|
180
|
+
}
|
|
181
|
+
/** The two CloudFront behavior configs this system needs - see `AssetCloudFrontBehaviors`'s doc comment for both attachment shapes. */
|
|
182
|
+
assetBehaviors() {
|
|
183
|
+
return this.behaviors;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Grant the CMS/editor principal (the CMS Lambda's role, typically) the
|
|
187
|
+
* exact permissions `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts)
|
|
188
|
+
* calls for:
|
|
189
|
+
*
|
|
190
|
+
* - put on `asset-staging/` (presigned POST + proxied `writeStaging`)
|
|
191
|
+
* - get on `asset-staging/` + `asset-originals/` + `asset-meta/` + `assets/`
|
|
192
|
+
* - put on `asset-originals/` + `asset-meta/` + `assets/`
|
|
193
|
+
* - delete on `asset-staging/` + `asset-meta/`
|
|
194
|
+
*/
|
|
195
|
+
grantUploadAccess(grantee) {
|
|
196
|
+
this.bucket.grantPut(grantee, `${PREFIXES.staging}/*`);
|
|
197
|
+
this.bucket.grantRead(grantee, `${PREFIXES.staging}/*`);
|
|
198
|
+
this.bucket.grantRead(grantee, `${PREFIXES.originals}/*`);
|
|
199
|
+
this.bucket.grantRead(grantee, `${PREFIXES.meta}/*`);
|
|
200
|
+
this.bucket.grantRead(grantee, `${PREFIXES.public}/*`);
|
|
201
|
+
this.bucket.grantPut(grantee, `${PREFIXES.originals}/*`);
|
|
202
|
+
this.bucket.grantPut(grantee, `${PREFIXES.meta}/*`);
|
|
203
|
+
this.bucket.grantPut(grantee, `${PREFIXES.public}/*`);
|
|
204
|
+
this.bucket.grantDelete(grantee, `${PREFIXES.staging}/*`);
|
|
205
|
+
this.bucket.grantDelete(grantee, `${PREFIXES.meta}/*`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=asset-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-support.js","sourceRoot":"","sources":["../../src/constructs/asset-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,8 @@ export interface CanopyCmsDistributionProps {
|
|
|
24
24
|
* - CloudFront distribution with Function URL origin
|
|
25
25
|
* - Route53 A/AAAA alias records
|
|
26
26
|
* - Cache policies: no-cache for /api/* and /edit*, cache /_next/static/*
|
|
27
|
+
* - Viewer-request CloudFront Function setting x-forwarded-host (redirect-URL
|
|
28
|
+
* derivation behind the Host-stripping OAC origin)
|
|
27
29
|
*/
|
|
28
30
|
export declare class CanopyCmsDistribution extends Construct {
|
|
29
31
|
/** The CloudFront distribution */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-distribution.d.ts","sourceRoot":"","sources":["../../src/constructs/cms-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAEL,cAAc,IAAI,UAAU,EAE5B,sBAAsB,IAAI,GAAG,EAC7B,WAAW,IAAI,OAAO,EAEtB,UAAU,IAAI,MAAM,EACrB,MAAM,aAAa,CAAA;AAEpB,MAAM,WAAW,0BAA0B;IACzC,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC,WAAW,CAAA;IAE/B,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAA;IAElB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAA;IAExB,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC,WAAW,CAAA;IAEhC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,GAAG,CAAC,YAAY,CAAA;CAC/B;AAED
|
|
1
|
+
{"version":3,"file":"cms-distribution.d.ts","sourceRoot":"","sources":["../../src/constructs/cms-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAEL,cAAc,IAAI,UAAU,EAE5B,sBAAsB,IAAI,GAAG,EAC7B,WAAW,IAAI,OAAO,EAEtB,UAAU,IAAI,MAAM,EACrB,MAAM,aAAa,CAAA;AAEpB,MAAM,WAAW,0BAA0B;IACzC,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC,WAAW,CAAA;IAE/B,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAA;IAElB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAA;IAExB,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC,WAAW,CAAA;IAEhC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,GAAG,CAAC,YAAY,CAAA;CAC/B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;IAClD,kCAAkC;IAClC,SAAgB,YAAY,EAAE,UAAU,CAAC,YAAY,CAAA;gBAEzC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B;CAyH5E"}
|
|
@@ -12,6 +12,8 @@ import { Duration, aws_cloudfront as cloudfront, aws_cloudfront_origins as origi
|
|
|
12
12
|
* - CloudFront distribution with Function URL origin
|
|
13
13
|
* - Route53 A/AAAA alias records
|
|
14
14
|
* - Cache policies: no-cache for /api/* and /edit*, cache /_next/static/*
|
|
15
|
+
* - Viewer-request CloudFront Function setting x-forwarded-host (redirect-URL
|
|
16
|
+
* derivation behind the Host-stripping OAC origin)
|
|
15
17
|
*/
|
|
16
18
|
export class CanopyCmsDistribution extends Construct {
|
|
17
19
|
constructor(scope, id, props) {
|
|
@@ -40,19 +42,19 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
40
42
|
// the Function URL are rejected (DEP-H2). withOriginAccessControl creates
|
|
41
43
|
// the OAC and grants CloudFront lambda:InvokeFunctionUrl automatically.
|
|
42
44
|
const origin = origins.FunctionUrlOrigin.withOriginAccessControl(props.functionUrl);
|
|
43
|
-
// Cache policy for API/editor routes:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
// Cache policy for API/editor routes: AWS's managed CACHING_DISABLED
|
|
46
|
+
// policy. Deploy-proven (deploy-test epic, 2026-07-23): CloudFront
|
|
47
|
+
// rejects ANY non-none cache-key setting on a caching-disabled policy -
|
|
48
|
+
// Authorization in a header allowlist (aws/aws-cdk#16977) but also
|
|
49
|
+
// cookieBehavior/queryStringBehavior `all()` ("The parameter
|
|
50
|
+
// CookieBehavior is invalid for policy with caching disabled"). With
|
|
51
|
+
// TTL 0 the cache key is meaningless anyway; the origin still receives
|
|
52
|
+
// the full viewer request (headers/cookies/query string, minus Host -
|
|
53
|
+
// forwarding Host would break the OAC-signed Function URL) via the
|
|
54
|
+
// ALL_VIEWER_EXCEPT_HOST_HEADER origin request policy on the behavior.
|
|
55
|
+
const noCachePolicy = cloudfront.CachePolicy.CACHING_DISABLED;
|
|
53
56
|
// Cache policy for static assets
|
|
54
57
|
const staticCachePolicy = new cloudfront.CachePolicy(this, 'StaticCachePolicy', {
|
|
55
|
-
cachePolicyName: `${id}-static`,
|
|
56
58
|
defaultTtl: Duration.days(365),
|
|
57
59
|
maxTtl: Duration.days(365),
|
|
58
60
|
minTtl: Duration.days(365),
|
|
@@ -60,6 +62,26 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
60
62
|
queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(),
|
|
61
63
|
cookieBehavior: cloudfront.CacheCookieBehavior.none(),
|
|
62
64
|
});
|
|
65
|
+
// CloudFront gives the origin the Function URL's own Host (forwarding the
|
|
66
|
+
// viewer Host would break the OAC SigV4 signature), and Lambda Web Adapter
|
|
67
|
+
// forwards no `x-forwarded-*` headers of its own - so without this
|
|
68
|
+
// function, Clerk/Next derive sign-in redirect URLs from the IAM-authed
|
|
69
|
+
// Function URL host instead of the public domain, and the redirect 403s
|
|
70
|
+
// (direct Function URL hits are rejected). Deploy-proven (deploy-test
|
|
71
|
+
// epic, 2026-07-23): x-forwarded-proto is on CloudFront Functions'
|
|
72
|
+
// DISALLOWED header list - setting it fails every request with 502
|
|
73
|
+
// FunctionValidationError. Only x-forwarded-host is set; proto is
|
|
74
|
+
// unambiguous anyway (viewer-facing CloudFront is HTTPS-only via
|
|
75
|
+
// REDIRECT_TO_HTTPS).
|
|
76
|
+
const forwardedHostFunction = new cloudfront.Function(this, 'ForwardedHostFunction', {
|
|
77
|
+
code: cloudfront.FunctionCode.fromInline([
|
|
78
|
+
'function handler(event) {',
|
|
79
|
+
' var request = event.request;',
|
|
80
|
+
" request.headers['x-forwarded-host'] = { value: request.headers.host.value };",
|
|
81
|
+
' return request;',
|
|
82
|
+
'}',
|
|
83
|
+
].join('\n')),
|
|
84
|
+
});
|
|
63
85
|
this.distribution = new cloudfront.Distribution(this, 'Distribution', {
|
|
64
86
|
domainNames: [props.domainName],
|
|
65
87
|
certificate,
|
|
@@ -69,6 +91,12 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
69
91
|
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
70
92
|
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
|
|
71
93
|
originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
|
|
94
|
+
functionAssociations: [
|
|
95
|
+
{
|
|
96
|
+
function: forwardedHostFunction,
|
|
97
|
+
eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
72
100
|
},
|
|
73
101
|
additionalBehaviors: {
|
|
74
102
|
'/_next/static/*': {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-distribution.js","sourceRoot":"","sources":["../../src/constructs/cms-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EACL,QAAQ,EACR,cAAc,IAAI,UAAU,EAC5B,sBAAsB,IAAI,OAAO,EACjC,sBAAsB,IAAI,GAAG,EAC7B,WAAW,IAAI,OAAO,EACtB,mBAAmB,IAAI,OAAO,GAE/B,MAAM,aAAa,CAAA;AAmBpB
|
|
1
|
+
{"version":3,"file":"cms-distribution.js","sourceRoot":"","sources":["../../src/constructs/cms-distribution.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
|
-
import { Duration, RemovalPolicy, aws_ec2 as ec2, aws_efs as efs, aws_lambda as lambda, aws_autoscaling as autoscaling } from 'aws-cdk-lib';
|
|
2
|
+
import { Duration, RemovalPolicy, aws_ec2 as ec2, aws_efs as efs, aws_lambda as lambda, aws_autoscaling as autoscaling, aws_logs as logs } from 'aws-cdk-lib';
|
|
3
|
+
import type { IBucket } from 'aws-cdk-lib/aws-s3';
|
|
3
4
|
export interface CanopyCmsServiceProps {
|
|
4
5
|
/** Docker image for the CMS Lambda function */
|
|
5
6
|
cmsDockerImage: lambda.DockerImageCode;
|
|
@@ -11,6 +12,14 @@ export interface CanopyCmsServiceProps {
|
|
|
11
12
|
timeout?: Duration;
|
|
12
13
|
/** Lambda reserved concurrency cap (default: 10) */
|
|
13
14
|
reservedConcurrency?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Lambda architecture (default: `Architecture.X86_64`, Lambda's own
|
|
17
|
+
* default). MUST match the platform the Docker image was built for - e.g.
|
|
18
|
+
* an image built for `Platform.LINUX_ARM64` requires
|
|
19
|
+
* `Architecture.ARM_64` here, or the function fails at invoke time with
|
|
20
|
+
* an exec format error.
|
|
21
|
+
*/
|
|
22
|
+
architecture?: lambda.Architecture;
|
|
14
23
|
/** EC2 spot max price (default: on-demand rate for t4g.nano) */
|
|
15
24
|
spotMaxPrice?: string;
|
|
16
25
|
/** Secrets Manager ARNs the worker needs to read (GitHub token, Clerk key) */
|
|
@@ -29,6 +38,27 @@ export interface CanopyCmsServiceProps {
|
|
|
29
38
|
clerkSecretKeySecretArn?: string;
|
|
30
39
|
/** Base branch name (default: 'main') */
|
|
31
40
|
baseBranch?: string;
|
|
41
|
+
/**
|
|
42
|
+
* The asset bucket (from `AssetSupport`, or any bucket following its
|
|
43
|
+
* prefix layout) the CMS Lambda's role should be granted access to. When
|
|
44
|
+
* provided, grants the exact prefix-scoped put/get/delete permissions
|
|
45
|
+
* `S3AssetStore` calls for (packages/canopycms/src/assets/store-s3.ts) -
|
|
46
|
+
* mirrors `AssetSupport.grantUploadAccess()` rather than depending on that
|
|
47
|
+
* construct directly, so `canopycms-cdk`'s two constructs stay decoupled.
|
|
48
|
+
*/
|
|
49
|
+
assetBucket?: IBucket;
|
|
50
|
+
/**
|
|
51
|
+
* Retention for the EC2 worker's CloudWatch log group (default: three
|
|
52
|
+
* months). Worker-only: the Lambdas keep their auto-created log groups.
|
|
53
|
+
*/
|
|
54
|
+
workerLogRetention?: logs.RetentionDays;
|
|
55
|
+
/**
|
|
56
|
+
* Name for the worker's CloudWatch log group (default:
|
|
57
|
+
* `/canopycms/<stackName>/worker`). Override to follow an org naming
|
|
58
|
+
* convention, or when instantiating this construct twice in one stack (the
|
|
59
|
+
* default name would collide).
|
|
60
|
+
*/
|
|
61
|
+
workerLogGroupName?: string;
|
|
32
62
|
}
|
|
33
63
|
/**
|
|
34
64
|
* Core CDK construct for CanopyCMS deployment.
|
|
@@ -39,8 +69,11 @@ export interface CanopyCmsServiceProps {
|
|
|
39
69
|
* - Lambda function (Docker image, EFS mount, private subnet, no internet)
|
|
40
70
|
* - Lambda Function URL (for CloudFront origin)
|
|
41
71
|
* - EC2 Worker (t4g.nano spot in ASG, public subnet, EFS mount, systemd)
|
|
72
|
+
* - Dedicated CloudWatch log group for the worker's stdout/stderr, shipped
|
|
73
|
+
* via the amazon-cloudwatch-agent (journald is not agent-readable)
|
|
42
74
|
* - Security groups (least-privilege)
|
|
43
|
-
* - IAM roles (Lambda: EFS only; EC2: EFS + Secrets Manager
|
|
75
|
+
* - IAM roles (Lambda: EFS only; EC2: EFS + Secrets Manager + CloudWatch
|
|
76
|
+
* Logs write, scoped to its own log group)
|
|
44
77
|
*/
|
|
45
78
|
export declare class CanopyCmsService extends Construct {
|
|
46
79
|
/** Lambda Function URL — use as CloudFront origin */
|
|
@@ -53,6 +86,8 @@ export declare class CanopyCmsService extends Construct {
|
|
|
53
86
|
readonly lambdaFunction: lambda.Function;
|
|
54
87
|
/** The EC2 worker Auto Scaling Group */
|
|
55
88
|
readonly workerAsg: autoscaling.AutoScalingGroup;
|
|
89
|
+
/** The EC2 worker's CloudWatch log group (worker stdout/stderr) */
|
|
90
|
+
readonly workerLogGroup: logs.LogGroup;
|
|
56
91
|
constructor(scope: Construct, id: string, props: CanopyCmsServiceProps);
|
|
57
92
|
}
|
|
58
93
|
//# sourceMappingURL=cms-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-service.d.ts","sourceRoot":"","sources":["../../src/constructs/cms-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cms-service.d.ts","sourceRoot":"","sources":["../../src/constructs/cms-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EACL,QAAQ,EACR,aAAa,EAEb,OAAO,IAAI,GAAG,EACd,OAAO,IAAI,GAAG,EAEd,UAAU,IAAI,MAAM,EACpB,eAAe,IAAI,WAAW,EAE9B,QAAQ,IAAI,IAAI,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAWjD,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC,eAAe,CAAA;IAEtC,4DAA4D;IAC5D,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAA;IAEd,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,QAAQ,CAAA;IAElB,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAE5B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,YAAY,CAAA;IAElC,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IAEtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEpC,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,aAAa,CAAA;IAEhC,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAA;IAEnB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAA;IAElB,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAE7B,mDAAmD;IACnD,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAEhC,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,IAAI,CAAC,aAAa,CAAA;IAEvC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,qDAAqD;IACrD,SAAgB,WAAW,EAAE,MAAM,CAAC,WAAW,CAAA;IAE/C,yBAAyB;IACzB,SAAgB,UAAU,EAAE,GAAG,CAAC,UAAU,CAAA;IAE1C,cAAc;IACd,SAAgB,GAAG,EAAE,GAAG,CAAC,IAAI,CAAA;IAE7B,0BAA0B;IAC1B,SAAgB,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAA;IAE/C,wCAAwC;IACxC,SAAgB,SAAS,EAAE,WAAW,CAAC,gBAAgB,CAAA;IAEvD,mEAAmE;IACnE,SAAgB,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAA;gBAEjC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB;CAgbvE"}
|