sst 2.30.4 → 2.31.0
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/constructs/BaseSite.d.ts +2 -13
- package/constructs/NextjsSite.d.ts +1 -1
- package/constructs/NextjsSite.js +8 -4
- package/constructs/RDS.js +2 -2
- package/constructs/SsrSite.d.ts +19 -73
- package/constructs/SsrSite.js +93 -205
- package/constructs/StaticSite.d.ts +46 -33
- package/constructs/StaticSite.js +57 -74
- package/constructs/deprecated/NextjsSite.js +57 -71
- package/package.json +5 -4
- package/support/base-site-archiver.mjs +18 -18
- package/support/custom-resources/index.mjs +8426 -383
- package/support/base-site-custom-resource/s3-handler.py +0 -195
- package/support/base-site-custom-resource/s3-upload.py +0 -89
package/constructs/BaseSite.d.ts
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { ErrorResponse, DistributionProps, BehaviorOptions, IOrigin } from "aws-cdk-lib/aws-cloudfront";
|
|
2
|
-
export interface BaseSiteFileOptionsFilter {
|
|
3
|
-
include?: string;
|
|
4
|
-
exclude?: string;
|
|
5
|
-
}
|
|
6
2
|
export interface BaseSiteFileOptions {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
contentType?: string;
|
|
10
|
-
contentEncoding?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface BaseSiteFileOptionsDeprecated {
|
|
13
|
-
include?: string | string[];
|
|
14
|
-
exclude?: string | string[];
|
|
3
|
+
files: string | string[];
|
|
4
|
+
ignore?: string | string[];
|
|
15
5
|
cacheControl?: string;
|
|
16
6
|
contentType?: string;
|
|
17
|
-
contentEncoding?: string;
|
|
18
7
|
}
|
|
19
8
|
export interface BaseSiteEnvironmentOutputsInfo {
|
|
20
9
|
path: string;
|
|
@@ -27,7 +27,7 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
|
|
|
27
27
|
* logging: "per-route",
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
logging?: "combined" | "per-route";
|
|
31
31
|
imageOptimization?: {
|
|
32
32
|
/**
|
|
33
33
|
* The amount of memory in MB allocated for image optimization function.
|
package/constructs/NextjsSite.js
CHANGED
|
@@ -13,7 +13,7 @@ import { toCdkSize } from "./util/size.js";
|
|
|
13
13
|
import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
14
14
|
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
15
15
|
import { VisibleError } from "../error.js";
|
|
16
|
-
const LAYER_VERSION = "
|
|
16
|
+
const LAYER_VERSION = "2";
|
|
17
17
|
const DEFAULT_OPEN_NEXT_VERSION = "2.2.4";
|
|
18
18
|
const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
|
|
19
19
|
"accept",
|
|
@@ -305,7 +305,7 @@ export class NextjsSite extends SsrSite {
|
|
|
305
305
|
};
|
|
306
306
|
}
|
|
307
307
|
wrapServerFunction(config) {
|
|
308
|
-
const { path: sitePath, experimental } = this.props;
|
|
308
|
+
const { path: sitePath, experimental, cdk } = this.props;
|
|
309
309
|
const stack = Stack.of(this);
|
|
310
310
|
const wrapperName = "nextjssite-index";
|
|
311
311
|
const serverPath = path.join(sitePath, ".open-next", "server-function");
|
|
@@ -341,7 +341,9 @@ export class NextjsSite extends SsrSite {
|
|
|
341
341
|
...config,
|
|
342
342
|
layers: this.isPerRouteLoggingEnabled()
|
|
343
343
|
? [
|
|
344
|
-
LayerVersion.fromLayerVersionArn(this, "SSTExtension",
|
|
344
|
+
LayerVersion.fromLayerVersionArn(this, "SSTExtension", cdk?.server?.architecture?.name === Architecture.X86_64.name
|
|
345
|
+
? `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-amd64:${LAYER_VERSION}`
|
|
346
|
+
: `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-arm64:${LAYER_VERSION}`),
|
|
345
347
|
]
|
|
346
348
|
: undefined,
|
|
347
349
|
handler: `${wrapperName}.handler`,
|
|
@@ -397,9 +399,11 @@ export class NextjsSite extends SsrSite {
|
|
|
397
399
|
isPerRouteLoggingEnabled() {
|
|
398
400
|
return (!this.doNotDeploy &&
|
|
399
401
|
!this.props.edge &&
|
|
400
|
-
this.props.
|
|
402
|
+
this.props.logging === "per-route");
|
|
401
403
|
}
|
|
402
404
|
disableDefaultLogging() {
|
|
405
|
+
// Note: keep default logs enabled
|
|
406
|
+
return;
|
|
403
407
|
const stack = Stack.of(this);
|
|
404
408
|
const server = this.serverFunction;
|
|
405
409
|
const policy = new Policy(this, "DisableLoggingPolicy", {
|
package/constructs/RDS.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import
|
|
2
|
+
import { globSync } from "glob";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import url from "url";
|
|
5
5
|
import * as crypto from "crypto";
|
|
@@ -347,7 +347,7 @@ export class RDS extends Construct {
|
|
|
347
347
|
}
|
|
348
348
|
generateMigrationsHash(migrations) {
|
|
349
349
|
// Get all files inside the migrations folder
|
|
350
|
-
const files =
|
|
350
|
+
const files = globSync("**", {
|
|
351
351
|
dot: true,
|
|
352
352
|
nodir: true,
|
|
353
353
|
follow: true,
|
package/constructs/SsrSite.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { SSTConstruct } from "./Construct.js";
|
|
|
8
8
|
import { NodeJSProps, FunctionProps } from "./Function.js";
|
|
9
9
|
import { SsrFunction, SsrFunctionProps } from "./SsrFunction.js";
|
|
10
10
|
import { EdgeFunction, EdgeFunctionProps } from "./EdgeFunction.js";
|
|
11
|
-
import { BaseSiteFileOptions,
|
|
11
|
+
import { BaseSiteFileOptions, BaseSiteReplaceProps, BaseSiteCdkDistributionProps } from "./BaseSite.js";
|
|
12
12
|
import { Size } from "./util/size.js";
|
|
13
13
|
import { Duration } from "./util/duration.js";
|
|
14
14
|
import { Permissions } from "./util/permission.js";
|
|
@@ -52,12 +52,7 @@ export interface SsrSiteNodeJSProps extends NodeJSProps {
|
|
|
52
52
|
}
|
|
53
53
|
export interface SsrDomainProps extends DistributionDomainProps {
|
|
54
54
|
}
|
|
55
|
-
export interface SsrSiteFileOptionsFilter extends BaseSiteFileOptionsFilter {
|
|
56
|
-
}
|
|
57
55
|
export interface SsrSiteFileOptions extends BaseSiteFileOptions {
|
|
58
|
-
filters: SsrSiteFileOptionsFilter[];
|
|
59
|
-
}
|
|
60
|
-
export interface SsrSiteFileOptionsDeprecated extends BaseSiteFileOptionsDeprecated {
|
|
61
56
|
}
|
|
62
57
|
export interface SsrSiteReplaceProps extends BaseSiteReplaceProps {
|
|
63
58
|
}
|
|
@@ -207,18 +202,18 @@ export interface SsrSiteProps {
|
|
|
207
202
|
*/
|
|
208
203
|
url?: string;
|
|
209
204
|
};
|
|
210
|
-
|
|
205
|
+
assets?: {
|
|
211
206
|
/**
|
|
212
|
-
* Character encoding for text based assets
|
|
207
|
+
* Character encoding for text based assets uploaded to S3 (ex: html, css, js, etc.). If "none" is specified, no charset will be returned in header.
|
|
213
208
|
* @default utf-8
|
|
214
209
|
* @example
|
|
215
210
|
* ```js
|
|
216
|
-
*
|
|
217
|
-
*
|
|
211
|
+
* assets: {
|
|
212
|
+
* textEncoding: "iso-8859-1"
|
|
218
213
|
* }
|
|
219
214
|
* ```
|
|
220
215
|
*/
|
|
221
|
-
textEncoding?: "
|
|
216
|
+
textEncoding?: "utf-8" | "iso-8859-1" | "windows-1252" | "ascii" | "none";
|
|
222
217
|
/**
|
|
223
218
|
* The strategy to use for invalidating the CDN cache. By default, the CDN cache will invalidate on changes any cached file, but this could become slow on very large projects.
|
|
224
219
|
* - "never" - No invalidation will be performed.
|
|
@@ -228,7 +223,7 @@ export interface SsrSiteProps {
|
|
|
228
223
|
* @default all
|
|
229
224
|
* @example
|
|
230
225
|
* ```js
|
|
231
|
-
*
|
|
226
|
+
* assets: {
|
|
232
227
|
* cdnInvalidationStrategy: "versioned"
|
|
233
228
|
* }
|
|
234
229
|
* ```
|
|
@@ -239,8 +234,8 @@ export interface SsrSiteProps {
|
|
|
239
234
|
* @default 1 year
|
|
240
235
|
* @example
|
|
241
236
|
* ```js
|
|
242
|
-
*
|
|
243
|
-
*
|
|
237
|
+
* assets: {
|
|
238
|
+
* versionedFilesTTL: "30 days"
|
|
244
239
|
* }
|
|
245
240
|
* ```
|
|
246
241
|
*/
|
|
@@ -250,7 +245,7 @@ export interface SsrSiteProps {
|
|
|
250
245
|
* @default public,max-age=31536000,immutable
|
|
251
246
|
* @example
|
|
252
247
|
* ```js
|
|
253
|
-
*
|
|
248
|
+
* assets: {
|
|
254
249
|
* versionedFilesCacheHeader: "public,max-age=31536000,immutable"
|
|
255
250
|
* }
|
|
256
251
|
* ```
|
|
@@ -261,8 +256,8 @@ export interface SsrSiteProps {
|
|
|
261
256
|
* @default 1 day
|
|
262
257
|
* @example
|
|
263
258
|
* ```js
|
|
264
|
-
*
|
|
265
|
-
*
|
|
259
|
+
* assets: {
|
|
260
|
+
* nonVersionedFilesTTL: "4 hours"
|
|
266
261
|
* }
|
|
267
262
|
* ```
|
|
268
263
|
*/
|
|
@@ -272,7 +267,7 @@ export interface SsrSiteProps {
|
|
|
272
267
|
* @default public,max-age=0,s-maxage=86400,stale-while-revalidate=8640
|
|
273
268
|
* @example
|
|
274
269
|
* ```js
|
|
275
|
-
*
|
|
270
|
+
* assets: {
|
|
276
271
|
* nonVersionedFilesCacheHeader: "public,max-age=0,no-cache"
|
|
277
272
|
* }
|
|
278
273
|
* ```
|
|
@@ -282,10 +277,10 @@ export interface SsrSiteProps {
|
|
|
282
277
|
* List of file options to specify cache control and content type for cached files. These file options are appended to the default file options so it's possible to override the default file options by specifying an overlapping file pattern.
|
|
283
278
|
* @example
|
|
284
279
|
* ```js
|
|
285
|
-
*
|
|
280
|
+
* assets: {
|
|
286
281
|
* fileOptions: [
|
|
287
282
|
* {
|
|
288
|
-
*
|
|
283
|
+
* files: "**\/*.zip",
|
|
289
284
|
* cacheControl: "private,no-cache,no-store,must-revalidate",
|
|
290
285
|
* contentType: "application/zip",
|
|
291
286
|
* },
|
|
@@ -294,6 +289,10 @@ export interface SsrSiteProps {
|
|
|
294
289
|
* ```
|
|
295
290
|
*/
|
|
296
291
|
fileOptions?: SsrSiteFileOptions[];
|
|
292
|
+
/**
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
295
|
+
_uploadConcurrency?: number;
|
|
297
296
|
};
|
|
298
297
|
/**
|
|
299
298
|
* While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
|
|
@@ -343,59 +342,6 @@ export interface SsrSiteProps {
|
|
|
343
342
|
responseHeadersPolicy?: IResponseHeadersPolicy;
|
|
344
343
|
server?: Pick<CdkFunctionProps, "layers" | "vpc" | "vpcSubnets" | "securityGroups" | "allowAllOutbound" | "allowPublicSubnet" | "architecture" | "logRetention"> & Pick<FunctionProps, "copyFiles">;
|
|
345
344
|
};
|
|
346
|
-
/**
|
|
347
|
-
* Pass in a list of file options to customize cache control and content type specific files. Specifying file options will bypass all default file options and only use the ones specified. Most configurations within the `cache` prop will be ignored.
|
|
348
|
-
* @deprecated Use `cache.fileOptions` instead. Note that the `cache.fileOptions` are appended to default file options, not a replacement as this prop was.
|
|
349
|
-
* @default
|
|
350
|
-
* Versioned files cached for 1 year at the CDN and browser level.
|
|
351
|
-
* Nonversioned files cached for 1 day at the CDN level, but not at the browser level.
|
|
352
|
-
* ```js
|
|
353
|
-
* fileOptions: [
|
|
354
|
-
* {
|
|
355
|
-
* exclude: "*",
|
|
356
|
-
* include: "{versioned_directory}/*",
|
|
357
|
-
* cacheControl: "public,max-age=31536000,immutable",
|
|
358
|
-
* },
|
|
359
|
-
* {
|
|
360
|
-
* exclude: "*",
|
|
361
|
-
* include: "[{non_versioned_file1}, {non_versioned_file2}, ...]",
|
|
362
|
-
* cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
363
|
-
* },
|
|
364
|
-
* {
|
|
365
|
-
* exclude: "*",
|
|
366
|
-
* include: "[{non_versioned_dir_1}/*, {non_versioned_dir_2}/*, ...]",
|
|
367
|
-
* cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
368
|
-
* },
|
|
369
|
-
* ]
|
|
370
|
-
* ```
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```js
|
|
374
|
-
* fileOptions: [
|
|
375
|
-
* {
|
|
376
|
-
* exclude: "*",
|
|
377
|
-
* include: "{versioned_directory}/*.css",
|
|
378
|
-
* cacheControl: "public,max-age=31536000,immutable",
|
|
379
|
-
* contentType: "text/css; charset=UTF-8",
|
|
380
|
-
* },
|
|
381
|
-
* {
|
|
382
|
-
* exclude: "*",
|
|
383
|
-
* include: "{versioned_directory}/*.js",
|
|
384
|
-
* cacheControl: "public,max-age=31536000,immutable",
|
|
385
|
-
* },
|
|
386
|
-
* {
|
|
387
|
-
* exclude: "*",
|
|
388
|
-
* include: "[{non_versioned_file1}, {non_versioned_file2}, ...]",
|
|
389
|
-
* cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
390
|
-
* },
|
|
391
|
-
* {
|
|
392
|
-
* exclude: "*",
|
|
393
|
-
* include: "[{non_versioned_dir_1}/*, {non_versioned_dir_2}/*, ...]",
|
|
394
|
-
* cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
395
|
-
* },
|
|
396
|
-
* ]
|
|
397
|
-
*/
|
|
398
|
-
fileOptions?: SsrSiteFileOptionsDeprecated[];
|
|
399
345
|
}
|
|
400
346
|
export type SsrSiteNormalizedProps = SsrSiteProps & {
|
|
401
347
|
path: Exclude<SsrSiteProps["path"], undefined>;
|
package/constructs/SsrSite.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import url from "url";
|
|
3
3
|
import fs from "fs";
|
|
4
|
-
import
|
|
4
|
+
import { globSync } from "glob";
|
|
5
5
|
import crypto from "crypto";
|
|
6
6
|
import spawn from "cross-spawn";
|
|
7
7
|
import { execSync } from "child_process";
|
|
@@ -12,7 +12,6 @@ import { Effect, Role, Policy, PolicyStatement, AccountPrincipal, ServicePrincip
|
|
|
12
12
|
import { Function as CdkFunction, Code, Runtime, FunctionUrlAuthType, InvokeMode, } from "aws-cdk-lib/aws-lambda";
|
|
13
13
|
import { Asset } from "aws-cdk-lib/aws-s3-assets";
|
|
14
14
|
import { ViewerProtocolPolicy, AllowedMethods, CachedMethods, LambdaEdgeEventType, CachePolicy, CacheQueryStringBehavior, CacheHeaderBehavior, CacheCookieBehavior, OriginRequestPolicy, Function as CfFunction, FunctionCode as CfFunctionCode, FunctionEventType as CfFunctionEventType, } from "aws-cdk-lib/aws-cloudfront";
|
|
15
|
-
import { AwsCliLayer } from "aws-cdk-lib/lambda-layer-awscli";
|
|
16
15
|
import { S3Origin, HttpOrigin, OriginGroup, } from "aws-cdk-lib/aws-cloudfront-origins";
|
|
17
16
|
import { Rule, Schedule } from "aws-cdk-lib/aws-events";
|
|
18
17
|
import { LambdaFunction } from "aws-cdk-lib/aws-events-targets";
|
|
@@ -67,10 +66,11 @@ export class SsrSite extends Construct {
|
|
|
67
66
|
const app = scope.node.root;
|
|
68
67
|
const stack = Stack.of(this);
|
|
69
68
|
const self = this;
|
|
70
|
-
const { path: sitePath, typesPath, buildCommand, runtime, timeout, memorySize, edge, regional, dev,
|
|
69
|
+
const { path: sitePath, typesPath, buildCommand, runtime, timeout, memorySize, edge, regional, dev, assets, nodejs, permissions, environment, bind, customDomain, waitForInvalidation, warm, cdk, } = props;
|
|
71
70
|
this.doNotDeploy = !stack.isActive || (app.mode === "dev" && !dev?.deploy);
|
|
72
71
|
validateSiteExists();
|
|
73
72
|
validateTimeout();
|
|
73
|
+
validateDeprecatedFileOptions();
|
|
74
74
|
writeTypesFile(typesPath);
|
|
75
75
|
useSites().add(stack.stackName, id, this.constructor.name, props);
|
|
76
76
|
if (this.doNotDeploy) {
|
|
@@ -82,7 +82,6 @@ export class SsrSite extends Construct {
|
|
|
82
82
|
}
|
|
83
83
|
let s3DeployCRs = [];
|
|
84
84
|
let ssrFunctions = [];
|
|
85
|
-
let singletonAwsCliLayer;
|
|
86
85
|
let singletonUrlSigner;
|
|
87
86
|
let singletonCachePolicy;
|
|
88
87
|
let singletonOriginRequestPolicy;
|
|
@@ -110,7 +109,7 @@ export class SsrSite extends Construct {
|
|
|
110
109
|
app.registerTypes(this);
|
|
111
110
|
function validateSiteExists() {
|
|
112
111
|
if (!fs.existsSync(sitePath)) {
|
|
113
|
-
throw new
|
|
112
|
+
throw new VisibleError(`No site found at "${path.resolve(sitePath)}"`);
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
function validateTimeout() {
|
|
@@ -119,9 +118,15 @@ export class SsrSite extends Construct {
|
|
|
119
118
|
: toCdkDuration(timeout).toSeconds();
|
|
120
119
|
const limit = edge ? 30 : 180;
|
|
121
120
|
if (num > limit) {
|
|
122
|
-
throw new
|
|
123
|
-
? `
|
|
124
|
-
: `
|
|
121
|
+
throw new VisibleError(edge
|
|
122
|
+
? `In the "${id}" construct, timeout must be less than or equal to 30 seconds when the "edge" flag is enabled.`
|
|
123
|
+
: `In the "${id}" construct, timeout must be less than or equal to 180 seconds.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function validateDeprecatedFileOptions() {
|
|
127
|
+
// @ts-expect-error
|
|
128
|
+
if (props.fileOptions) {
|
|
129
|
+
throw new VisibleError(`In the "${id}" construct, the "fileOptions" property has been replaced by "assets.fileOptions". More details on upgrading - https://docs.sst.dev/upgrade-guide#upgrade-to-v2310`);
|
|
125
130
|
}
|
|
126
131
|
}
|
|
127
132
|
function writeTypesFile(typesPath) {
|
|
@@ -411,8 +416,7 @@ function handler(event) {
|
|
|
411
416
|
originPath: "/" + (props.originPath ?? ""),
|
|
412
417
|
});
|
|
413
418
|
const assets = createS3OriginAssets(props.copy);
|
|
414
|
-
const
|
|
415
|
-
const s3deployCR = createS3OriginDeployment(assets, assetFileOptions);
|
|
419
|
+
const s3deployCR = createS3OriginDeployment(props.copy, assets);
|
|
416
420
|
s3DeployCRs.push(s3deployCR);
|
|
417
421
|
return s3Origin;
|
|
418
422
|
}
|
|
@@ -562,185 +566,44 @@ function handler(event) {
|
|
|
562
566
|
}
|
|
563
567
|
return assets;
|
|
564
568
|
}
|
|
565
|
-
function
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
css: { mime: "text/css", isText: true },
|
|
585
|
-
js: { mime: "text/javascript", isText: true },
|
|
586
|
-
mjs: { mime: "text/javascript", isText: true },
|
|
587
|
-
apng: { mime: "image/apng", isText: false },
|
|
588
|
-
avif: { mime: "image/avif", isText: false },
|
|
589
|
-
gif: { mime: "image/gif", isText: false },
|
|
590
|
-
jpeg: { mime: "image/jpeg", isText: false },
|
|
591
|
-
jpg: { mime: "image/jpeg", isText: false },
|
|
592
|
-
png: { mime: "image/png", isText: false },
|
|
593
|
-
svg: { mime: "image/svg+xml", isText: true },
|
|
594
|
-
bmp: { mime: "image/bmp", isText: false },
|
|
595
|
-
tiff: { mime: "image/tiff", isText: false },
|
|
596
|
-
webp: { mime: "image/webp", isText: false },
|
|
597
|
-
ico: { mime: "image/vnd.microsoft.icon", isText: false },
|
|
598
|
-
eot: { mime: "application/vnd.ms-fontobject", isText: false },
|
|
599
|
-
ttf: { mime: "font/ttf", isText: false },
|
|
600
|
-
otf: { mime: "font/otf", isText: false },
|
|
601
|
-
woff: { mime: "font/woff", isText: false },
|
|
602
|
-
woff2: { mime: "font/woff2", isText: false },
|
|
603
|
-
json: { mime: "application/json", isText: true },
|
|
604
|
-
jsonld: { mime: "application/ld+json", isText: true },
|
|
605
|
-
xml: { mime: "application/xml", isText: true },
|
|
606
|
-
pdf: { mime: "application/pdf", isText: false },
|
|
607
|
-
zip: { mime: "application/zip", isText: false },
|
|
608
|
-
};
|
|
609
|
-
copy.forEach((files) => {
|
|
610
|
-
if (!files.cached)
|
|
611
|
-
return;
|
|
612
|
-
for (const [extension, contentType] of Object.entries(commonWebFileExtensions)) {
|
|
613
|
-
// Create a file option for: common extension + unversioned files
|
|
614
|
-
fileOptions.push({
|
|
615
|
-
filters: [
|
|
616
|
-
{ exclude: "*" },
|
|
617
|
-
{ include: `${path.posix.join(files.to, "*")}.${extension}` },
|
|
618
|
-
...(files.versionedSubDir
|
|
619
|
-
? [
|
|
620
|
-
{
|
|
621
|
-
exclude: path.posix.join(files.to, files.versionedSubDir, "*"),
|
|
622
|
-
},
|
|
623
|
-
]
|
|
624
|
-
: []),
|
|
625
|
-
],
|
|
626
|
-
cacheControl: nonVersionedFilesCacheHeader,
|
|
627
|
-
contentType: `${contentType.mime}${contentType.isText && textEncoding !== "none"
|
|
628
|
-
? `;charset=${textEncoding}`
|
|
629
|
-
: ""}`,
|
|
630
|
-
});
|
|
631
|
-
// Create a file option for: common extension + versioned files
|
|
632
|
-
if (files.versionedSubDir) {
|
|
633
|
-
fileOptions.push({
|
|
634
|
-
filters: [
|
|
635
|
-
{ exclude: "*" },
|
|
636
|
-
{
|
|
637
|
-
include: path.posix.join(files.to, files.versionedSubDir, `*.${extension}`),
|
|
638
|
-
},
|
|
639
|
-
],
|
|
640
|
-
cacheControl: versionedFilesCacheHeader,
|
|
641
|
-
contentType: `${contentType.mime}${contentType.isText && textEncoding !== "none"
|
|
642
|
-
? `;charset=${textEncoding}`
|
|
643
|
-
: ""}`,
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
// Create a file option for: other extensions + unversioned files
|
|
648
|
-
fileOptions.push({
|
|
649
|
-
filters: [
|
|
650
|
-
{ include: "*" },
|
|
651
|
-
...(files.versionedSubDir
|
|
652
|
-
? [
|
|
653
|
-
{
|
|
654
|
-
exclude: path.posix.join(files.to, files.versionedSubDir, "*"),
|
|
655
|
-
},
|
|
656
|
-
]
|
|
657
|
-
: []),
|
|
658
|
-
...Object.entries(commonWebFileExtensions).map(([ext]) => ({
|
|
659
|
-
exclude: `*.${ext}`,
|
|
660
|
-
})),
|
|
661
|
-
],
|
|
662
|
-
cacheControl: nonVersionedFilesCacheHeader,
|
|
663
|
-
});
|
|
664
|
-
// Create a file option for: other extensions + versioned files
|
|
665
|
-
if (files.versionedSubDir) {
|
|
666
|
-
fileOptions.push({
|
|
667
|
-
filters: [
|
|
668
|
-
{
|
|
669
|
-
include: path.posix.join(files.to, files.versionedSubDir, "*"),
|
|
670
|
-
},
|
|
671
|
-
...Object.entries(commonWebFileExtensions).map(([ext]) => ({
|
|
672
|
-
exclude: `*.${ext}`,
|
|
673
|
-
})),
|
|
674
|
-
],
|
|
675
|
-
cacheControl: versionedFilesCacheHeader,
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
if (cache?.fileOptions) {
|
|
680
|
-
fileOptions.push(...cache.fileOptions);
|
|
681
|
-
}
|
|
682
|
-
return fileOptions;
|
|
683
|
-
}
|
|
684
|
-
function createS3OriginDeployment(assets, fileOptions) {
|
|
685
|
-
// Create a Lambda function that will be doing the uploading
|
|
686
|
-
const uploader = new CdkFunction(self, "S3Uploader", {
|
|
687
|
-
code: Code.fromAsset(path.join(__dirname, "../support/base-site-custom-resource")),
|
|
688
|
-
layers: [useAwsCliLayer()],
|
|
689
|
-
runtime: Runtime.PYTHON_3_11,
|
|
690
|
-
handler: "s3-upload.handler",
|
|
691
|
-
timeout: CdkDuration.minutes(15),
|
|
692
|
-
memorySize: 1024,
|
|
693
|
-
});
|
|
694
|
-
bucket.grantReadWrite(uploader);
|
|
695
|
-
assets.forEach((asset) => asset.grantRead(uploader));
|
|
696
|
-
// Create the custom resource function
|
|
697
|
-
const handler = new CdkFunction(self, "S3Handler", {
|
|
698
|
-
code: Code.fromAsset(path.join(__dirname, "../support/base-site-custom-resource")),
|
|
699
|
-
layers: [useAwsCliLayer()],
|
|
700
|
-
runtime: Runtime.PYTHON_3_11,
|
|
701
|
-
handler: "s3-handler.handler",
|
|
702
|
-
timeout: CdkDuration.minutes(15),
|
|
703
|
-
memorySize: 1024,
|
|
704
|
-
environment: {
|
|
705
|
-
UPLOADER_FUNCTION_NAME: uploader.functionName,
|
|
706
|
-
},
|
|
569
|
+
function createS3OriginDeployment(copy, s3Assets) {
|
|
570
|
+
const policy = new Policy(self, "S3UploaderPolicy", {
|
|
571
|
+
statements: [
|
|
572
|
+
new PolicyStatement({
|
|
573
|
+
effect: Effect.ALLOW,
|
|
574
|
+
actions: ["lambda:InvokeFunction"],
|
|
575
|
+
resources: [stack.customResourceHandler.functionArn],
|
|
576
|
+
}),
|
|
577
|
+
new PolicyStatement({
|
|
578
|
+
effect: Effect.ALLOW,
|
|
579
|
+
actions: ["s3:ListBucket", "s3:PutObject", "s3:DeleteObject"],
|
|
580
|
+
resources: [bucket.bucketArn, `${bucket.bucketArn}/*`],
|
|
581
|
+
}),
|
|
582
|
+
new PolicyStatement({
|
|
583
|
+
effect: Effect.ALLOW,
|
|
584
|
+
actions: ["s3:GetObject"],
|
|
585
|
+
resources: [`${s3Assets[0].bucket.bucketArn}/*`],
|
|
586
|
+
}),
|
|
587
|
+
],
|
|
707
588
|
});
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
serviceToken: handler.functionArn,
|
|
713
|
-
resourceType: "Custom::SSTBucketDeployment",
|
|
589
|
+
stack.customResourceHandler.role?.attachInlinePolicy(policy);
|
|
590
|
+
const resource = new CustomResource(self, "S3Uploader", {
|
|
591
|
+
serviceToken: stack.customResourceHandler.functionArn,
|
|
592
|
+
resourceType: "Custom::S3Uploader",
|
|
714
593
|
properties: {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
594
|
+
sources: s3Assets.map((s3Asset) => ({
|
|
595
|
+
bucketName: s3Asset.s3BucketName,
|
|
596
|
+
objectKey: s3Asset.s3ObjectKey,
|
|
718
597
|
})),
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
...(typeof exclude === "string" ? [exclude] : exclude ?? [])
|
|
725
|
-
.map((entry) => ["--exclude", entry])
|
|
726
|
-
.flat(2),
|
|
727
|
-
...(typeof include === "string" ? [include] : include ?? [])
|
|
728
|
-
.map((entry) => ["--include", entry])
|
|
729
|
-
.flat(2),
|
|
730
|
-
...(filters || [])
|
|
731
|
-
.map((filter) => Object.entries(filter).map(([key, value]) => [
|
|
732
|
-
`--${key}`,
|
|
733
|
-
value,
|
|
734
|
-
]))
|
|
735
|
-
.flat(2),
|
|
736
|
-
cacheControl ? ["--cache-control", cacheControl] : [],
|
|
737
|
-
contentType ? ["--content-type", contentType] : [],
|
|
738
|
-
contentEncoding ? ["--content-encoding", contentEncoding] : [],
|
|
739
|
-
].flat();
|
|
740
|
-
}),
|
|
741
|
-
ReplaceValues: getS3ContentReplaceValues(),
|
|
598
|
+
destinationBucketName: bucket.bucketName,
|
|
599
|
+
concurrency: assets?._uploadConcurrency,
|
|
600
|
+
textEncoding: assets?.textEncoding ?? "utf-8",
|
|
601
|
+
fileOptions: getS3FileOptions(copy),
|
|
602
|
+
replaceValues: getS3ContentReplaceValues(),
|
|
742
603
|
},
|
|
743
604
|
});
|
|
605
|
+
resource.node.addDependency(policy);
|
|
606
|
+
return resource;
|
|
744
607
|
}
|
|
745
608
|
function useFunctionUrlSigningFunction() {
|
|
746
609
|
singletonUrlSigner =
|
|
@@ -768,10 +631,40 @@ function handler(event) {
|
|
|
768
631
|
OriginRequestPolicy.fromOriginRequestPolicyId(self, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
|
|
769
632
|
return singletonOriginRequestPolicy;
|
|
770
633
|
}
|
|
771
|
-
function
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
634
|
+
function getS3FileOptions(copy) {
|
|
635
|
+
const fileOptions = [];
|
|
636
|
+
const nonVersionedFilesTTL = typeof assets?.nonVersionedFilesTTL === "number"
|
|
637
|
+
? assets.nonVersionedFilesTTL
|
|
638
|
+
: toCdkDuration(assets?.nonVersionedFilesTTL ?? "1 day").toSeconds();
|
|
639
|
+
const staleWhileRevalidateTTL = Math.max(Math.floor(nonVersionedFilesTTL / 10), 30);
|
|
640
|
+
const versionedFilesTTL = typeof assets?.versionedFilesTTL === "number"
|
|
641
|
+
? assets.versionedFilesTTL
|
|
642
|
+
: toCdkDuration(assets?.versionedFilesTTL ?? "365 days").toSeconds();
|
|
643
|
+
copy.forEach(({ cached, to, versionedSubDir }) => {
|
|
644
|
+
if (!cached)
|
|
645
|
+
return;
|
|
646
|
+
// Create a default file option for: unversioned files
|
|
647
|
+
fileOptions.push({
|
|
648
|
+
files: "**",
|
|
649
|
+
ignore: versionedSubDir
|
|
650
|
+
? path.posix.join(to, versionedSubDir, "**")
|
|
651
|
+
: undefined,
|
|
652
|
+
cacheControl: assets?.nonVersionedFilesCacheHeader ??
|
|
653
|
+
`public,max-age=0,s-maxage=${nonVersionedFilesTTL},stale-while-revalidate=${staleWhileRevalidateTTL}`,
|
|
654
|
+
});
|
|
655
|
+
// Create a default file option for: versioned files
|
|
656
|
+
if (versionedSubDir) {
|
|
657
|
+
fileOptions.push({
|
|
658
|
+
files: path.posix.join(to, versionedSubDir, "**"),
|
|
659
|
+
cacheControl: assets?.versionedFilesCacheHeader ??
|
|
660
|
+
`public,max-age=${versionedFilesTTL},immutable`,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
if (assets?.fileOptions) {
|
|
665
|
+
fileOptions.push(...assets.fileOptions);
|
|
666
|
+
}
|
|
667
|
+
return fileOptions;
|
|
775
668
|
}
|
|
776
669
|
function getS3ContentReplaceValues() {
|
|
777
670
|
const replaceValues = [];
|
|
@@ -796,7 +689,7 @@ function handler(event) {
|
|
|
796
689
|
return replaceValues;
|
|
797
690
|
}
|
|
798
691
|
function createDistributionInvalidation() {
|
|
799
|
-
const cdnInvalidationStrategy =
|
|
692
|
+
const cdnInvalidationStrategy = assets?.cdnInvalidationStrategy ?? "all";
|
|
800
693
|
if (cdnInvalidationStrategy === "never")
|
|
801
694
|
return;
|
|
802
695
|
if (plan.buildId) {
|
|
@@ -833,31 +726,26 @@ function handler(event) {
|
|
|
833
726
|
// The below options are needed to support following symlinks when building zip files:
|
|
834
727
|
// - nodir: This will prevent symlinks themselves from being copied into the zip.
|
|
835
728
|
// - follow: This will follow symlinks and copy the files within.
|
|
836
|
-
const globOptions = {
|
|
837
|
-
dot: true,
|
|
838
|
-
nodir: true,
|
|
839
|
-
follow: true,
|
|
840
|
-
cwd: path.resolve(sitePath, item.from),
|
|
841
|
-
};
|
|
842
729
|
// For versioned files, use file path for digest since file version in name should change on content change
|
|
843
730
|
if (item.versionedSubDir) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
731
|
+
globSync("**", {
|
|
732
|
+
dot: true,
|
|
733
|
+
nodir: true,
|
|
734
|
+
follow: true,
|
|
847
735
|
cwd: path.resolve(sitePath, item.from, item.versionedSubDir),
|
|
848
|
-
})
|
|
849
|
-
.forEach((filePath) => hash.update(filePath));
|
|
736
|
+
}).forEach((filePath) => hash.update(filePath));
|
|
850
737
|
}
|
|
851
738
|
// For non-versioned files, use file content for digest
|
|
852
739
|
if (cdnInvalidationStrategy === "all") {
|
|
853
|
-
|
|
854
|
-
.sync("**", {
|
|
855
|
-
...globOptions,
|
|
740
|
+
globSync("**", {
|
|
856
741
|
ignore: item.versionedSubDir
|
|
857
742
|
? [path.posix.join(item.versionedSubDir, "**")]
|
|
858
743
|
: undefined,
|
|
859
|
-
|
|
860
|
-
|
|
744
|
+
dot: true,
|
|
745
|
+
nodir: true,
|
|
746
|
+
follow: true,
|
|
747
|
+
cwd: path.resolve(sitePath, item.from),
|
|
748
|
+
}).forEach((filePath) => hash.update(fs.readFileSync(path.resolve(sitePath, item.from, filePath))));
|
|
861
749
|
}
|
|
862
750
|
});
|
|
863
751
|
const buildId = hash.digest("hex");
|