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.
Files changed (148) hide show
  1. package/dist/constructs/asset-support.d.ts +143 -0
  2. package/dist/constructs/asset-support.d.ts.map +1 -0
  3. package/dist/constructs/asset-support.js +208 -0
  4. package/dist/constructs/asset-support.js.map +1 -0
  5. package/dist/constructs/cms-distribution.d.ts +2 -0
  6. package/dist/constructs/cms-distribution.d.ts.map +1 -1
  7. package/dist/constructs/cms-distribution.js +39 -11
  8. package/dist/constructs/cms-distribution.js.map +1 -1
  9. package/dist/constructs/cms-service.d.ts +37 -2
  10. package/dist/constructs/cms-service.d.ts.map +1 -1
  11. package/dist/constructs/cms-service.js +122 -11
  12. package/dist/constructs/cms-service.js.map +1 -1
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/index.js.map +1 -1
  17. package/lambda/asset-transform/dist/handler.js +30452 -0
  18. package/lambda/asset-transform/dist/node_modules/.package-lock.json +129 -0
  19. package/lambda/asset-transform/dist/node_modules/@img/colour/LICENSE.md +82 -0
  20. package/lambda/asset-transform/dist/node_modules/@img/colour/README.md +15 -0
  21. package/lambda/asset-transform/dist/node_modules/@img/colour/color.cjs +1596 -0
  22. package/lambda/asset-transform/dist/node_modules/@img/colour/index.cjs +1 -0
  23. package/lambda/asset-transform/dist/node_modules/@img/colour/index.d.ts +929 -0
  24. package/lambda/asset-transform/dist/node_modules/@img/colour/package.json +58 -0
  25. package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/README.md +46 -0
  26. package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/glib-2.0/include/glibconfig.h +218 -0
  27. package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/index.js +1 -0
  28. package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/libvips-cpp.so.8.18.3 +0 -0
  29. package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/package.json +43 -0
  30. package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/versions.json +30 -0
  31. package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/LICENSE +191 -0
  32. package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/README.md +18 -0
  33. package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/index.cjs +2 -0
  34. package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/lib/sharp-linux-arm64-0.35.3.node +0 -0
  35. package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/package.json +47 -0
  36. package/lambda/asset-transform/dist/node_modules/detect-libc/LICENSE +201 -0
  37. package/lambda/asset-transform/dist/node_modules/detect-libc/README.md +163 -0
  38. package/lambda/asset-transform/dist/node_modules/detect-libc/index.d.ts +14 -0
  39. package/lambda/asset-transform/dist/node_modules/detect-libc/lib/detect-libc.js +313 -0
  40. package/lambda/asset-transform/dist/node_modules/detect-libc/lib/elf.js +39 -0
  41. package/lambda/asset-transform/dist/node_modules/detect-libc/lib/filesystem.js +51 -0
  42. package/lambda/asset-transform/dist/node_modules/detect-libc/lib/process.js +24 -0
  43. package/lambda/asset-transform/dist/node_modules/detect-libc/package.json +44 -0
  44. package/lambda/asset-transform/dist/node_modules/semver/LICENSE +15 -0
  45. package/lambda/asset-transform/dist/node_modules/semver/README.md +680 -0
  46. package/lambda/asset-transform/dist/node_modules/semver/bin/semver.js +195 -0
  47. package/lambda/asset-transform/dist/node_modules/semver/classes/comparator.js +143 -0
  48. package/lambda/asset-transform/dist/node_modules/semver/classes/index.js +7 -0
  49. package/lambda/asset-transform/dist/node_modules/semver/classes/range.js +577 -0
  50. package/lambda/asset-transform/dist/node_modules/semver/classes/semver.js +350 -0
  51. package/lambda/asset-transform/dist/node_modules/semver/functions/clean.js +8 -0
  52. package/lambda/asset-transform/dist/node_modules/semver/functions/cmp.js +54 -0
  53. package/lambda/asset-transform/dist/node_modules/semver/functions/coerce.js +62 -0
  54. package/lambda/asset-transform/dist/node_modules/semver/functions/compare-build.js +9 -0
  55. package/lambda/asset-transform/dist/node_modules/semver/functions/compare-loose.js +5 -0
  56. package/lambda/asset-transform/dist/node_modules/semver/functions/compare.js +7 -0
  57. package/lambda/asset-transform/dist/node_modules/semver/functions/diff.js +60 -0
  58. package/lambda/asset-transform/dist/node_modules/semver/functions/eq.js +5 -0
  59. package/lambda/asset-transform/dist/node_modules/semver/functions/gt.js +5 -0
  60. package/lambda/asset-transform/dist/node_modules/semver/functions/gte.js +5 -0
  61. package/lambda/asset-transform/dist/node_modules/semver/functions/inc.js +21 -0
  62. package/lambda/asset-transform/dist/node_modules/semver/functions/lt.js +5 -0
  63. package/lambda/asset-transform/dist/node_modules/semver/functions/lte.js +5 -0
  64. package/lambda/asset-transform/dist/node_modules/semver/functions/major.js +5 -0
  65. package/lambda/asset-transform/dist/node_modules/semver/functions/minor.js +5 -0
  66. package/lambda/asset-transform/dist/node_modules/semver/functions/neq.js +5 -0
  67. package/lambda/asset-transform/dist/node_modules/semver/functions/parse.js +18 -0
  68. package/lambda/asset-transform/dist/node_modules/semver/functions/patch.js +5 -0
  69. package/lambda/asset-transform/dist/node_modules/semver/functions/prerelease.js +8 -0
  70. package/lambda/asset-transform/dist/node_modules/semver/functions/rcompare.js +5 -0
  71. package/lambda/asset-transform/dist/node_modules/semver/functions/rsort.js +5 -0
  72. package/lambda/asset-transform/dist/node_modules/semver/functions/satisfies.js +12 -0
  73. package/lambda/asset-transform/dist/node_modules/semver/functions/sort.js +5 -0
  74. package/lambda/asset-transform/dist/node_modules/semver/functions/truncate.js +48 -0
  75. package/lambda/asset-transform/dist/node_modules/semver/functions/valid.js +8 -0
  76. package/lambda/asset-transform/dist/node_modules/semver/index.js +93 -0
  77. package/lambda/asset-transform/dist/node_modules/semver/internal/constants.js +37 -0
  78. package/lambda/asset-transform/dist/node_modules/semver/internal/debug.js +11 -0
  79. package/lambda/asset-transform/dist/node_modules/semver/internal/identifiers.js +29 -0
  80. package/lambda/asset-transform/dist/node_modules/semver/internal/lrucache.js +42 -0
  81. package/lambda/asset-transform/dist/node_modules/semver/internal/parse-options.js +17 -0
  82. package/lambda/asset-transform/dist/node_modules/semver/internal/re.js +223 -0
  83. package/lambda/asset-transform/dist/node_modules/semver/package.json +78 -0
  84. package/lambda/asset-transform/dist/node_modules/semver/preload.js +4 -0
  85. package/lambda/asset-transform/dist/node_modules/semver/range.bnf +17 -0
  86. package/lambda/asset-transform/dist/node_modules/semver/ranges/gtr.js +6 -0
  87. package/lambda/asset-transform/dist/node_modules/semver/ranges/intersects.js +9 -0
  88. package/lambda/asset-transform/dist/node_modules/semver/ranges/ltr.js +6 -0
  89. package/lambda/asset-transform/dist/node_modules/semver/ranges/max-satisfying.js +27 -0
  90. package/lambda/asset-transform/dist/node_modules/semver/ranges/min-satisfying.js +26 -0
  91. package/lambda/asset-transform/dist/node_modules/semver/ranges/min-version.js +63 -0
  92. package/lambda/asset-transform/dist/node_modules/semver/ranges/outside.js +82 -0
  93. package/lambda/asset-transform/dist/node_modules/semver/ranges/simplify.js +49 -0
  94. package/lambda/asset-transform/dist/node_modules/semver/ranges/subset.js +249 -0
  95. package/lambda/asset-transform/dist/node_modules/semver/ranges/to-comparators.js +10 -0
  96. package/lambda/asset-transform/dist/node_modules/semver/ranges/valid.js +13 -0
  97. package/lambda/asset-transform/dist/node_modules/sharp/LICENSE +191 -0
  98. package/lambda/asset-transform/dist/node_modules/sharp/README.md +112 -0
  99. package/lambda/asset-transform/dist/node_modules/sharp/dist/channel.cjs +177 -0
  100. package/lambda/asset-transform/dist/node_modules/sharp/dist/channel.mjs +177 -0
  101. package/lambda/asset-transform/dist/node_modules/sharp/dist/colour.cjs +199 -0
  102. package/lambda/asset-transform/dist/node_modules/sharp/dist/colour.mjs +199 -0
  103. package/lambda/asset-transform/dist/node_modules/sharp/dist/composite.cjs +213 -0
  104. package/lambda/asset-transform/dist/node_modules/sharp/dist/composite.mjs +213 -0
  105. package/lambda/asset-transform/dist/node_modules/sharp/dist/constructor.cjs +511 -0
  106. package/lambda/asset-transform/dist/node_modules/sharp/dist/constructor.mjs +511 -0
  107. package/lambda/asset-transform/dist/node_modules/sharp/dist/index.cjs +25 -0
  108. package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.cts +1999 -0
  109. package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.mts +2046 -0
  110. package/lambda/asset-transform/dist/node_modules/sharp/dist/index.mjs +25 -0
  111. package/lambda/asset-transform/dist/node_modules/sharp/dist/input.cjs +814 -0
  112. package/lambda/asset-transform/dist/node_modules/sharp/dist/input.mjs +814 -0
  113. package/lambda/asset-transform/dist/node_modules/sharp/dist/is.cjs +143 -0
  114. package/lambda/asset-transform/dist/node_modules/sharp/dist/is.mjs +143 -0
  115. package/lambda/asset-transform/dist/node_modules/sharp/dist/libvips.cjs +212 -0
  116. package/lambda/asset-transform/dist/node_modules/sharp/dist/libvips.mjs +212 -0
  117. package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.cjs +998 -0
  118. package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.mjs +998 -0
  119. package/lambda/asset-transform/dist/node_modules/sharp/dist/output.cjs +1788 -0
  120. package/lambda/asset-transform/dist/node_modules/sharp/dist/output.mjs +1788 -0
  121. package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.cjs +617 -0
  122. package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.mjs +617 -0
  123. package/lambda/asset-transform/dist/node_modules/sharp/dist/sharp.cjs +174 -0
  124. package/lambda/asset-transform/dist/node_modules/sharp/dist/sharp.mjs +174 -0
  125. package/lambda/asset-transform/dist/node_modules/sharp/dist/utility.cjs +301 -0
  126. package/lambda/asset-transform/dist/node_modules/sharp/dist/utility.mjs +301 -0
  127. package/lambda/asset-transform/dist/node_modules/sharp/install/build.js +38 -0
  128. package/lambda/asset-transform/dist/node_modules/sharp/lib/index.d.ts +1999 -0
  129. package/lambda/asset-transform/dist/node_modules/sharp/package.json +227 -0
  130. package/lambda/asset-transform/dist/node_modules/sharp/src/binding.gyp +303 -0
  131. package/lambda/asset-transform/dist/node_modules/sharp/src/common.cc +1183 -0
  132. package/lambda/asset-transform/dist/node_modules/sharp/src/common.h +421 -0
  133. package/lambda/asset-transform/dist/node_modules/sharp/src/metadata.cc +404 -0
  134. package/lambda/asset-transform/dist/node_modules/sharp/src/metadata.h +95 -0
  135. package/lambda/asset-transform/dist/node_modules/sharp/src/operations.cc +516 -0
  136. package/lambda/asset-transform/dist/node_modules/sharp/src/operations.h +137 -0
  137. package/lambda/asset-transform/dist/node_modules/sharp/src/pipeline.cc +1948 -0
  138. package/lambda/asset-transform/dist/node_modules/sharp/src/pipeline.h +422 -0
  139. package/lambda/asset-transform/dist/node_modules/sharp/src/sharp.cc +43 -0
  140. package/lambda/asset-transform/dist/node_modules/sharp/src/stats.cc +186 -0
  141. package/lambda/asset-transform/dist/node_modules/sharp/src/stats.h +62 -0
  142. package/lambda/asset-transform/dist/node_modules/sharp/src/utilities.cc +289 -0
  143. package/lambda/asset-transform/dist/node_modules/sharp/src/utilities.h +22 -0
  144. package/lambda/asset-transform/dist/package.json +5 -0
  145. package/package.json +15 -8
  146. package/worker/canopy-worker.service +16 -2
  147. package/worker/dist/index.js +50573 -0
  148. 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;;;;;;;;;;;;GAYG;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;CA4F5E"}
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: no caching, forward all headers
44
- const noCachePolicy = new cloudfront.CachePolicy(this, 'NoCachePolicy', {
45
- cachePolicyName: `${id}-no-cache`,
46
- defaultTtl: Duration.seconds(0),
47
- maxTtl: Duration.seconds(0),
48
- minTtl: Duration.seconds(0),
49
- headerBehavior: cloudfront.CacheHeaderBehavior.allowList('Authorization', 'Cookie', 'Host'),
50
- queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(),
51
- cookieBehavior: cloudfront.CacheCookieBehavior.all(),
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":"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"}
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":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EACL,QAAQ,EACR,aAAa,EACb,OAAO,IAAI,GAAG,EACd,OAAO,IAAI,GAAG,EAEd,UAAU,IAAI,MAAM,EACpB,eAAe,IAAI,WAAW,EAE/B,MAAM,aAAa,CAAA;AAEpB,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,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;CACpB;AAED;;;;;;;;;;;GAWG;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;gBAE3C,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB;CAwPvE"}
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"}