sst 2.24.22 → 2.24.24
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/Function.d.ts +13 -0
- package/constructs/Function.js +3 -0
- package/constructs/Job.d.ts +13 -0
- package/constructs/Job.js +1 -0
- package/constructs/NextjsSite.d.ts +1 -1
- package/constructs/NextjsSite.js +8 -10
- package/constructs/RemixSite.d.ts +1 -0
- package/constructs/RemixSite.js +5 -0
- package/constructs/Service.d.ts +75 -8
- package/constructs/Service.js +28 -8
- package/constructs/SsrSite.d.ts +16 -9
- package/constructs/SsrSite.js +98 -69
- package/package.json +1 -1
- package/runtime/handlers/container.js +2 -0
- package/runtime/handlers/java.js +11 -15
package/constructs/Function.d.ts
CHANGED
|
@@ -566,6 +566,19 @@ export interface ContainerProps {
|
|
|
566
566
|
* ```
|
|
567
567
|
*/
|
|
568
568
|
file?: string;
|
|
569
|
+
/**
|
|
570
|
+
* Build args to pass to the docker build command.
|
|
571
|
+
* @default No build args
|
|
572
|
+
* @example
|
|
573
|
+
* ```js
|
|
574
|
+
* container: {
|
|
575
|
+
* buildArgs: {
|
|
576
|
+
* FOO: "bar"
|
|
577
|
+
* }
|
|
578
|
+
* }
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
buildArgs?: Record<string, string>;
|
|
569
582
|
}
|
|
570
583
|
/**
|
|
571
584
|
* Used to configure additional files to copy into the function bundle
|
package/constructs/Function.js
CHANGED
|
@@ -206,6 +206,9 @@ export class Function extends CDKFunction {
|
|
|
206
206
|
...(props.container?.file
|
|
207
207
|
? { file: props.container.file }
|
|
208
208
|
: {}),
|
|
209
|
+
...(props.container?.buildArgs
|
|
210
|
+
? { buildArgs: props.container.buildArgs }
|
|
211
|
+
: {}),
|
|
209
212
|
exclude: [".sst"],
|
|
210
213
|
ignoreMode: IgnoreMode.GLOB,
|
|
211
214
|
}),
|
package/constructs/Job.d.ts
CHANGED
|
@@ -31,6 +31,19 @@ export interface JobContainerProps {
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
file?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Build args to pass to the docker build command.
|
|
36
|
+
* @default No build args
|
|
37
|
+
* @example
|
|
38
|
+
* ```js
|
|
39
|
+
* container: {
|
|
40
|
+
* buildArgs: {
|
|
41
|
+
* FOO: "bar"
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
buildArgs?: Record<string, string>;
|
|
34
47
|
}
|
|
35
48
|
export interface JobProps {
|
|
36
49
|
/**
|
package/constructs/Job.js
CHANGED
|
@@ -63,7 +63,7 @@ export declare class NextjsSite extends SsrSite {
|
|
|
63
63
|
private createWarmer;
|
|
64
64
|
protected createCloudFrontDistributionForRegional(): Distribution;
|
|
65
65
|
protected createCloudFrontDistributionForEdge(): Distribution;
|
|
66
|
-
protected
|
|
66
|
+
protected useServerBehaviorCachePolicy(): CachePolicy;
|
|
67
67
|
private buildImageBehavior;
|
|
68
68
|
protected generateBuildId(): string;
|
|
69
69
|
getConstructMetadata(): {
|
package/constructs/NextjsSite.js
CHANGED
|
@@ -255,8 +255,7 @@ export class NextjsSite extends SsrSite {
|
|
|
255
255
|
*/
|
|
256
256
|
const { customDomain, cdk } = this.props;
|
|
257
257
|
const cfDistributionProps = cdk?.distribution || {};
|
|
258
|
-
const
|
|
259
|
-
const serverBehavior = this.buildDefaultBehaviorForRegional(cachePolicy);
|
|
258
|
+
const serverBehavior = this.buildDefaultBehaviorForRegional();
|
|
260
259
|
return new Distribution(this, "CDN", {
|
|
261
260
|
scopeOverride: this,
|
|
262
261
|
customDomain,
|
|
@@ -271,7 +270,7 @@ export class NextjsSite extends SsrSite {
|
|
|
271
270
|
additionalBehaviors: {
|
|
272
271
|
"api/*": serverBehavior,
|
|
273
272
|
"_next/data/*": serverBehavior,
|
|
274
|
-
"_next/image*": this.buildImageBehavior(
|
|
273
|
+
"_next/image*": this.buildImageBehavior(),
|
|
275
274
|
...(cfDistributionProps.additionalBehaviors || {}),
|
|
276
275
|
},
|
|
277
276
|
},
|
|
@@ -281,8 +280,7 @@ export class NextjsSite extends SsrSite {
|
|
|
281
280
|
createCloudFrontDistributionForEdge() {
|
|
282
281
|
const { customDomain, cdk } = this.props;
|
|
283
282
|
const cfDistributionProps = cdk?.distribution || {};
|
|
284
|
-
const
|
|
285
|
-
const serverBehavior = this.buildDefaultBehaviorForEdge(cachePolicy);
|
|
283
|
+
const serverBehavior = this.buildDefaultBehaviorForEdge();
|
|
286
284
|
return new Distribution(this, "CDN", {
|
|
287
285
|
scopeOverride: this,
|
|
288
286
|
customDomain,
|
|
@@ -297,15 +295,15 @@ export class NextjsSite extends SsrSite {
|
|
|
297
295
|
additionalBehaviors: {
|
|
298
296
|
"api/*": serverBehavior,
|
|
299
297
|
"_next/data/*": serverBehavior,
|
|
300
|
-
"_next/image*": this.buildImageBehavior(
|
|
298
|
+
"_next/image*": this.buildImageBehavior(),
|
|
301
299
|
...(cfDistributionProps.additionalBehaviors || {}),
|
|
302
300
|
},
|
|
303
301
|
},
|
|
304
302
|
},
|
|
305
303
|
});
|
|
306
304
|
}
|
|
307
|
-
|
|
308
|
-
return super.
|
|
305
|
+
useServerBehaviorCachePolicy() {
|
|
306
|
+
return super.useServerBehaviorCachePolicy([
|
|
309
307
|
"accept",
|
|
310
308
|
"rsc",
|
|
311
309
|
"next-router-prefetch",
|
|
@@ -313,7 +311,7 @@ export class NextjsSite extends SsrSite {
|
|
|
313
311
|
"next-url",
|
|
314
312
|
]);
|
|
315
313
|
}
|
|
316
|
-
buildImageBehavior(
|
|
314
|
+
buildImageBehavior() {
|
|
317
315
|
const { cdk, regional } = this.props;
|
|
318
316
|
const imageFn = this.createImageOptimizationFunction();
|
|
319
317
|
const imageFnUrl = imageFn.addFunctionUrl({
|
|
@@ -327,7 +325,7 @@ export class NextjsSite extends SsrSite {
|
|
|
327
325
|
allowedMethods: AllowedMethods.ALLOW_ALL,
|
|
328
326
|
cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
|
|
329
327
|
compress: true,
|
|
330
|
-
cachePolicy,
|
|
328
|
+
cachePolicy: cdk?.serverCachePolicy ?? this.useServerBehaviorCachePolicy(),
|
|
331
329
|
responseHeadersPolicy: cdk?.responseHeadersPolicy,
|
|
332
330
|
edgeLambdas: regional?.enableServerUrlIamAuth
|
|
333
331
|
? [
|
|
@@ -20,6 +20,7 @@ export declare class RemixSite extends SsrSite {
|
|
|
20
20
|
serverBuildOutputFile: string;
|
|
21
21
|
clientBuildOutputDir: string;
|
|
22
22
|
clientBuildVersionedSubDir: string;
|
|
23
|
+
clientCFFunctionInjection: string;
|
|
23
24
|
};
|
|
24
25
|
private createServerLambdaBundle;
|
|
25
26
|
protected createFunctionForRegional(): SsrFunction;
|
package/constructs/RemixSite.js
CHANGED
|
@@ -53,6 +53,11 @@ export class RemixSite extends SsrSite {
|
|
|
53
53
|
serverBuildOutputFile: "build/index.js",
|
|
54
54
|
clientBuildOutputDir: "public",
|
|
55
55
|
clientBuildVersionedSubDir: "build",
|
|
56
|
+
// Note: When using libraries like remix-flat-routes the file can
|
|
57
|
+
// contains special characters like "+". It needs to be encoded.
|
|
58
|
+
clientCFFunctionInjection: `
|
|
59
|
+
request.uri = request.uri.split('/').map(encodeURIComponent).join('/');
|
|
60
|
+
`,
|
|
56
61
|
};
|
|
57
62
|
}
|
|
58
63
|
createServerLambdaBundle(wrapperFile) {
|
package/constructs/Service.d.ts
CHANGED
|
@@ -4,8 +4,9 @@ import { SSTConstruct } from "./Construct.js";
|
|
|
4
4
|
import { Permissions } from "./util/permission.js";
|
|
5
5
|
import { FunctionBindingProps } from "./util/functionBinding.js";
|
|
6
6
|
import { IVpc } from "aws-cdk-lib/aws-ec2";
|
|
7
|
-
import { Cluster, ContainerDefinitionOptions } from "aws-cdk-lib/aws-ecs";
|
|
7
|
+
import { Cluster, ContainerDefinitionOptions, CpuArchitecture, FargateServiceProps } from "aws-cdk-lib/aws-ecs";
|
|
8
8
|
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
9
|
+
import { ApplicationTargetGroupProps } from "aws-cdk-lib/aws-elasticloadbalancingv2";
|
|
9
10
|
declare const supportedCpus: {
|
|
10
11
|
"0.25 vCPU": number;
|
|
11
12
|
"0.5 vCPU": number;
|
|
@@ -29,7 +30,18 @@ export interface ServiceProps {
|
|
|
29
30
|
*/
|
|
30
31
|
file?: string;
|
|
31
32
|
/**
|
|
32
|
-
* The
|
|
33
|
+
* The CPU architecture of the container.
|
|
34
|
+
* @default "x86_64"
|
|
35
|
+
* @example
|
|
36
|
+
* ```js
|
|
37
|
+
* {
|
|
38
|
+
* architecture: "arm64",
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
architecture?: Lowercase<keyof Pick<typeof CpuArchitecture, "ARM64" | "X86_64">>;
|
|
43
|
+
/**
|
|
44
|
+
* The amount of CPU allocated.
|
|
33
45
|
* @default "0.25 vCPU"
|
|
34
46
|
* @example
|
|
35
47
|
* ```js
|
|
@@ -214,6 +226,23 @@ export interface ServiceProps {
|
|
|
214
226
|
* ```
|
|
215
227
|
*/
|
|
216
228
|
waitForInvalidation?: boolean;
|
|
229
|
+
build?: {
|
|
230
|
+
/**
|
|
231
|
+
* Build args to pass to the docker build command.
|
|
232
|
+
* @default No build args
|
|
233
|
+
* @example
|
|
234
|
+
* ```js
|
|
235
|
+
* {
|
|
236
|
+
* build: {
|
|
237
|
+
* buildArgs: {
|
|
238
|
+
* FOO: "bar"
|
|
239
|
+
* }
|
|
240
|
+
* }
|
|
241
|
+
* }
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
buildArgs?: Record<string, string>;
|
|
245
|
+
};
|
|
217
246
|
dev?: {
|
|
218
247
|
/**
|
|
219
248
|
* When running `sst dev, site is not deployed. This is to ensure `sst dev` can start up quickly.
|
|
@@ -248,7 +277,9 @@ export interface ServiceProps {
|
|
|
248
277
|
* @example
|
|
249
278
|
* ```js
|
|
250
279
|
* {
|
|
251
|
-
*
|
|
280
|
+
* cdk: {
|
|
281
|
+
* cloudfrontDistribution: false
|
|
282
|
+
* }
|
|
252
283
|
* }
|
|
253
284
|
* ```
|
|
254
285
|
*/
|
|
@@ -259,11 +290,44 @@ export interface ServiceProps {
|
|
|
259
290
|
* @example
|
|
260
291
|
* ```js
|
|
261
292
|
* {
|
|
262
|
-
*
|
|
293
|
+
* cdk: {
|
|
294
|
+
* applicationLoadBalancer: false
|
|
295
|
+
* }
|
|
263
296
|
* }
|
|
264
297
|
* ```
|
|
265
298
|
*/
|
|
266
299
|
applicationLoadBalancer?: boolean;
|
|
300
|
+
/**
|
|
301
|
+
* Customize the Application Load Balancer's target group.
|
|
302
|
+
* @default true
|
|
303
|
+
* @example
|
|
304
|
+
* ```js
|
|
305
|
+
* {
|
|
306
|
+
* cdk: {
|
|
307
|
+
* applicationLoadBalancerTargetGroup: {
|
|
308
|
+
* healthCheck: {
|
|
309
|
+
* path: "/health"
|
|
310
|
+
* }
|
|
311
|
+
* }
|
|
312
|
+
* }
|
|
313
|
+
* }
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
applicationLoadBalancerTargetGroup?: ApplicationTargetGroupProps;
|
|
317
|
+
/**
|
|
318
|
+
* Customize the Fargate Service.
|
|
319
|
+
* @example
|
|
320
|
+
* ```js
|
|
321
|
+
* {
|
|
322
|
+
* cdk: {
|
|
323
|
+
* fargateService: {
|
|
324
|
+
* circuitBreaker: { rollback: true }
|
|
325
|
+
* }
|
|
326
|
+
* }
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
fargateService?: Omit<FargateServiceProps, "cluster" | "taskDefinition">;
|
|
267
331
|
/**
|
|
268
332
|
* Customizing the container definition for the ECS task.
|
|
269
333
|
* @example
|
|
@@ -272,14 +336,16 @@ export interface ServiceProps {
|
|
|
272
336
|
* cdk: {
|
|
273
337
|
* container: {
|
|
274
338
|
* healthCheck: {
|
|
275
|
-
* command: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
|
|
276
|
-
* }
|
|
339
|
+
* command: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
|
|
340
|
+
* }
|
|
277
341
|
* }
|
|
278
342
|
* }
|
|
279
343
|
* }
|
|
280
344
|
* ```
|
|
281
345
|
*/
|
|
282
|
-
container?: Omit<ContainerDefinitionOptions, "image"
|
|
346
|
+
container?: Omit<ContainerDefinitionOptions, "image"> & {
|
|
347
|
+
image?: ContainerDefinitionOptions["image"];
|
|
348
|
+
};
|
|
283
349
|
/**
|
|
284
350
|
* Runs codebuild job in the specified VPC. Note this will only work once deployed.
|
|
285
351
|
*
|
|
@@ -300,8 +366,9 @@ export interface ServiceProps {
|
|
|
300
366
|
};
|
|
301
367
|
}
|
|
302
368
|
type ServiceNormalizedProps = ServiceProps & {
|
|
303
|
-
|
|
369
|
+
architecture: Exclude<ServiceProps["architecture"], undefined>;
|
|
304
370
|
cpu: Exclude<ServiceProps["cpu"], undefined>;
|
|
371
|
+
path: Exclude<ServiceProps["path"], undefined>;
|
|
305
372
|
memory: Exclude<ServiceProps["memory"], undefined>;
|
|
306
373
|
port: Exclude<ServiceProps["port"], undefined>;
|
|
307
374
|
logRetention: Exclude<ServiceProps["logRetention"], undefined>;
|
package/constructs/Service.js
CHANGED
|
@@ -19,7 +19,7 @@ import { attachPermissionsToRole } from "./util/permission.js";
|
|
|
19
19
|
import { bindEnvironment, bindPermissions, getParameterPath, getReferencedSecrets, } from "./util/functionBinding.js";
|
|
20
20
|
import { useProject } from "../project.js";
|
|
21
21
|
import { Vpc, } from "aws-cdk-lib/aws-ec2";
|
|
22
|
-
import { AwsLogDriver, Cluster,
|
|
22
|
+
import { AwsLogDriver, Cluster, ContainerImage, CpuArchitecture, FargateService, FargateTaskDefinition, } from "aws-cdk-lib/aws-ecs";
|
|
23
23
|
import { LogGroup, LogRetention, RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
24
24
|
import { Platform } from "aws-cdk-lib/aws-ecr-assets";
|
|
25
25
|
import { ApplicationLoadBalancer, } from "aws-cdk-lib/aws-elasticloadbalancingv2";
|
|
@@ -153,6 +153,7 @@ export class Service extends Construct {
|
|
|
153
153
|
this.id = id;
|
|
154
154
|
this.props = {
|
|
155
155
|
path: ".",
|
|
156
|
+
architecture: props?.architecture || "x86_64",
|
|
156
157
|
cpu: props?.cpu || "0.25 vCPU",
|
|
157
158
|
memory: props?.memory || "0.5 GB",
|
|
158
159
|
port: props?.port || 3000,
|
|
@@ -188,16 +189,19 @@ export class Service extends Construct {
|
|
|
188
189
|
this.attachPermissionsForService(props?.permissions || []);
|
|
189
190
|
Object.entries(props?.environment || {}).map(([key, value]) => this.addEnvironmentForService(key, value));
|
|
190
191
|
useDeferredTasks().add(async () => {
|
|
191
|
-
if (!app.isRunningSSTTest()) {
|
|
192
|
+
if (!app.isRunningSSTTest() && !props?.cdk?.container?.image) {
|
|
192
193
|
Colors.line(`➜ Building the container image for the "${this.node.id}" service...`);
|
|
193
194
|
// Build app
|
|
194
195
|
let dockerfile;
|
|
196
|
+
// case: custom Dockerfile provided
|
|
195
197
|
if (this.props.file) {
|
|
196
198
|
dockerfile = this.props.file;
|
|
197
199
|
}
|
|
200
|
+
// case: default Dockerfile found
|
|
198
201
|
else if (await existsAsync(path.join(this.props.path, "Dockerfile"))) {
|
|
199
202
|
dockerfile = "Dockerfile";
|
|
200
203
|
}
|
|
204
|
+
// case: nixpack
|
|
201
205
|
else {
|
|
202
206
|
await this.createNixpacksBuilder();
|
|
203
207
|
dockerfile = await this.runNixpacksBuild();
|
|
@@ -384,7 +388,7 @@ export class Service extends Construct {
|
|
|
384
388
|
}));
|
|
385
389
|
}
|
|
386
390
|
createService(vpc) {
|
|
387
|
-
const { cpu, memory, port, logRetention, cdk } = this.props;
|
|
391
|
+
const { architecture, cpu, memory, port, logRetention, cdk } = this.props;
|
|
388
392
|
const app = this.node.root;
|
|
389
393
|
const clusterName = app.logicalPrefixedName(this.node.id);
|
|
390
394
|
const logGroup = new LogRetention(this, "LogRetention", {
|
|
@@ -402,6 +406,11 @@ export class Service extends Construct {
|
|
|
402
406
|
// @ts-ignore
|
|
403
407
|
memoryLimitMiB: supportedMemories[cpu][memory],
|
|
404
408
|
cpu: supportedCpus[cpu],
|
|
409
|
+
runtimePlatform: {
|
|
410
|
+
cpuArchitecture: architecture === "arm64"
|
|
411
|
+
? CpuArchitecture.ARM64
|
|
412
|
+
: CpuArchitecture.X86_64,
|
|
413
|
+
},
|
|
405
414
|
});
|
|
406
415
|
const container = taskDefinition.addContainer("Container", {
|
|
407
416
|
logging: new AwsLogDriver({
|
|
@@ -415,11 +424,14 @@ export class Service extends Construct {
|
|
|
415
424
|
SST_SSM_PREFIX: useProject().config.ssmPrefix,
|
|
416
425
|
},
|
|
417
426
|
...cdk?.container,
|
|
418
|
-
image:
|
|
427
|
+
image: cdk?.container?.image ?? {
|
|
428
|
+
bind: () => ({ imageName: "placeholder" }),
|
|
429
|
+
},
|
|
419
430
|
});
|
|
420
431
|
const service = new FargateService(this, "Service", {
|
|
421
432
|
cluster,
|
|
422
433
|
taskDefinition,
|
|
434
|
+
...cdk?.fargateService,
|
|
423
435
|
});
|
|
424
436
|
return { cluster, taskDefinition, container, service };
|
|
425
437
|
}
|
|
@@ -427,6 +439,8 @@ export class Service extends Construct {
|
|
|
427
439
|
const { cdk } = this.props;
|
|
428
440
|
// Do not create load balancer if disabled
|
|
429
441
|
if (cdk?.applicationLoadBalancer === false) {
|
|
442
|
+
if (cdk?.applicationLoadBalancerTargetGroup)
|
|
443
|
+
throw new VisibleError(`In the "${this.node.id}" Service, the "cdk.applicationLoadBalancerTargetGroup" cannot be applied if the Application Load Balancer is diabled.`);
|
|
430
444
|
return {};
|
|
431
445
|
}
|
|
432
446
|
const alb = new ApplicationLoadBalancer(this, "LoadBalancer", {
|
|
@@ -437,6 +451,7 @@ export class Service extends Construct {
|
|
|
437
451
|
const target = listener.addTargets("TargetGroup", {
|
|
438
452
|
port: 80,
|
|
439
453
|
targets: [service],
|
|
454
|
+
...cdk?.applicationLoadBalancerTargetGroup,
|
|
440
455
|
});
|
|
441
456
|
return { alb, target };
|
|
442
457
|
}
|
|
@@ -592,13 +607,16 @@ export class Service extends Construct {
|
|
|
592
607
|
return ".nixpacks/Dockerfile";
|
|
593
608
|
}
|
|
594
609
|
async runDockerBuild(dockerfile) {
|
|
610
|
+
const { path: servicePath, architecture, build } = this.props;
|
|
611
|
+
const platform = architecture === "arm64" ? "linux/arm64" : "linux/amd64";
|
|
595
612
|
try {
|
|
596
613
|
await execAsync([
|
|
597
614
|
"docker",
|
|
598
615
|
"build",
|
|
599
616
|
`-t sst-build:service-${this.node.id}`,
|
|
600
|
-
|
|
601
|
-
`-f ${path.join(
|
|
617
|
+
`--platform ${platform}`,
|
|
618
|
+
`-f ${path.join(servicePath, dockerfile)}`,
|
|
619
|
+
...Object.entries(build?.buildArgs || {}).map(([k, v]) => `--build-arg ${k}=${v}`),
|
|
602
620
|
this.props.path,
|
|
603
621
|
].join(" "), {
|
|
604
622
|
env: {
|
|
@@ -612,9 +630,11 @@ export class Service extends Construct {
|
|
|
612
630
|
}
|
|
613
631
|
}
|
|
614
632
|
updateContainerImage(dockerfile, taskDefinition, container) {
|
|
615
|
-
const
|
|
616
|
-
|
|
633
|
+
const { path: servicePath, architecture, build } = this.props;
|
|
634
|
+
const image = ContainerImage.fromAsset(servicePath, {
|
|
635
|
+
platform: architecture === "arm64" ? Platform.LINUX_ARM64 : Platform.LINUX_AMD64,
|
|
617
636
|
file: dockerfile,
|
|
637
|
+
buildArgs: build?.buildArgs,
|
|
618
638
|
exclude: [".sst"],
|
|
619
639
|
ignoreMode: IgnoreMode.GLOB,
|
|
620
640
|
});
|
package/constructs/SsrSite.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type SsrBuildConfig = {
|
|
|
19
19
|
clientBuildOutputDir: string;
|
|
20
20
|
clientBuildVersionedSubDir: string;
|
|
21
21
|
clientBuildS3KeyPrefix?: string;
|
|
22
|
+
clientCFFunctionInjection?: string;
|
|
22
23
|
prerenderedBuildOutputDir?: string;
|
|
23
24
|
prerenderedBuildS3KeyPrefix?: string;
|
|
24
25
|
};
|
|
@@ -284,7 +285,10 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
284
285
|
private serverLambdaForDev?;
|
|
285
286
|
private serverUrlSigningFunction?;
|
|
286
287
|
protected bucket: Bucket;
|
|
287
|
-
private
|
|
288
|
+
private serverCfFunction?;
|
|
289
|
+
private serverBehaviorCachePolicy?;
|
|
290
|
+
private serverBehaviorOriginRequestPolicy?;
|
|
291
|
+
private staticCfFunction?;
|
|
288
292
|
private s3Origin;
|
|
289
293
|
private distribution;
|
|
290
294
|
constructor(scope: Construct, id: string, props?: SsrSiteProps);
|
|
@@ -347,19 +351,22 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
347
351
|
private grantServerS3Permissions;
|
|
348
352
|
private grantServerCloudFrontPermissions;
|
|
349
353
|
private createCloudFrontS3Origin;
|
|
350
|
-
private createCloudFrontFunction;
|
|
351
354
|
protected createCloudFrontDistributionForRegional(): Distribution;
|
|
352
355
|
protected createCloudFrontDistributionForEdge(): Distribution;
|
|
353
|
-
protected buildDefaultBehaviorForRegional(
|
|
354
|
-
protected
|
|
355
|
-
protected
|
|
356
|
-
protected
|
|
356
|
+
protected buildDefaultBehaviorForRegional(): BehaviorOptions;
|
|
357
|
+
protected buildDefaultBehaviorForEdge(): BehaviorOptions;
|
|
358
|
+
protected addStaticFileBehaviors(): void;
|
|
359
|
+
protected useServerBehaviorFunctionAssociations(): {
|
|
357
360
|
eventType: CfFunctionEventType;
|
|
358
361
|
function: CfFunction;
|
|
359
362
|
}[];
|
|
360
|
-
protected
|
|
361
|
-
|
|
362
|
-
|
|
363
|
+
protected useStaticBehaviorFunctionAssociations(): {
|
|
364
|
+
eventType: CfFunctionEventType;
|
|
365
|
+
function: CfFunction;
|
|
366
|
+
}[];
|
|
367
|
+
protected useServerUrlSigningFunction(): EdgeFunction;
|
|
368
|
+
protected useServerBehaviorCachePolicy(allowedHeaders?: string[]): CachePolicy;
|
|
369
|
+
private useServerBehaviorOriginRequestPolicy;
|
|
363
370
|
private getS3ContentReplaceValues;
|
|
364
371
|
private validateSiteExists;
|
|
365
372
|
private validateTimeout;
|
package/constructs/SsrSite.js
CHANGED
|
@@ -51,7 +51,10 @@ export class SsrSite extends Construct {
|
|
|
51
51
|
serverLambdaForDev;
|
|
52
52
|
serverUrlSigningFunction;
|
|
53
53
|
bucket;
|
|
54
|
-
|
|
54
|
+
serverCfFunction;
|
|
55
|
+
serverBehaviorCachePolicy;
|
|
56
|
+
serverBehaviorOriginRequestPolicy;
|
|
57
|
+
staticCfFunction;
|
|
55
58
|
s3Origin;
|
|
56
59
|
distribution;
|
|
57
60
|
constructor(scope, id, props) {
|
|
@@ -75,8 +78,8 @@ export class SsrSite extends Construct {
|
|
|
75
78
|
this.writeTypesFile();
|
|
76
79
|
useSites().add(stack.stackName, id, this.constructor.name, this.props);
|
|
77
80
|
if (this.doNotDeploy) {
|
|
78
|
-
// @ts-
|
|
79
|
-
this.
|
|
81
|
+
// @ts-expect-error
|
|
82
|
+
this.bucket = this.s3Origin = this.distribution = null;
|
|
80
83
|
this.serverLambdaForDev = this.createFunctionForDev();
|
|
81
84
|
return;
|
|
82
85
|
}
|
|
@@ -92,7 +95,6 @@ export class SsrSite extends Construct {
|
|
|
92
95
|
this.grantServerS3Permissions();
|
|
93
96
|
// Create CloudFront
|
|
94
97
|
this.s3Origin = this.createCloudFrontS3Origin();
|
|
95
|
-
this.cfFunction = this.createCloudFrontFunction();
|
|
96
98
|
this.distribution = this.props.edge
|
|
97
99
|
? this.createCloudFrontDistributionForEdge()
|
|
98
100
|
: this.createCloudFrontDistributionForRegional();
|
|
@@ -486,21 +488,9 @@ export class SsrSite extends Construct {
|
|
|
486
488
|
originPath: "/" + (this.buildConfig.clientBuildS3KeyPrefix ?? ""),
|
|
487
489
|
});
|
|
488
490
|
}
|
|
489
|
-
createCloudFrontFunction() {
|
|
490
|
-
return new CfFunction(this, "CloudFrontFunction", {
|
|
491
|
-
code: CfFunctionCode.fromInline(`
|
|
492
|
-
function handler(event) {
|
|
493
|
-
var request = event.request;
|
|
494
|
-
request.headers["x-forwarded-host"] = request.headers.host;
|
|
495
|
-
${this.buildConfig.serverCFFunctionInjection || ""}
|
|
496
|
-
return request;
|
|
497
|
-
}`),
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
491
|
createCloudFrontDistributionForRegional() {
|
|
501
492
|
const { customDomain, cdk } = this.props;
|
|
502
493
|
const cfDistributionProps = cdk?.distribution || {};
|
|
503
|
-
const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
|
|
504
494
|
return new Distribution(this, "CDN", {
|
|
505
495
|
scopeOverride: this,
|
|
506
496
|
customDomain,
|
|
@@ -511,7 +501,7 @@ function handler(event) {
|
|
|
511
501
|
// Override props.
|
|
512
502
|
...cfDistributionProps,
|
|
513
503
|
// these values can NOT be overwritten by cfDistributionProps
|
|
514
|
-
defaultBehavior: this.buildDefaultBehaviorForRegional(
|
|
504
|
+
defaultBehavior: this.buildDefaultBehaviorForRegional(),
|
|
515
505
|
additionalBehaviors: {
|
|
516
506
|
...(cfDistributionProps.additionalBehaviors || {}),
|
|
517
507
|
},
|
|
@@ -522,7 +512,6 @@ function handler(event) {
|
|
|
522
512
|
createCloudFrontDistributionForEdge() {
|
|
523
513
|
const { customDomain, cdk } = this.props;
|
|
524
514
|
const cfDistributionProps = cdk?.distribution || {};
|
|
525
|
-
const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
|
|
526
515
|
return new Distribution(this, "CDN", {
|
|
527
516
|
scopeOverride: this,
|
|
528
517
|
customDomain,
|
|
@@ -533,7 +522,7 @@ function handler(event) {
|
|
|
533
522
|
// Override props.
|
|
534
523
|
...cfDistributionProps,
|
|
535
524
|
// these values can NOT be overwritten by cfDistributionProps
|
|
536
|
-
defaultBehavior: this.buildDefaultBehaviorForEdge(
|
|
525
|
+
defaultBehavior: this.buildDefaultBehaviorForEdge(),
|
|
537
526
|
additionalBehaviors: {
|
|
538
527
|
...(cfDistributionProps.additionalBehaviors || {}),
|
|
539
528
|
},
|
|
@@ -541,7 +530,7 @@ function handler(event) {
|
|
|
541
530
|
},
|
|
542
531
|
});
|
|
543
532
|
}
|
|
544
|
-
buildDefaultBehaviorForRegional(
|
|
533
|
+
buildDefaultBehaviorForRegional() {
|
|
545
534
|
const { timeout, regional, cdk } = this.props;
|
|
546
535
|
const cfDistributionProps = cdk?.distribution || {};
|
|
547
536
|
const fnUrl = this.serverLambdaForRegional.addFunctionUrl({
|
|
@@ -562,12 +551,12 @@ function handler(event) {
|
|
|
562
551
|
allowedMethods: AllowedMethods.ALLOW_ALL,
|
|
563
552
|
cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
|
|
564
553
|
compress: true,
|
|
565
|
-
cachePolicy,
|
|
554
|
+
cachePolicy: cdk?.serverCachePolicy ?? this.useServerBehaviorCachePolicy(),
|
|
566
555
|
responseHeadersPolicy: cdk?.responseHeadersPolicy,
|
|
567
|
-
originRequestPolicy: this.
|
|
556
|
+
originRequestPolicy: this.useServerBehaviorOriginRequestPolicy(),
|
|
568
557
|
...(cfDistributionProps.defaultBehavior || {}),
|
|
569
558
|
functionAssociations: [
|
|
570
|
-
...this.
|
|
559
|
+
...this.useServerBehaviorFunctionAssociations(),
|
|
571
560
|
...(cfDistributionProps.defaultBehavior?.functionAssociations || []),
|
|
572
561
|
],
|
|
573
562
|
edgeLambdas: [
|
|
@@ -584,25 +573,7 @@ function handler(event) {
|
|
|
584
573
|
],
|
|
585
574
|
};
|
|
586
575
|
}
|
|
587
|
-
|
|
588
|
-
this.serverUrlSigningFunction =
|
|
589
|
-
this.serverUrlSigningFunction ??
|
|
590
|
-
new EdgeFunction(this, "ServerUrlSigningFunction", {
|
|
591
|
-
bundle: path.join(__dirname, "../support/signing-function"),
|
|
592
|
-
runtime: "nodejs18.x",
|
|
593
|
-
handler: "index.handler",
|
|
594
|
-
timeout: 10,
|
|
595
|
-
memorySize: 128,
|
|
596
|
-
permissions: [
|
|
597
|
-
new PolicyStatement({
|
|
598
|
-
actions: ["lambda:InvokeFunctionUrl"],
|
|
599
|
-
resources: [this.serverLambdaForRegional?.functionArn],
|
|
600
|
-
}),
|
|
601
|
-
],
|
|
602
|
-
});
|
|
603
|
-
return this.serverUrlSigningFunction;
|
|
604
|
-
}
|
|
605
|
-
buildDefaultBehaviorForEdge(cachePolicy) {
|
|
576
|
+
buildDefaultBehaviorForEdge() {
|
|
606
577
|
const { cdk } = this.props;
|
|
607
578
|
const cfDistributionProps = cdk?.distribution || {};
|
|
608
579
|
return {
|
|
@@ -611,12 +582,12 @@ function handler(event) {
|
|
|
611
582
|
allowedMethods: AllowedMethods.ALLOW_ALL,
|
|
612
583
|
cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
|
|
613
584
|
compress: true,
|
|
614
|
-
cachePolicy,
|
|
585
|
+
cachePolicy: cdk?.serverCachePolicy ?? this.useServerBehaviorCachePolicy(),
|
|
615
586
|
responseHeadersPolicy: cdk?.responseHeadersPolicy,
|
|
616
|
-
originRequestPolicy: this.
|
|
587
|
+
originRequestPolicy: this.useServerBehaviorOriginRequestPolicy(),
|
|
617
588
|
...(cfDistributionProps.defaultBehavior || {}),
|
|
618
589
|
functionAssociations: [
|
|
619
|
-
...this.
|
|
590
|
+
...this.useServerBehaviorFunctionAssociations(),
|
|
620
591
|
...(cfDistributionProps.defaultBehavior?.functionAssociations || []),
|
|
621
592
|
],
|
|
622
593
|
edgeLambdas: [
|
|
@@ -629,14 +600,6 @@ function handler(event) {
|
|
|
629
600
|
],
|
|
630
601
|
};
|
|
631
602
|
}
|
|
632
|
-
buildBehaviorFunctionAssociations() {
|
|
633
|
-
return [
|
|
634
|
-
{
|
|
635
|
-
eventType: CfFunctionEventType.VIEWER_REQUEST,
|
|
636
|
-
function: this.cfFunction,
|
|
637
|
-
},
|
|
638
|
-
];
|
|
639
|
-
}
|
|
640
603
|
addStaticFileBehaviors() {
|
|
641
604
|
const { cdk } = this.props;
|
|
642
605
|
// Create a template for statics behaviours
|
|
@@ -650,27 +613,93 @@ function handler(event) {
|
|
|
650
613
|
compress: true,
|
|
651
614
|
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
|
|
652
615
|
responseHeadersPolicy: cdk?.responseHeadersPolicy,
|
|
616
|
+
functionAssociations: [
|
|
617
|
+
...this.useStaticBehaviorFunctionAssociations(),
|
|
618
|
+
],
|
|
653
619
|
});
|
|
654
620
|
}
|
|
655
621
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
622
|
+
useServerBehaviorFunctionAssociations() {
|
|
623
|
+
this.serverCfFunction =
|
|
624
|
+
this.serverCfFunction ??
|
|
625
|
+
new CfFunction(this, "CloudFrontFunction", {
|
|
626
|
+
code: CfFunctionCode.fromInline(`
|
|
627
|
+
function handler(event) {
|
|
628
|
+
var request = event.request;
|
|
629
|
+
request.headers["x-forwarded-host"] = request.headers.host;
|
|
630
|
+
${this.buildConfig.serverCFFunctionInjection || ""}
|
|
631
|
+
return request;
|
|
632
|
+
}`),
|
|
633
|
+
});
|
|
634
|
+
return [
|
|
635
|
+
{
|
|
636
|
+
eventType: CfFunctionEventType.VIEWER_REQUEST,
|
|
637
|
+
function: this.serverCfFunction,
|
|
638
|
+
},
|
|
639
|
+
];
|
|
640
|
+
}
|
|
641
|
+
useStaticBehaviorFunctionAssociations() {
|
|
642
|
+
if (!this.buildConfig.clientCFFunctionInjection)
|
|
643
|
+
return [];
|
|
644
|
+
this.staticCfFunction =
|
|
645
|
+
this.staticCfFunction ??
|
|
646
|
+
new CfFunction(this, "CloudFrontFunctionForStaticBehavior", {
|
|
647
|
+
code: CfFunctionCode.fromInline(`
|
|
648
|
+
function handler(event) {
|
|
649
|
+
var request = event.request;
|
|
650
|
+
${this.buildConfig.clientCFFunctionInjection || ""}
|
|
651
|
+
return request;
|
|
652
|
+
}`),
|
|
653
|
+
});
|
|
654
|
+
return [
|
|
655
|
+
{
|
|
656
|
+
eventType: CfFunctionEventType.VIEWER_REQUEST,
|
|
657
|
+
function: this.staticCfFunction,
|
|
658
|
+
},
|
|
659
|
+
];
|
|
660
|
+
}
|
|
661
|
+
useServerUrlSigningFunction() {
|
|
662
|
+
this.serverUrlSigningFunction =
|
|
663
|
+
this.serverUrlSigningFunction ??
|
|
664
|
+
new EdgeFunction(this, "ServerUrlSigningFunction", {
|
|
665
|
+
bundle: path.join(__dirname, "../support/signing-function"),
|
|
666
|
+
runtime: "nodejs18.x",
|
|
667
|
+
handler: "index.handler",
|
|
668
|
+
timeout: 10,
|
|
669
|
+
memorySize: 128,
|
|
670
|
+
permissions: [
|
|
671
|
+
new PolicyStatement({
|
|
672
|
+
actions: ["lambda:InvokeFunctionUrl"],
|
|
673
|
+
resources: [this.serverLambdaForRegional?.functionArn],
|
|
674
|
+
}),
|
|
675
|
+
],
|
|
676
|
+
});
|
|
677
|
+
return this.serverUrlSigningFunction;
|
|
678
|
+
}
|
|
679
|
+
useServerBehaviorCachePolicy(allowedHeaders) {
|
|
680
|
+
this.serverBehaviorCachePolicy =
|
|
681
|
+
this.serverBehaviorCachePolicy ??
|
|
682
|
+
new CachePolicy(this, "ServerCache", {
|
|
683
|
+
queryStringBehavior: CacheQueryStringBehavior.all(),
|
|
684
|
+
headerBehavior: allowedHeaders && allowedHeaders.length > 0
|
|
685
|
+
? CacheHeaderBehavior.allowList(...allowedHeaders)
|
|
686
|
+
: CacheHeaderBehavior.none(),
|
|
687
|
+
cookieBehavior: CacheCookieBehavior.none(),
|
|
688
|
+
defaultTtl: CdkDuration.days(0),
|
|
689
|
+
maxTtl: CdkDuration.days(365),
|
|
690
|
+
minTtl: CdkDuration.days(0),
|
|
691
|
+
enableAcceptEncodingBrotli: true,
|
|
692
|
+
enableAcceptEncodingGzip: true,
|
|
693
|
+
comment: "SST server response cache policy",
|
|
694
|
+
});
|
|
695
|
+
return this.serverBehaviorCachePolicy;
|
|
670
696
|
}
|
|
671
|
-
|
|
697
|
+
useServerBehaviorOriginRequestPolicy() {
|
|
672
698
|
// CloudFront's Managed-AllViewerExceptHostHeader policy
|
|
673
|
-
|
|
699
|
+
this.serverBehaviorOriginRequestPolicy =
|
|
700
|
+
this.serverBehaviorOriginRequestPolicy ??
|
|
701
|
+
OriginRequestPolicy.fromOriginRequestPolicyId(this, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
|
|
702
|
+
return this.serverBehaviorOriginRequestPolicy;
|
|
674
703
|
}
|
|
675
704
|
/////////////////////
|
|
676
705
|
// Helper Functions
|
package/package.json
CHANGED
|
@@ -58,6 +58,7 @@ export const useContainerHandler = Context.memo(async () => {
|
|
|
58
58
|
...(input.props.container?.file
|
|
59
59
|
? [`-f ${input.props.container.file}`]
|
|
60
60
|
: []),
|
|
61
|
+
...Object.entries(input.props.container?.buildArgs || {}).map(([k, v]) => `--build-arg ${k}=${v}`),
|
|
61
62
|
`.`,
|
|
62
63
|
].join(" "), {
|
|
63
64
|
cwd: project,
|
|
@@ -84,6 +85,7 @@ export const useContainerHandler = Context.memo(async () => {
|
|
|
84
85
|
...(input.props.container?.file
|
|
85
86
|
? [`-f ${input.props.container.file}`]
|
|
86
87
|
: []),
|
|
88
|
+
...Object.entries(input.props.container?.buildArgs || {}).map(([k, v]) => `--build-arg ${k}=${v}`),
|
|
87
89
|
`--platform ${platform}`,
|
|
88
90
|
`.`,
|
|
89
91
|
].join(" "), {
|
package/runtime/handlers/java.js
CHANGED
|
@@ -68,26 +68,22 @@ export const useJavaHandler = Context.memo(async () => {
|
|
|
68
68
|
const buildTask = input.props.java?.buildTask || "build";
|
|
69
69
|
const outputDir = input.props.java?.buildOutputDir || "distributions";
|
|
70
70
|
sources.set(input.functionID, srcPath);
|
|
71
|
-
async function build() {
|
|
72
|
-
// build
|
|
73
|
-
await execAsync(`${buildBinary} ${buildTask} -Dorg.gradle.logging.level=${process.env.DEBUG ? "debug" : "lifecycle"}`, {
|
|
74
|
-
cwd: srcPath,
|
|
75
|
-
});
|
|
76
|
-
// unzip
|
|
77
|
-
const buildOutput = path.join(srcPath, "build", outputDir);
|
|
78
|
-
const zip = (await fs.readdir(buildOutput)).find((f) => f.endsWith(".zip"));
|
|
79
|
-
await new Promise((resolve, reject) => {
|
|
80
|
-
const zipper = new AdmZip(path.join(buildOutput, zip));
|
|
81
|
-
zipper.extractAllToAsync(input.out, false, false, (err) => err ? reject(err) : resolve(undefined));
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
71
|
try {
|
|
85
|
-
//
|
|
72
|
+
// Build
|
|
73
|
+
// Note: run gradle build once per directory. Otherwise they'll interfere
|
|
86
74
|
// with one another
|
|
87
|
-
const buildPromise = runningBuilds.get(buildBinary) ??
|
|
75
|
+
const buildPromise = runningBuilds.get(buildBinary) ??
|
|
76
|
+
execAsync(`${buildBinary} ${buildTask} -Dorg.gradle.logging.level=${process.env.DEBUG ? "debug" : "lifecycle"}`, {
|
|
77
|
+
cwd: srcPath,
|
|
78
|
+
});
|
|
88
79
|
runningBuilds.set(buildBinary, buildPromise);
|
|
89
80
|
await buildPromise;
|
|
90
81
|
runningBuilds.delete(buildBinary);
|
|
82
|
+
// unzip
|
|
83
|
+
const buildOutput = path.join(srcPath, "build", outputDir);
|
|
84
|
+
const zip = (await fs.readdir(buildOutput)).find((f) => f.endsWith(".zip"));
|
|
85
|
+
const zipper = new AdmZip(path.join(buildOutput, zip));
|
|
86
|
+
zipper.extractAllTo(input.out, false, false);
|
|
91
87
|
return {
|
|
92
88
|
type: "success",
|
|
93
89
|
handler: input.props.handler,
|