sst 2.41.5 → 2.43.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/cdk/deployments.d.ts +3 -1
- package/cdk/deployments.js +22 -19
- package/constructs/AstroSite.d.ts +6 -32
- package/constructs/Function.js +2 -2
- package/constructs/NextjsSite.d.ts +37 -127
- package/constructs/NextjsSite.js +188 -278
- package/constructs/RDS.d.ts +2 -2
- package/constructs/RDS.js +6 -1
- package/constructs/RemixSite.d.ts +3 -0
- package/constructs/Service.js +3 -2
- package/constructs/SolidStartSite.d.ts +3 -0
- package/constructs/SsrFunction.d.ts +4 -2
- package/constructs/SsrFunction.js +6 -1
- package/constructs/SsrSite.d.ts +16 -8
- package/constructs/SsrSite.js +30 -26
- package/constructs/SvelteKitSite.d.ts +3 -0
- package/node/future/auth/adapter/apple.d.ts +2 -1
- package/node/future/auth/adapter/apple.js +9 -1
- package/node/future/auth/adapter/google.d.ts +3 -2
- package/node/future/auth/adapter/google.js +11 -3
- package/package.json +9 -9
- package/runtime/handlers/container.js +6 -4
- package/support/ssr-warmer/index.mjs +52 -49
package/constructs/NextjsSite.js
CHANGED
|
@@ -1,35 +1,20 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import zlib from "zlib";
|
|
4
3
|
import crypto from "crypto";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { Code, Runtime, Function as CdkFunction, Architecture, LayerVersion, } from "aws-cdk-lib/aws-lambda";
|
|
4
|
+
import { Duration as CdkDuration, RemovalPolicy, CustomResource, Fn, } from "aws-cdk-lib/core";
|
|
5
|
+
import { Code, Runtime, Function as CdkFunction, Architecture, } from "aws-cdk-lib/aws-lambda";
|
|
8
6
|
import { AttributeType, Billing, TableV2 as Table, } from "aws-cdk-lib/aws-dynamodb";
|
|
9
7
|
import { Provider } from "aws-cdk-lib/custom-resources";
|
|
10
8
|
import { Queue } from "aws-cdk-lib/aws-sqs";
|
|
11
9
|
import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
|
|
12
10
|
import { Stack } from "./Stack.js";
|
|
13
|
-
import { SsrSite } from "./SsrSite.js";
|
|
11
|
+
import { SsrSite, } from "./SsrSite.js";
|
|
14
12
|
import { toCdkSize } from "./util/size.js";
|
|
15
|
-
import {
|
|
13
|
+
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
16
14
|
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
17
15
|
import { VisibleError } from "../error.js";
|
|
18
|
-
import { Asset } from "aws-cdk-lib/aws-s3-assets";
|
|
19
|
-
import { useFunctions } from "./Function.js";
|
|
20
|
-
import { useDeferredTasks } from "./deferred_task.js";
|
|
21
16
|
import { Logger } from "../logger.js";
|
|
22
|
-
const
|
|
23
|
-
const DEFAULT_OPEN_NEXT_VERSION = "2.3.8";
|
|
24
|
-
const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
|
|
25
|
-
"accept",
|
|
26
|
-
"rsc",
|
|
27
|
-
"next-router-prefetch",
|
|
28
|
-
"next-router-state-tree",
|
|
29
|
-
"next-url",
|
|
30
|
-
"x-prerender-bypass",
|
|
31
|
-
"x-prerender-revalidate",
|
|
32
|
-
];
|
|
17
|
+
const DEFAULT_OPEN_NEXT_VERSION = "3.0.2";
|
|
33
18
|
/**
|
|
34
19
|
* The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
|
|
35
20
|
* @example
|
|
@@ -48,118 +33,135 @@ export class NextjsSite extends SsrSite {
|
|
|
48
33
|
appPathsManifest;
|
|
49
34
|
pagesManifest;
|
|
50
35
|
prerenderManifest;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
logging: rawProps?.logging ?? "per-route",
|
|
54
|
-
experimental: {
|
|
55
|
-
streaming: rawProps?.experimental?.streaming ?? false,
|
|
56
|
-
disableDynamoDBCache: rawProps?.experimental?.disableDynamoDBCache ?? false,
|
|
57
|
-
disableIncrementalCache: rawProps?.experimental?.disableIncrementalCache ?? false,
|
|
58
|
-
...rawProps?.experimental,
|
|
59
|
-
},
|
|
60
|
-
...rawProps,
|
|
61
|
-
};
|
|
36
|
+
openNextOutput;
|
|
37
|
+
constructor(scope, id, props = {}) {
|
|
62
38
|
super(scope, id, {
|
|
63
39
|
buildCommand: [
|
|
64
40
|
"npx",
|
|
65
41
|
"--yes",
|
|
66
42
|
`open-next@${props?.openNextVersion ?? DEFAULT_OPEN_NEXT_VERSION}`,
|
|
67
43
|
"build",
|
|
68
|
-
...(props.openNext?.buildOutputPath
|
|
69
|
-
? ["--build-output-path", props.openNext.buildOutputPath]
|
|
70
|
-
: []),
|
|
71
|
-
...(props.experimental.streaming ? ["--streaming"] : []),
|
|
72
|
-
...(props.experimental.disableDynamoDBCache
|
|
73
|
-
? ["--dangerously-disable-dynamodb-cache"]
|
|
74
|
-
: []),
|
|
75
|
-
...(props.experimental.disableIncrementalCache
|
|
76
|
-
? ["--dangerously-disable-incremental-cache"]
|
|
77
|
-
: []),
|
|
78
44
|
].join(" "),
|
|
79
45
|
...props,
|
|
80
46
|
});
|
|
47
|
+
const disableIncrementalCache = this.openNextOutput?.additionalProps?.disableIncrementalCache ?? false;
|
|
48
|
+
const disableTagCache = this.openNextOutput?.additionalProps?.disableTagCache ?? false;
|
|
81
49
|
this.handleMissingSourcemap();
|
|
82
|
-
if (this.
|
|
83
|
-
|
|
84
|
-
this.uploadSourcemaps();
|
|
50
|
+
if (this.openNextOutput?.edgeFunctions?.middleware) {
|
|
51
|
+
this.setMiddlewareEnv();
|
|
85
52
|
}
|
|
86
|
-
if (!
|
|
53
|
+
if (!disableIncrementalCache) {
|
|
87
54
|
this.createRevalidationQueue();
|
|
88
|
-
if (!
|
|
55
|
+
if (!disableTagCache) {
|
|
89
56
|
this.createRevalidationTable();
|
|
90
57
|
}
|
|
91
58
|
}
|
|
92
59
|
}
|
|
93
|
-
|
|
94
|
-
|
|
60
|
+
createFunctionOrigin(fn, key, bucket) {
|
|
61
|
+
const { path: sitePath, environment, cdk } = this.props;
|
|
62
|
+
const baseServerConfig = {
|
|
63
|
+
description: "Next.js Server",
|
|
64
|
+
environment: {
|
|
65
|
+
CACHE_BUCKET_NAME: bucket.bucketName,
|
|
66
|
+
CACHE_BUCKET_KEY_PREFIX: "_cache",
|
|
67
|
+
CACHE_BUCKET_REGION: Stack.of(this).region,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
type: "function",
|
|
72
|
+
constructId: `${key}ServerFunction`,
|
|
73
|
+
function: {
|
|
74
|
+
...baseServerConfig,
|
|
75
|
+
handler: fn.handler,
|
|
76
|
+
bundle: path.join(sitePath, fn.bundle),
|
|
77
|
+
runtime: "nodejs18.x",
|
|
78
|
+
architecture: Architecture.ARM_64,
|
|
79
|
+
memorySize: 1536,
|
|
80
|
+
environment: {
|
|
81
|
+
...environment,
|
|
82
|
+
...baseServerConfig.environment,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
streaming: fn.streaming,
|
|
86
|
+
injections: [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
createEcsOrigin(ecs, key, bucket) {
|
|
90
|
+
throw new Error("Ecs origin are not supported yet");
|
|
95
91
|
}
|
|
96
|
-
|
|
97
|
-
const { path: sitePath,
|
|
98
|
-
const
|
|
99
|
-
const serverConfig = {
|
|
100
|
-
description: "Next.js server",
|
|
101
|
-
bundle: path.join(sitePath, ".open-next", "server-function"),
|
|
102
|
-
handler: "index.handler",
|
|
92
|
+
createEdgeOrigin(fn, key, bucket) {
|
|
93
|
+
const { path: sitePath, cdk, environment } = this.props;
|
|
94
|
+
const baseServerConfig = {
|
|
103
95
|
environment: {
|
|
104
96
|
CACHE_BUCKET_NAME: bucket.bucketName,
|
|
105
97
|
CACHE_BUCKET_KEY_PREFIX: "_cache",
|
|
106
98
|
CACHE_BUCKET_REGION: Stack.of(this).region,
|
|
107
99
|
},
|
|
108
|
-
layers: this.isPerRouteLoggingEnabled()
|
|
109
|
-
? [
|
|
110
|
-
LayerVersion.fromLayerVersionArn(this, "SSTExtension", cdk?.server?.architecture?.name === Architecture.X86_64.name
|
|
111
|
-
? `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-amd64:${LAYER_VERSION}`
|
|
112
|
-
: `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-arm64:${LAYER_VERSION}`),
|
|
113
|
-
]
|
|
114
|
-
: undefined,
|
|
115
100
|
};
|
|
116
|
-
|
|
101
|
+
return {
|
|
102
|
+
constructId: `${key}EdgeFunction`,
|
|
103
|
+
function: {
|
|
104
|
+
handler: fn.handler,
|
|
105
|
+
bundle: path.join(sitePath, fn.bundle),
|
|
106
|
+
runtime: "nodejs18.x",
|
|
107
|
+
memorySize: 1024,
|
|
108
|
+
environment: {
|
|
109
|
+
...environment,
|
|
110
|
+
...baseServerConfig.environment,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
plan(bucket) {
|
|
116
|
+
const { path: sitePath } = this.props;
|
|
117
|
+
const imageOptimization = this.props.imageOptimization;
|
|
118
|
+
const openNextOutputPath = path.join(sitePath ?? ".", ".open-next", "open-next.output.json");
|
|
119
|
+
if (!fs.existsSync(openNextOutputPath)) {
|
|
120
|
+
throw new VisibleError(`Failed to load ".open-next/output.json" for the "${this.id}" site.`);
|
|
121
|
+
}
|
|
122
|
+
const openNextOutput = JSON.parse(fs.readFileSync(openNextOutputPath).toString());
|
|
123
|
+
this.openNextOutput = openNextOutput;
|
|
124
|
+
const imageOpt = openNextOutput.origins
|
|
125
|
+
.imageOptimizer;
|
|
126
|
+
const defaultOrigin = openNextOutput.origins.default;
|
|
127
|
+
const remainingOrigins = Object.entries(openNextOutput.origins).filter(([key, value]) => {
|
|
128
|
+
const result = key !== "imageOptimizer" && key !== "default" && key !== "s3";
|
|
129
|
+
return result;
|
|
130
|
+
});
|
|
131
|
+
const edgeFunctions = Object.entries(openNextOutput.edgeFunctions).reduce((acc, [key, value]) => {
|
|
132
|
+
return { ...acc, [key]: this.createEdgeOrigin(value, key, bucket) };
|
|
133
|
+
}, {});
|
|
117
134
|
return this.validatePlan({
|
|
118
|
-
edge:
|
|
135
|
+
edge: false,
|
|
119
136
|
cloudFrontFunctions: {
|
|
120
137
|
serverCfFunction: {
|
|
121
138
|
constructId: "CloudFrontFunction",
|
|
122
139
|
injections: [
|
|
123
140
|
this.useCloudFrontFunctionHostHeaderInjection(),
|
|
124
|
-
this.
|
|
141
|
+
this.useCloudFrontFunctionCacheHeaderKey(),
|
|
142
|
+
this.useCloudfrontGeoHeadersInjection(),
|
|
125
143
|
],
|
|
126
144
|
},
|
|
127
145
|
},
|
|
128
|
-
edgeFunctions
|
|
129
|
-
? {
|
|
130
|
-
edgeServer: {
|
|
131
|
-
constructId: "ServerFunction",
|
|
132
|
-
function: serverConfig,
|
|
133
|
-
},
|
|
134
|
-
}
|
|
135
|
-
: undefined,
|
|
146
|
+
edgeFunctions,
|
|
136
147
|
origins: {
|
|
137
|
-
|
|
138
|
-
? {}
|
|
139
|
-
: {
|
|
140
|
-
regionalServer: {
|
|
141
|
-
type: "function",
|
|
142
|
-
constructId: "ServerFunction",
|
|
143
|
-
function: serverConfig,
|
|
144
|
-
streaming: experimental?.streaming,
|
|
145
|
-
injections: this.isPerRouteLoggingEnabled()
|
|
146
|
-
? [this.useServerFunctionPerRouteLoggingInjection()]
|
|
147
|
-
: [],
|
|
148
|
-
},
|
|
149
|
-
}),
|
|
148
|
+
s3: openNextOutput.origins.s3,
|
|
150
149
|
imageOptimizer: {
|
|
151
150
|
type: "image-optimization-function",
|
|
152
|
-
constructId: "ImageFunction",
|
|
153
151
|
function: {
|
|
154
|
-
description: "Next.js
|
|
155
|
-
handler:
|
|
156
|
-
code: Code.fromAsset(path.join(sitePath,
|
|
152
|
+
description: "Next.js Image Optimization Function",
|
|
153
|
+
handler: imageOpt.handler,
|
|
154
|
+
code: Code.fromAsset(path.join(sitePath, imageOpt.bundle)),
|
|
157
155
|
runtime: Runtime.NODEJS_18_X,
|
|
158
156
|
architecture: Architecture.ARM_64,
|
|
159
157
|
environment: {
|
|
160
158
|
BUCKET_NAME: bucket.bucketName,
|
|
161
159
|
BUCKET_KEY_PREFIX: "_assets",
|
|
160
|
+
...(this.props.imageOptimization?.staticImageOptimization
|
|
161
|
+
? { OPENNEXT_STATIC_ETAG: "true" }
|
|
162
|
+
: {}),
|
|
162
163
|
},
|
|
164
|
+
permissions: ["s3"],
|
|
163
165
|
memorySize: imageOptimization?.memorySize
|
|
164
166
|
? typeof imageOptimization.memorySize === "string"
|
|
165
167
|
? toCdkSize(imageOptimization.memorySize).toMebibytes()
|
|
@@ -167,98 +169,55 @@ export class NextjsSite extends SsrSite {
|
|
|
167
169
|
: 1536,
|
|
168
170
|
},
|
|
169
171
|
},
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
},
|
|
180
|
-
{ from: ".open-next/cache", to: "_cache", cached: false },
|
|
181
|
-
],
|
|
182
|
-
},
|
|
172
|
+
default: defaultOrigin.type === "ecs"
|
|
173
|
+
? this.createEcsOrigin(defaultOrigin, "default", bucket)
|
|
174
|
+
: this.createFunctionOrigin(defaultOrigin, "default", bucket),
|
|
175
|
+
...Object.fromEntries(remainingOrigins.map(([key, value]) => [
|
|
176
|
+
key,
|
|
177
|
+
value.type === "ecs"
|
|
178
|
+
? this.createEcsOrigin(value, key, bucket)
|
|
179
|
+
: this.createFunctionOrigin(value, key, bucket),
|
|
180
|
+
])),
|
|
183
181
|
},
|
|
184
|
-
behaviors:
|
|
185
|
-
|
|
186
|
-
?
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
cfFunction: "serverCfFunction",
|
|
190
|
-
edgeFunction: "edgeServer",
|
|
191
|
-
origin: "s3",
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
cacheType: "server",
|
|
195
|
-
pattern: this.prefixPattern("api/*"),
|
|
196
|
-
cfFunction: "serverCfFunction",
|
|
197
|
-
edgeFunction: "edgeServer",
|
|
198
|
-
origin: "s3",
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
cacheType: "server",
|
|
202
|
-
pattern: this.prefixPattern("_next/data/*"),
|
|
203
|
-
cfFunction: "serverCfFunction",
|
|
204
|
-
edgeFunction: "edgeServer",
|
|
205
|
-
origin: "s3",
|
|
206
|
-
},
|
|
207
|
-
]
|
|
208
|
-
: [
|
|
209
|
-
{
|
|
210
|
-
cacheType: "server",
|
|
211
|
-
cfFunction: "serverCfFunction",
|
|
212
|
-
origin: "regionalServer",
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
cacheType: "server",
|
|
216
|
-
pattern: this.prefixPattern("api/*"),
|
|
217
|
-
cfFunction: "serverCfFunction",
|
|
218
|
-
origin: "regionalServer",
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
cacheType: "server",
|
|
222
|
-
pattern: this.prefixPattern("_next/data/*"),
|
|
223
|
-
cfFunction: "serverCfFunction",
|
|
224
|
-
origin: "regionalServer",
|
|
225
|
-
},
|
|
226
|
-
]),
|
|
227
|
-
{
|
|
228
|
-
cacheType: "server",
|
|
229
|
-
pattern: this.prefixPattern("_next/image*"),
|
|
182
|
+
behaviors: openNextOutput.behaviors.map((behavior) => {
|
|
183
|
+
return {
|
|
184
|
+
pattern: behavior.pattern === "*" ? undefined : behavior.pattern,
|
|
185
|
+
origin: behavior.origin,
|
|
186
|
+
cacheType: behavior.origin === "s3" ? "static" : "server",
|
|
230
187
|
cfFunction: "serverCfFunction",
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
: item),
|
|
241
|
-
origin: "s3",
|
|
242
|
-
})),
|
|
243
|
-
],
|
|
188
|
+
edgeFunction: behavior.edgeFunction ?? "",
|
|
189
|
+
};
|
|
190
|
+
}),
|
|
191
|
+
buildId: this.getBuildId(),
|
|
192
|
+
warmer: openNextOutput.additionalProps?.warmer
|
|
193
|
+
? {
|
|
194
|
+
function: path.join(sitePath, openNextOutput.additionalProps.warmer.bundle),
|
|
195
|
+
}
|
|
196
|
+
: undefined,
|
|
244
197
|
serverCachePolicy: {
|
|
245
|
-
allowedHeaders:
|
|
198
|
+
allowedHeaders: ["x-open-next-cache-key"],
|
|
246
199
|
},
|
|
247
|
-
buildId: this.getBuildId(),
|
|
248
200
|
});
|
|
249
201
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
202
|
+
setMiddlewareEnv() {
|
|
203
|
+
const origins = this.serverFunctions.reduce((acc, server) => {
|
|
204
|
+
return {
|
|
205
|
+
...acc,
|
|
206
|
+
[server.function
|
|
207
|
+
? server.id.replace("ServerFunction", "")
|
|
208
|
+
: server.id.replace("ServerContainer", "")]: {
|
|
209
|
+
host: Fn.parseDomainName(server.url ?? ""),
|
|
210
|
+
port: 443,
|
|
211
|
+
protocol: "https",
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}, {});
|
|
215
|
+
this.edgeFunctions?.middleware?.addEnvironment("OPEN_NEXT_ORIGIN", Fn.toJsonString(origins));
|
|
256
216
|
}
|
|
257
217
|
createRevalidationQueue() {
|
|
258
218
|
if (!this.serverFunction)
|
|
259
219
|
return;
|
|
260
220
|
const { cdk } = this.props;
|
|
261
|
-
const server = this.serverFunction;
|
|
262
221
|
const queue = new Queue(this, "RevalidationQueue", {
|
|
263
222
|
fifo: true,
|
|
264
223
|
receiveMessageWaitTime: CdkDuration.seconds(20),
|
|
@@ -272,16 +231,17 @@ export class NextjsSite extends SsrSite {
|
|
|
272
231
|
...cdk?.revalidation,
|
|
273
232
|
});
|
|
274
233
|
consumer.addEventSource(new SqsEventSource(queue, { batchSize: 5 }));
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
234
|
+
this.serverFunctions.forEach((server) => {
|
|
235
|
+
// Allow server to send messages to the queue
|
|
236
|
+
server.addEnvironment("REVALIDATION_QUEUE_URL", queue.queueUrl);
|
|
237
|
+
server.addEnvironment("REVALIDATION_QUEUE_REGION", Stack.of(this).region);
|
|
238
|
+
queue.grantSendMessages(server.role);
|
|
239
|
+
});
|
|
279
240
|
}
|
|
280
241
|
createRevalidationTable() {
|
|
281
242
|
if (!this.serverFunction)
|
|
282
243
|
return;
|
|
283
244
|
const { path: sitePath } = this.props;
|
|
284
|
-
const server = this.serverFunction;
|
|
285
245
|
const table = new Table(this, "RevalidationTable", {
|
|
286
246
|
partitionKey: { name: "tag", type: AttributeType.STRING },
|
|
287
247
|
sortKey: { name: "path", type: AttributeType.STRING },
|
|
@@ -296,8 +256,10 @@ export class NextjsSite extends SsrSite {
|
|
|
296
256
|
],
|
|
297
257
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
298
258
|
});
|
|
299
|
-
server
|
|
300
|
-
|
|
259
|
+
this.serverFunctions.forEach((server) => {
|
|
260
|
+
server?.addEnvironment("CACHE_DYNAMO_TABLE", table.tableName);
|
|
261
|
+
table.grantReadWriteData(server.role);
|
|
262
|
+
});
|
|
301
263
|
const dynamodbProviderPath = path.join(sitePath, ".open-next", "dynamodb-provider");
|
|
302
264
|
if (fs.existsSync(dynamodbProviderPath)) {
|
|
303
265
|
// Provision 128MB of memory for every 4,000 prerendered routes,
|
|
@@ -338,35 +300,11 @@ export class NextjsSite extends SsrSite {
|
|
|
338
300
|
}
|
|
339
301
|
}
|
|
340
302
|
getConstructMetadata() {
|
|
341
|
-
const metadata = this.getConstructMetadataBase();
|
|
342
303
|
return {
|
|
343
|
-
...metadata,
|
|
344
304
|
type: "NextjsSite",
|
|
345
|
-
|
|
346
|
-
...metadata.data,
|
|
347
|
-
routes: this.isPerRouteLoggingEnabled()
|
|
348
|
-
? {
|
|
349
|
-
logGroupPrefix: `/sst/lambda/${this.serverFunction.functionName}`,
|
|
350
|
-
data: this.useRoutes().map(({ route, logGroupPath }) => ({
|
|
351
|
-
route,
|
|
352
|
-
logGroupPath,
|
|
353
|
-
})),
|
|
354
|
-
}
|
|
355
|
-
: undefined,
|
|
356
|
-
},
|
|
305
|
+
...this.getConstructMetadataBase(),
|
|
357
306
|
};
|
|
358
307
|
}
|
|
359
|
-
removeSourcemaps() {
|
|
360
|
-
const { path: sitePath } = this.props;
|
|
361
|
-
const files = globSync("**/*.js.map", {
|
|
362
|
-
cwd: path.join(sitePath, ".open-next", "server-function"),
|
|
363
|
-
nodir: true,
|
|
364
|
-
dot: true,
|
|
365
|
-
});
|
|
366
|
-
for (const file of files) {
|
|
367
|
-
fs.rmSync(path.join(sitePath, ".open-next", "server-function", file));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
308
|
useRoutes() {
|
|
371
309
|
if (this._routes)
|
|
372
310
|
return this._routes;
|
|
@@ -510,38 +448,57 @@ export class NextjsSite extends SsrSite {
|
|
|
510
448
|
Logger.debug("Failed to load prerender-manifest.json", e);
|
|
511
449
|
}
|
|
512
450
|
}
|
|
513
|
-
|
|
451
|
+
// This function is used to improve cache hit ratio by setting the cache key based on the request headers and the path
|
|
452
|
+
// next/image only need the accept header, and this header is not useful for the rest of the query
|
|
453
|
+
useCloudFrontFunctionCacheHeaderKey() {
|
|
514
454
|
return `
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
455
|
+
function getHeader(key) {
|
|
456
|
+
var header = request.headers[key];
|
|
457
|
+
if(header) {
|
|
458
|
+
if(header.multiValue){
|
|
459
|
+
return header.multiValue.map((header) => header.value).join(",");
|
|
460
|
+
}
|
|
461
|
+
if(header.value){
|
|
462
|
+
return header.value;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return ""
|
|
466
|
+
}
|
|
467
|
+
var cacheKey = "";
|
|
468
|
+
if(request.uri.startsWith("/_next/image")) {
|
|
469
|
+
cacheKey = getHeader("accept");
|
|
470
|
+
}else {
|
|
471
|
+
cacheKey = getHeader("rsc") + getHeader("next-router-prefetch") + getHeader("next-router-state-tree") + getHeader("next-url") + getHeader("x-prerender-revalidate");
|
|
472
|
+
}
|
|
473
|
+
if(request.cookies["__prerender_bypass"]) {
|
|
474
|
+
cacheKey += request.cookies["__prerender_bypass"] ? request.cookies["__prerender_bypass"].value : "";
|
|
534
475
|
}
|
|
535
|
-
|
|
476
|
+
var crypto = require('crypto');
|
|
477
|
+
|
|
478
|
+
var hashedKey = crypto.createHash('md5').update(cacheKey).digest('hex');
|
|
479
|
+
request.headers["x-open-next-cache-key"] = {value: hashedKey};
|
|
480
|
+
`;
|
|
536
481
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
// header "x-prerender-bypass", and add it to cache policy's allowed headers.
|
|
482
|
+
// Inject the CloudFront viewer country, region, latitude, and longitude headers into the request headers
|
|
483
|
+
// for OpenNext to use them
|
|
484
|
+
useCloudfrontGeoHeadersInjection() {
|
|
541
485
|
return `
|
|
542
|
-
if
|
|
543
|
-
request.headers["x-
|
|
544
|
-
}
|
|
486
|
+
if(request.headers["cloudfront-viewer-city"]) {
|
|
487
|
+
request.headers["x-open-next-city"] = request.headers["cloudfront-viewer-city"];
|
|
488
|
+
}
|
|
489
|
+
if(request.headers["cloudfront-viewer-country"]) {
|
|
490
|
+
request.headers["x-open-next-country"] = request.headers["cloudfront-viewer-country"];
|
|
491
|
+
}
|
|
492
|
+
if(request.headers["cloudfront-viewer-region"]) {
|
|
493
|
+
request.headers["x-open-next-region"] = request.headers["cloudfront-viewer-region"];
|
|
494
|
+
}
|
|
495
|
+
if(request.headers["cloudfront-viewer-latitude"]) {
|
|
496
|
+
request.headers["x-open-next-latitude"] = request.headers["cloudfront-viewer-latitude"];
|
|
497
|
+
}
|
|
498
|
+
if(request.headers["cloudfront-viewer-longitude"]) {
|
|
499
|
+
request.headers["x-open-next-longitude"] = request.headers["cloudfront-viewer-longitude"];
|
|
500
|
+
}
|
|
501
|
+
`;
|
|
545
502
|
}
|
|
546
503
|
getBuildId() {
|
|
547
504
|
const { path: sitePath } = this.props;
|
|
@@ -605,11 +562,6 @@ if (request.cookies["__prerender_bypass"]) {
|
|
|
605
562
|
return;
|
|
606
563
|
return sourcemapPath;
|
|
607
564
|
}
|
|
608
|
-
isPerRouteLoggingEnabled() {
|
|
609
|
-
return (!this.doNotDeploy &&
|
|
610
|
-
!this.props.edge &&
|
|
611
|
-
this.props.logging === "per-route");
|
|
612
|
-
}
|
|
613
565
|
handleMissingSourcemap() {
|
|
614
566
|
if (this.doNotDeploy || this.props.edge)
|
|
615
567
|
return;
|
|
@@ -618,48 +570,6 @@ if (request.cookies["__prerender_bypass"]) {
|
|
|
618
570
|
return;
|
|
619
571
|
this.serverFunction._overrideMissingSourcemap();
|
|
620
572
|
}
|
|
621
|
-
disableDefaultLogging() {
|
|
622
|
-
const stack = Stack.of(this);
|
|
623
|
-
const server = this.serverFunction;
|
|
624
|
-
const policy = new Policy(this, "DisableLoggingPolicy", {
|
|
625
|
-
statements: [
|
|
626
|
-
new PolicyStatement({
|
|
627
|
-
effect: Effect.DENY,
|
|
628
|
-
actions: [
|
|
629
|
-
"logs:CreateLogGroup",
|
|
630
|
-
"logs:CreateLogStream",
|
|
631
|
-
"logs:PutLogEvents",
|
|
632
|
-
],
|
|
633
|
-
resources: [
|
|
634
|
-
`arn:aws:logs:${stack.region}:${stack.account}:log-group:/aws/lambda/${server.functionName}`,
|
|
635
|
-
`arn:aws:logs:${stack.region}:${stack.account}:log-group:/aws/lambda/${server.functionName}:*`,
|
|
636
|
-
],
|
|
637
|
-
}),
|
|
638
|
-
],
|
|
639
|
-
});
|
|
640
|
-
server.role?.attachInlinePolicy(policy);
|
|
641
|
-
}
|
|
642
|
-
uploadSourcemaps() {
|
|
643
|
-
const stack = Stack.of(this);
|
|
644
|
-
const server = this.serverFunction;
|
|
645
|
-
this.useRoutes().forEach(({ sourcemapPath, sourcemapKey }) => {
|
|
646
|
-
if (!sourcemapPath || !sourcemapKey)
|
|
647
|
-
return;
|
|
648
|
-
useDeferredTasks().add(async () => {
|
|
649
|
-
// zip sourcemap
|
|
650
|
-
const zipPath = `${sourcemapPath}.gz.zip`;
|
|
651
|
-
const data = await fs.promises.readFile(sourcemapPath);
|
|
652
|
-
await fs.promises.writeFile(zipPath, zlib.gzipSync(data));
|
|
653
|
-
const asset = new Asset(this, `Sourcemap-${sourcemapKey}`, {
|
|
654
|
-
path: zipPath,
|
|
655
|
-
});
|
|
656
|
-
useFunctions().sourcemaps.add(stack.stackName, {
|
|
657
|
-
asset,
|
|
658
|
-
tarKey: path.join(server.functionArn, sourcemapKey),
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
573
|
static buildCloudWatchRouteName(route) {
|
|
664
574
|
return route.replace(/[^a-zA-Z0-9_\-/.#]/g, "");
|
|
665
575
|
}
|
package/constructs/RDS.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface RDSProps {
|
|
|
13
13
|
/**
|
|
14
14
|
* Database engine of the cluster. Cannot be changed once set.
|
|
15
15
|
*/
|
|
16
|
-
engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
|
|
16
|
+
engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.12" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
|
|
17
17
|
/**
|
|
18
18
|
* Name of a database which is automatically created inside the cluster.
|
|
19
19
|
*/
|
|
@@ -191,7 +191,7 @@ export declare class RDS extends Construct implements SSTConstruct {
|
|
|
191
191
|
getConstructMetadata(): {
|
|
192
192
|
type: "RDS";
|
|
193
193
|
data: {
|
|
194
|
-
engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
|
|
194
|
+
engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.12" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
|
|
195
195
|
secretArn: string;
|
|
196
196
|
types: RDSTypes | undefined;
|
|
197
197
|
clusterArn: string;
|
package/constructs/RDS.js
CHANGED
|
@@ -219,6 +219,11 @@ export class RDS extends Construct {
|
|
|
219
219
|
version: AuroraPostgresEngineVersion.VER_11_16,
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
|
+
else if (engine === "postgresql13.12") {
|
|
223
|
+
return DatabaseClusterEngine.auroraPostgres({
|
|
224
|
+
version: AuroraPostgresEngineVersion.VER_13_12,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
222
227
|
else if (engine === "postgresql13.9") {
|
|
223
228
|
return DatabaseClusterEngine.auroraPostgres({
|
|
224
229
|
version: AuroraPostgresEngineVersion.VER_13_9,
|
|
@@ -239,7 +244,7 @@ export class RDS extends Construct {
|
|
|
239
244
|
version: AuroraPostgresEngineVersion.VER_16_1,
|
|
240
245
|
});
|
|
241
246
|
}
|
|
242
|
-
throw new Error(`The specified "engine" is not supported for sst.RDS. Only mysql5.6, mysql5.7, postgresql11.13, postgresql11.16, and
|
|
247
|
+
throw new Error(`The specified "engine" is not supported for sst.RDS. Only mysql5.6, mysql5.7, postgresql11.13, postgresql11.16, postgresql13.12, and postgresql13.9 engines are currently supported.`);
|
|
243
248
|
}
|
|
244
249
|
getScaling(scaling) {
|
|
245
250
|
return {
|
|
@@ -90,6 +90,9 @@ export declare class RemixSite extends SsrSite {
|
|
|
90
90
|
allowedHeaders?: string[] | undefined;
|
|
91
91
|
} | undefined;
|
|
92
92
|
buildId?: string | undefined;
|
|
93
|
+
warmer?: {
|
|
94
|
+
function: string;
|
|
95
|
+
} | undefined;
|
|
93
96
|
};
|
|
94
97
|
private hasViteConfig;
|
|
95
98
|
private getServerModuleFormat;
|
package/constructs/Service.js
CHANGED
|
@@ -659,8 +659,9 @@ export class Service extends Construct {
|
|
|
659
659
|
`type=${build?.cacheTo.type}`,
|
|
660
660
|
...(build?.cacheTo?.params
|
|
661
661
|
? Object.entries(build?.cacheTo?.params).map(([pk, pv]) => `${pk}=${pv}`)
|
|
662
|
-
: [])
|
|
663
|
-
],
|
|
662
|
+
: []),
|
|
663
|
+
].join(","),
|
|
664
|
+
,
|
|
664
665
|
]
|
|
665
666
|
: []),
|
|
666
667
|
this.props.path,
|