@sitecore-jss/sitecore-jss-proxy 22.3.0 → 22.3.1-canary.2
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/README.md +1 -1
- package/dist/cjs/index.js +19 -447
- package/dist/cjs/middleware/editing/config.js +27 -0
- package/dist/cjs/middleware/editing/index.js +89 -0
- package/dist/cjs/middleware/editing/render.js +103 -0
- package/dist/cjs/middleware/headless-ssr-proxy/index.js +450 -0
- package/dist/cjs/middleware/healthcheck/index.js +16 -0
- package/dist/cjs/middleware/index.js +32 -0
- package/dist/cjs/personalize/PersonalizeHelper.js +243 -0
- package/dist/cjs/personalize/index.js +5 -0
- package/dist/cjs/personalize/test-data/personalizeData.js +86 -0
- package/dist/cjs/types/personalize.js +2 -0
- package/dist/esm/index.js +4 -441
- package/dist/esm/middleware/editing/config.js +23 -0
- package/dist/esm/middleware/editing/index.js +84 -0
- package/dist/esm/middleware/editing/render.js +98 -0
- package/dist/esm/middleware/headless-ssr-proxy/index.js +441 -0
- package/dist/esm/middleware/healthcheck/index.js +12 -0
- package/dist/esm/middleware/index.js +4 -0
- package/dist/esm/personalize/PersonalizeHelper.js +236 -0
- package/dist/esm/personalize/index.js +1 -0
- package/dist/esm/personalize/test-data/personalizeData.js +82 -0
- package/dist/esm/types/personalize.js +1 -0
- package/package.json +22 -9
- package/types/index.d.ts +4 -21
- package/types/middleware/editing/config.d.ts +27 -0
- package/types/middleware/editing/index.d.ts +32 -0
- package/types/middleware/editing/render.d.ts +42 -0
- package/types/{ProxyConfig.d.ts → middleware/headless-ssr-proxy/ProxyConfig.d.ts} +2 -5
- package/types/middleware/headless-ssr-proxy/index.d.ts +20 -0
- package/types/middleware/healthcheck/index.d.ts +6 -0
- package/types/middleware/index.d.ts +3 -0
- package/types/personalize/PersonalizeHelper.d.ts +43 -0
- package/types/personalize/index.d.ts +2 -0
- package/types/personalize/test-data/personalizeData.d.ts +59 -0
- package/types/types/AppRenderer.d.ts +35 -0
- package/types/types/index.d.ts +3 -0
- package/types/types/personalize.d.ts +85 -0
- package/types/AppRenderer.d.ts +0 -10
- package/types/RenderResponse.d.ts +0 -15
- /package/dist/cjs/{ProxyConfig.js → middleware/headless-ssr-proxy/ProxyConfig.js} +0 -0
- /package/dist/cjs/{AppRenderer.js → types/AppRenderer.js} +0 -0
- /package/dist/cjs/{RouteUrlParser.js → types/RouteUrlParser.js} +0 -0
- /package/dist/cjs/{RenderResponse.js → types/index.js} +0 -0
- /package/dist/esm/{ProxyConfig.js → middleware/headless-ssr-proxy/ProxyConfig.js} +0 -0
- /package/dist/esm/{AppRenderer.js → types/AppRenderer.js} +0 -0
- /package/dist/esm/{RouteUrlParser.js → types/RouteUrlParser.js} +0 -0
- /package/dist/esm/{RenderResponse.js → types/index.js} +0 -0
- /package/types/{RouteUrlParser.d.ts → types/RouteUrlParser.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sitecore JavaScript Rendering SDK Proxy
|
|
2
2
|
|
|
3
|
-
This module is provided as a part of Sitecore JavaScript Rendering SDK (JSS). It
|
|
3
|
+
This module is provided as a part of Sitecore JavaScript Rendering SDK (JSS). It provides middlewares, utilities to work in a headless mode.
|
|
4
4
|
|
|
5
5
|
<!---
|
|
6
6
|
@TODO: Update to next version docs before release
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,450 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
13
15
|
};
|
|
14
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// For some reason, every other response returned by Sitecore contains the 'set-cookie' header with the SC_ANALYTICS_GLOBAL_COOKIE value as an empty string.
|
|
22
|
-
// This effectively sets the cookie to empty on the client as well, so if a user were to close their browser
|
|
23
|
-
// after one of these 'empty value' responses, they would not be tracked as a returning visitor after re-opening their browser.
|
|
24
|
-
// To address this, we simply parse the response cookies and if the analytics cookie is present but has an empty value, then we
|
|
25
|
-
// remove it from the response header. This means the existing cookie in the browser remains intact.
|
|
26
|
-
const removeEmptyAnalyticsCookie = (proxyResponse) => {
|
|
27
|
-
const cookies = set_cookie_parser_1.default.parse(proxyResponse.headers['set-cookie']);
|
|
28
|
-
if (cookies) {
|
|
29
|
-
const analyticsCookieIndex = cookies.findIndex((c) => c.name === 'SC_ANALYTICS_GLOBAL_COOKIE');
|
|
30
|
-
if (analyticsCookieIndex !== -1) {
|
|
31
|
-
const analyticsCookie = cookies[analyticsCookieIndex];
|
|
32
|
-
if (analyticsCookie && analyticsCookie.value === '') {
|
|
33
|
-
cookies.splice(analyticsCookieIndex, 1);
|
|
34
|
-
/* eslint-disable no-param-reassign */
|
|
35
|
-
proxyResponse.headers['set-cookie'] = cookies;
|
|
36
|
-
/* eslint-enable no-param-reassign */
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
exports.removeEmptyAnalyticsCookie = removeEmptyAnalyticsCookie;
|
|
42
|
-
// inspired by: http://stackoverflow.com/a/22487927/9324
|
|
43
|
-
/**
|
|
44
|
-
* @param {IncomingMessage} proxyResponse
|
|
45
|
-
* @param {IncomingMessage} request
|
|
46
|
-
* @param {ServerResponse} serverResponse
|
|
47
|
-
* @param {AppRenderer} renderer
|
|
48
|
-
* @param {ProxyConfig} config
|
|
49
|
-
*/
|
|
50
|
-
function renderAppToResponse(proxyResponse, request, serverResponse, renderer, config) {
|
|
51
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
// monkey-patch FTW?
|
|
53
|
-
const originalWriteHead = serverResponse.writeHead;
|
|
54
|
-
const originalWrite = serverResponse.write;
|
|
55
|
-
const originalEnd = serverResponse.end;
|
|
56
|
-
// these lines are necessary and must happen before we do any writing to the response
|
|
57
|
-
// otherwise the headers will have already been sent
|
|
58
|
-
delete proxyResponse.headers['content-length'];
|
|
59
|
-
proxyResponse.headers['content-type'] = 'text/html; charset=utf-8';
|
|
60
|
-
// remove IIS server header for security
|
|
61
|
-
delete proxyResponse.headers.server;
|
|
62
|
-
if (config.setHeaders) {
|
|
63
|
-
config.setHeaders(request, serverResponse, proxyResponse);
|
|
64
|
-
}
|
|
65
|
-
const contentEncoding = proxyResponse.headers['content-encoding'];
|
|
66
|
-
if (contentEncoding &&
|
|
67
|
-
(contentEncoding.indexOf('gzip') !== -1 || contentEncoding.indexOf('deflate') !== -1)) {
|
|
68
|
-
delete proxyResponse.headers['content-encoding'];
|
|
69
|
-
}
|
|
70
|
-
// we are going to set our own status code if rendering fails
|
|
71
|
-
serverResponse.writeHead = () => serverResponse;
|
|
72
|
-
// buffer the response body as it is written for later processing
|
|
73
|
-
let buf = Buffer.from('');
|
|
74
|
-
serverResponse.write = (data, encoding) => {
|
|
75
|
-
if (Buffer.isBuffer(data)) {
|
|
76
|
-
buf = Buffer.concat([buf, data]); // append raw buffer
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
buf = Buffer.concat([buf, Buffer.from(data, encoding)]); // append string with optional character encoding (default utf8)
|
|
80
|
-
}
|
|
81
|
-
// sanity check: if the response is huge, bail.
|
|
82
|
-
// ...we don't want to let someone bring down the server by filling up all our RAM.
|
|
83
|
-
if (buf.length > config.maxResponseSizeBytes) {
|
|
84
|
-
throw new Error('Document too large');
|
|
85
|
-
}
|
|
86
|
-
return true;
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* Extract layout service data from proxy response
|
|
90
|
-
*/
|
|
91
|
-
function extractLayoutServiceDataFromProxyResponse() {
|
|
92
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
-
if (proxyResponse.statusCode === http_status_codes_1.default.OK ||
|
|
94
|
-
proxyResponse.statusCode === http_status_codes_1.default.NOT_FOUND) {
|
|
95
|
-
let responseString;
|
|
96
|
-
if (contentEncoding &&
|
|
97
|
-
(contentEncoding.indexOf('gzip') !== -1 || contentEncoding.indexOf('deflate') !== -1)) {
|
|
98
|
-
responseString = new Promise((resolve, reject) => {
|
|
99
|
-
if (config.debug) {
|
|
100
|
-
console.log('Layout service response is compressed; decompressing.');
|
|
101
|
-
}
|
|
102
|
-
zlib_1.default.unzip(buf, (error, result) => {
|
|
103
|
-
if (error) {
|
|
104
|
-
reject(error);
|
|
105
|
-
}
|
|
106
|
-
if (result) {
|
|
107
|
-
resolve(result.toString('utf-8'));
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
responseString = Promise.resolve(buf.toString('utf-8'));
|
|
114
|
-
}
|
|
115
|
-
return responseString.then(util_1.tryParseJson);
|
|
116
|
-
}
|
|
117
|
-
return Promise.resolve(null);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* function replies with HTTP 500 when an error occurs
|
|
122
|
-
* @param {Error} error
|
|
123
|
-
*/
|
|
124
|
-
function replyWithError(error) {
|
|
125
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
-
console.error(error);
|
|
127
|
-
let errorResponse = {
|
|
128
|
-
statusCode: proxyResponse.statusCode || http_status_codes_1.default.INTERNAL_SERVER_ERROR,
|
|
129
|
-
content: proxyResponse.statusMessage || 'Internal Server Error',
|
|
130
|
-
headers: {},
|
|
131
|
-
};
|
|
132
|
-
if (config.onError) {
|
|
133
|
-
const onError = yield config.onError(error, proxyResponse);
|
|
134
|
-
errorResponse = Object.assign(Object.assign({}, errorResponse), onError);
|
|
135
|
-
}
|
|
136
|
-
completeProxyResponse(Buffer.from(errorResponse.content), errorResponse.statusCode, errorResponse.headers);
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
// callback handles the result of server-side rendering
|
|
140
|
-
/**
|
|
141
|
-
* @param {Error | null} error
|
|
142
|
-
* @param {RenderResponse} result
|
|
143
|
-
*/
|
|
144
|
-
function handleRenderingResult(error, result) {
|
|
145
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
-
if (!error && !result) {
|
|
147
|
-
return replyWithError(new Error('Render function did not return a result or an error!'));
|
|
148
|
-
}
|
|
149
|
-
if (error) {
|
|
150
|
-
return replyWithError(error);
|
|
151
|
-
}
|
|
152
|
-
if (!result) {
|
|
153
|
-
// should not occur, but makes TS happy
|
|
154
|
-
return replyWithError(new Error('Render function result did not return a result.'));
|
|
155
|
-
}
|
|
156
|
-
if (!result.html) {
|
|
157
|
-
return replyWithError(new Error('Render function result was returned but html property was falsy.'));
|
|
158
|
-
}
|
|
159
|
-
if (config.transformSSRContent) {
|
|
160
|
-
result.html = yield config.transformSSRContent(result, request, serverResponse);
|
|
161
|
-
}
|
|
162
|
-
// we have to convert back to a buffer so that we can get the *byte count* (rather than character count) of the body
|
|
163
|
-
let content = Buffer.from(result.html);
|
|
164
|
-
// setting the content-length header is not absolutely necessary, but is recommended
|
|
165
|
-
proxyResponse.headers['content-length'] = content.length.toString(10);
|
|
166
|
-
// if original request was a HEAD, we should not return a response body
|
|
167
|
-
if (request.method === 'HEAD') {
|
|
168
|
-
if (config.debug) {
|
|
169
|
-
console.log('DEBUG: Original request method was HEAD, clearing response body');
|
|
170
|
-
}
|
|
171
|
-
content = Buffer.from([]);
|
|
172
|
-
}
|
|
173
|
-
if (result.redirect) {
|
|
174
|
-
if (!result.status) {
|
|
175
|
-
result.status = 302;
|
|
176
|
-
}
|
|
177
|
-
proxyResponse.headers.location = result.redirect;
|
|
178
|
-
}
|
|
179
|
-
const finalStatusCode = result.status || proxyResponse.statusCode || http_status_codes_1.default.OK;
|
|
180
|
-
if (config.debug) {
|
|
181
|
-
console.log('DEBUG: FINAL response headers for client', JSON.stringify(proxyResponse.headers, null, 2));
|
|
182
|
-
console.log('DEBUG: FINAL status code for client', finalStatusCode);
|
|
183
|
-
}
|
|
184
|
-
completeProxyResponse(content, finalStatusCode);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* @param {Buffer | null} content
|
|
189
|
-
* @param {number} statusCode
|
|
190
|
-
* @param {IncomingHttpHeaders} [headers]
|
|
191
|
-
*/
|
|
192
|
-
function completeProxyResponse(content, statusCode, headers) {
|
|
193
|
-
if (!headers) {
|
|
194
|
-
headers = proxyResponse.headers;
|
|
195
|
-
}
|
|
196
|
-
originalWriteHead.apply(serverResponse, [statusCode, headers]);
|
|
197
|
-
if (content)
|
|
198
|
-
originalWrite.call(serverResponse, content);
|
|
199
|
-
originalEnd.call(serverResponse);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* @param {object} layoutServiceData
|
|
203
|
-
*/
|
|
204
|
-
function createViewBag(layoutServiceData) {
|
|
205
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
-
let viewBag = {
|
|
207
|
-
statusCode: proxyResponse.statusCode,
|
|
208
|
-
dictionary: {},
|
|
209
|
-
};
|
|
210
|
-
if (config.createViewBag) {
|
|
211
|
-
const customViewBag = yield config.createViewBag(request, serverResponse, proxyResponse, layoutServiceData);
|
|
212
|
-
viewBag = Object.assign(Object.assign({}, viewBag), customViewBag);
|
|
213
|
-
}
|
|
214
|
-
return viewBag;
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
// as the response is ending, we parse the current response body which is JSON, then
|
|
218
|
-
// render the app using that JSON, but return HTML to the final response.
|
|
219
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
220
|
-
// @ts-ignore
|
|
221
|
-
serverResponse.end = () => __awaiter(this, void 0, void 0, function* () {
|
|
222
|
-
try {
|
|
223
|
-
const layoutServiceData = yield extractLayoutServiceDataFromProxyResponse();
|
|
224
|
-
const viewBag = yield createViewBag(layoutServiceData);
|
|
225
|
-
if (!layoutServiceData) {
|
|
226
|
-
throw new Error(`Received invalid response ${proxyResponse.statusCode} ${proxyResponse.statusMessage}`);
|
|
227
|
-
}
|
|
228
|
-
return renderer(handleRenderingResult,
|
|
229
|
-
// originalUrl not defined in `http-proxy-middleware` types but it exists
|
|
230
|
-
request.originalUrl, layoutServiceData, viewBag);
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
return replyWithError(error);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* @param {IncomingMessage} proxyResponse
|
|
240
|
-
* @param {Request} request
|
|
241
|
-
* @param {Response} serverResponse
|
|
242
|
-
* @param {AppRenderer} renderer
|
|
243
|
-
* @param {ProxyConfig} config
|
|
244
|
-
*/
|
|
245
|
-
function handleProxyResponse(proxyResponse, request, serverResponse, renderer, config) {
|
|
246
|
-
(0, exports.removeEmptyAnalyticsCookie)(proxyResponse);
|
|
247
|
-
if (config.debug) {
|
|
248
|
-
console.log('DEBUG: request url', request.url);
|
|
249
|
-
console.log('DEBUG: request query', request.query);
|
|
250
|
-
console.log('DEBUG: request original url', request.originalUrl);
|
|
251
|
-
console.log('DEBUG: proxied request response code', proxyResponse.statusCode);
|
|
252
|
-
console.log('DEBUG: RAW request headers', JSON.stringify(request.headers, null, 2));
|
|
253
|
-
console.log('DEBUG: RAW headers from the proxied response', JSON.stringify(proxyResponse.headers, null, 2));
|
|
254
|
-
}
|
|
255
|
-
// if the request URL contains any of the excluded rewrite routes, we assume the response does not need to be server rendered.
|
|
256
|
-
// instead, the response should just be relayed as usual.
|
|
257
|
-
if (isUrlIgnored(request.originalUrl, config, true)) {
|
|
258
|
-
return Promise.resolve(undefined);
|
|
259
|
-
}
|
|
260
|
-
// your first thought might be: why do we need to render the app here? why not just pass the JSON response to another piece of middleware that will render the app?
|
|
261
|
-
// the answer: the proxy middleware ends the response and does not "chain"
|
|
262
|
-
return renderAppToResponse(proxyResponse, request, serverResponse, renderer, config);
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* @param {string} reqPath
|
|
266
|
-
* @param {Request} req
|
|
267
|
-
* @param {ProxyConfig} config
|
|
268
|
-
* @param {RouteUrlParser} parseRouteUrl
|
|
269
|
-
*/
|
|
270
|
-
function rewriteRequestPath(reqPath, req, config, parseRouteUrl) {
|
|
271
|
-
// the path comes in URL-encoded by default,
|
|
272
|
-
// but we don't want that because...
|
|
273
|
-
// 1. We need to URL-encode it before we send it out to the Layout Service, if it matches a route
|
|
274
|
-
// 2. We don't want to force people to URL-encode ignored routes, etc (just use spaces instead of %20, etc)
|
|
275
|
-
const decodedReqPath = decodeURIComponent(reqPath);
|
|
276
|
-
// if the request URL contains a path/route that should not be re-written, then just pass it along as-is
|
|
277
|
-
if (isUrlIgnored(reqPath, config)) {
|
|
278
|
-
// we do not return the decoded URL because we're using it verbatim - should be encoded.
|
|
279
|
-
return reqPath;
|
|
280
|
-
}
|
|
281
|
-
// if the request URL doesn't contain the layout service controller path, assume we need to rewrite the request URL so that it does
|
|
282
|
-
// if this seems redundant, it is. the config.pathRewriteExcludeRoutes should contain the layout service path, but can't always assume that it will...
|
|
283
|
-
if (decodedReqPath.indexOf(config.layoutServiceRoute) !== -1) {
|
|
284
|
-
return reqPath;
|
|
285
|
-
}
|
|
286
|
-
let finalReqPath = decodedReqPath;
|
|
287
|
-
const qsIndex = finalReqPath.indexOf('?');
|
|
288
|
-
let qs = '';
|
|
289
|
-
if (qsIndex > -1 || Object.keys(req.query).length) {
|
|
290
|
-
qs = (0, util_1.buildQueryString)(req.query);
|
|
291
|
-
// Splice qs part when url contains that
|
|
292
|
-
if (qsIndex > -1)
|
|
293
|
-
finalReqPath = finalReqPath.slice(0, qsIndex);
|
|
294
|
-
}
|
|
295
|
-
if (config.qsParams) {
|
|
296
|
-
if (qs) {
|
|
297
|
-
qs += '&';
|
|
298
|
-
}
|
|
299
|
-
qs += `${config.qsParams}`;
|
|
300
|
-
}
|
|
301
|
-
let lang;
|
|
302
|
-
if (parseRouteUrl) {
|
|
303
|
-
if (config.debug) {
|
|
304
|
-
console.log(`DEBUG: Parsing route URL using ${decodedReqPath} URL...`);
|
|
305
|
-
}
|
|
306
|
-
const routeParams = parseRouteUrl(finalReqPath);
|
|
307
|
-
if (routeParams) {
|
|
308
|
-
if (routeParams.sitecoreRoute) {
|
|
309
|
-
finalReqPath = routeParams.sitecoreRoute;
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
finalReqPath = '/';
|
|
313
|
-
}
|
|
314
|
-
if (!finalReqPath.startsWith('/')) {
|
|
315
|
-
finalReqPath = `/${finalReqPath}`;
|
|
316
|
-
}
|
|
317
|
-
lang = routeParams.lang;
|
|
318
|
-
if (routeParams.qsParams) {
|
|
319
|
-
qs += `&${routeParams.qsParams}`;
|
|
320
|
-
}
|
|
321
|
-
if (config.debug) {
|
|
322
|
-
console.log('DEBUG: parseRouteUrl() result', routeParams);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
let path = `${config.layoutServiceRoute}?item=${encodeURIComponent(finalReqPath)}&sc_apikey=${config.apiKey}`;
|
|
327
|
-
if (lang) {
|
|
328
|
-
path = `${path}&sc_lang=${lang}`;
|
|
329
|
-
}
|
|
330
|
-
if (qs) {
|
|
331
|
-
path = `${path}&${qs}`;
|
|
332
|
-
}
|
|
333
|
-
return path;
|
|
334
|
-
}
|
|
335
|
-
exports.rewriteRequestPath = rewriteRequestPath;
|
|
336
|
-
/**
|
|
337
|
-
* @param {string} originalUrl
|
|
338
|
-
* @param {ProxyConfig} config
|
|
339
|
-
* @param {boolean} noDebug
|
|
340
|
-
*/
|
|
341
|
-
function isUrlIgnored(originalUrl, config, noDebug = false) {
|
|
342
|
-
if (config.pathRewriteExcludePredicate && config.pathRewriteExcludeRoutes) {
|
|
343
|
-
console.error('ERROR: pathRewriteExcludePredicate and pathRewriteExcludeRoutes were both provided in config. Provide only one.');
|
|
344
|
-
process.exit(1);
|
|
345
|
-
}
|
|
346
|
-
let result = null;
|
|
347
|
-
if (config.pathRewriteExcludeRoutes) {
|
|
348
|
-
const matchRoute = decodeURIComponent(originalUrl).toUpperCase();
|
|
349
|
-
result = config.pathRewriteExcludeRoutes.find((excludedRoute) => excludedRoute.length > 0 && matchRoute.startsWith(excludedRoute));
|
|
350
|
-
if (!noDebug && config.debug) {
|
|
351
|
-
if (!result) {
|
|
352
|
-
console.log(`DEBUG: URL ${originalUrl} did not match the proxy exclude list, and will be treated as a layout service route to render. Excludes:`, config.pathRewriteExcludeRoutes);
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
console.log(`DEBUG: URL ${originalUrl} matched the proxy exclude list and will be served verbatim as received. Excludes: `, config.pathRewriteExcludeRoutes);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return result ? true : false;
|
|
359
|
-
}
|
|
360
|
-
if (config.pathRewriteExcludePredicate) {
|
|
361
|
-
result = config.pathRewriteExcludePredicate(originalUrl);
|
|
362
|
-
if (!noDebug && config.debug) {
|
|
363
|
-
if (!result) {
|
|
364
|
-
console.log(`DEBUG: URL ${originalUrl} did not match the proxy exclude function, and will be treated as a layout service route to render.`);
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
console.log(`DEBUG: URL ${originalUrl} matched the proxy exclude function and will be served verbatim as received.`);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return result;
|
|
371
|
-
}
|
|
372
|
-
return false;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* @param {ClientRequest} proxyReq
|
|
376
|
-
* @param {Request} req
|
|
377
|
-
* @param {Response} res
|
|
378
|
-
* @param {ServerOptions} options
|
|
379
|
-
* @param {ProxyConfig} config
|
|
380
|
-
* @param {Function} customOnProxyReq
|
|
381
|
-
*/
|
|
382
|
-
function handleProxyRequest(proxyReq, req, res, options, config, customOnProxyReq) {
|
|
383
|
-
if (!isUrlIgnored(req.originalUrl, config, true)) {
|
|
384
|
-
// In case 'followRedirects' is enabled, and before the proxy was initialized we had set 'originalMethod'
|
|
385
|
-
// now we need to set req.method back to original one, since proxyReq is already initialized.
|
|
386
|
-
// See more info in 'preProxyHandler'
|
|
387
|
-
if (options.followRedirects && req.originalMethod === 'HEAD') {
|
|
388
|
-
req.method = req.originalMethod;
|
|
389
|
-
delete req.originalMethod;
|
|
390
|
-
if (config.debug) {
|
|
391
|
-
console.log('DEBUG: Rewriting HEAD request to GET to create accurate headers');
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
else if (proxyReq.method === 'HEAD') {
|
|
395
|
-
if (config.debug) {
|
|
396
|
-
console.log('DEBUG: Rewriting HEAD request to GET to create accurate headers');
|
|
397
|
-
}
|
|
398
|
-
// if a HEAD request, we still need to issue a GET so we can return accurate headers
|
|
399
|
-
proxyReq.method = 'GET';
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
// invoke custom onProxyReq
|
|
403
|
-
if (customOnProxyReq) {
|
|
404
|
-
customOnProxyReq(proxyReq, req, res, options);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* @param {AppRenderer} renderer
|
|
409
|
-
* @param {ProxyConfig} config
|
|
410
|
-
* @param {RouteUrlParser} parseRouteUrl
|
|
411
|
-
*/
|
|
412
|
-
function createOptions(renderer, config, parseRouteUrl) {
|
|
413
|
-
var _a;
|
|
414
|
-
if (!config.maxResponseSizeBytes) {
|
|
415
|
-
config.maxResponseSizeBytes = 10 * 1024 * 1024;
|
|
416
|
-
}
|
|
417
|
-
// ensure all excludes are case insensitive
|
|
418
|
-
if (config.pathRewriteExcludeRoutes && Array.isArray(config.pathRewriteExcludeRoutes)) {
|
|
419
|
-
config.pathRewriteExcludeRoutes = config.pathRewriteExcludeRoutes.map((exclude) => exclude.toUpperCase());
|
|
420
|
-
}
|
|
421
|
-
if (config.debug) {
|
|
422
|
-
console.log('DEBUG: Final proxy config', config);
|
|
423
|
-
}
|
|
424
|
-
const customOnProxyReq = (_a = config.proxyOptions) === null || _a === void 0 ? void 0 : _a.onProxyReq;
|
|
425
|
-
return Object.assign(Object.assign({}, config.proxyOptions), { target: config.apiHost, changeOrigin: true, ws: config.ws || false, pathRewrite: (reqPath, req) => rewriteRequestPath(reqPath, req, config, parseRouteUrl), logLevel: config.debug ? 'debug' : 'info', onProxyReq: (proxyReq, req, res, options) => handleProxyRequest(proxyReq, req, res, options, config, customOnProxyReq), onProxyRes: (proxyRes, req, res) => handleProxyResponse(proxyRes, req, res, renderer, config) });
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* @param {AppRenderer} renderer
|
|
429
|
-
* @param {ProxyConfig} config
|
|
430
|
-
* @param {RouteUrlParser} parseRouteUrl
|
|
431
|
-
*/
|
|
432
|
-
function scProxy(renderer, config, parseRouteUrl) {
|
|
433
|
-
const options = createOptions(renderer, config, parseRouteUrl);
|
|
434
|
-
const preProxyHandler = (req, _res, next) => {
|
|
435
|
-
// When 'followRedirects' is enabled, 'onProxyReq' is executed after 'proxyReq' is initialized based on original 'req'
|
|
436
|
-
// and there are no public properties/methods to modify Redirectable 'proxyReq'.
|
|
437
|
-
// so, we need to set 'HEAD' req as 'GET' before the proxy is initialized.
|
|
438
|
-
// During the 'onProxyReq' event we will set 'req.method' back as 'HEAD'.
|
|
439
|
-
// if a HEAD request, we need to issue a GET so we can return accurate headers
|
|
440
|
-
if (req.method === 'HEAD' &&
|
|
441
|
-
options.followRedirects &&
|
|
442
|
-
!isUrlIgnored(req.originalUrl, config, true)) {
|
|
443
|
-
req.method = 'GET';
|
|
444
|
-
req.originalMethod = 'HEAD';
|
|
445
|
-
}
|
|
446
|
-
next();
|
|
447
|
-
};
|
|
448
|
-
return [preProxyHandler, (0, http_proxy_middleware_1.createProxyMiddleware)(options)];
|
|
449
|
-
}
|
|
450
|
-
exports.default = scProxy;
|
|
17
|
+
exports.GRAPHQL_LAYOUT_QUERY_NAME = void 0;
|
|
18
|
+
__exportStar(require("./middleware"), exports);
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
__exportStar(require("./personalize"), exports);
|
|
21
|
+
var layout_1 = require("@sitecore-jss/sitecore-jss/layout");
|
|
22
|
+
Object.defineProperty(exports, "GRAPHQL_LAYOUT_QUERY_NAME", { enumerable: true, get: function () { return layout_1.GRAPHQL_LAYOUT_QUERY_NAME; } });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.editingConfigMiddleware = void 0;
|
|
4
|
+
const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
|
|
5
|
+
const layout_1 = require("@sitecore-jss/sitecore-jss/layout");
|
|
6
|
+
/**
|
|
7
|
+
* Middleware to handle editing config requests
|
|
8
|
+
* @param {EditingConfigEndpointOptions} config Configuration for the endpoint
|
|
9
|
+
* @returns {RequestHandler} Middleware function
|
|
10
|
+
*/
|
|
11
|
+
const editingConfigMiddleware = (config) => (_req, res) => {
|
|
12
|
+
sitecore_jss_1.debug.editing('editing config middleware start');
|
|
13
|
+
const startTimestamp = Date.now();
|
|
14
|
+
const components = Array.isArray(config.components)
|
|
15
|
+
? config.components
|
|
16
|
+
: Array.from(config.components.keys());
|
|
17
|
+
sitecore_jss_1.debug.editing('editing config middleware end in %dms: %o', Date.now() - startTimestamp, {
|
|
18
|
+
components,
|
|
19
|
+
packages: config.metadata.packages,
|
|
20
|
+
});
|
|
21
|
+
res.status(200).json({
|
|
22
|
+
components,
|
|
23
|
+
packages: config.metadata.packages,
|
|
24
|
+
editMode: layout_1.EditMode.Metadata,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
exports.editingConfigMiddleware = editingConfigMiddleware;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.editingRouter = exports.editingMiddleware = void 0;
|
|
13
|
+
const express_1 = require("express");
|
|
14
|
+
const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
|
|
15
|
+
const editing_1 = require("@sitecore-jss/sitecore-jss/editing");
|
|
16
|
+
const utils_1 = require("@sitecore-jss/sitecore-jss/utils");
|
|
17
|
+
const config_1 = require("./config");
|
|
18
|
+
const render_1 = require("./render");
|
|
19
|
+
/**
|
|
20
|
+
* Default endpoints for editing requests
|
|
21
|
+
*/
|
|
22
|
+
const ENDPOINTS = {
|
|
23
|
+
CONFIG: '/config',
|
|
24
|
+
RENDER: '/render',
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Middleware to handle editing requests
|
|
28
|
+
* @param {Request} req Request
|
|
29
|
+
* @param {Response} res Response
|
|
30
|
+
* @param {NextFunction} next Next function
|
|
31
|
+
*/
|
|
32
|
+
const editingMiddleware = (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
var _a;
|
|
34
|
+
const providedSecret = req.query[editing_1.QUERY_PARAM_EDITING_SECRET];
|
|
35
|
+
const secret = process.env.JSS_EDITING_SECRET;
|
|
36
|
+
sitecore_jss_1.debug.editing('editing middleware start: %o', {
|
|
37
|
+
path: req.originalUrl,
|
|
38
|
+
method: req.method,
|
|
39
|
+
query: req.query,
|
|
40
|
+
headers: req.headers,
|
|
41
|
+
});
|
|
42
|
+
if (!(0, utils_1.enforceCors)(req, res, editing_1.EDITING_ALLOWED_ORIGINS)) {
|
|
43
|
+
sitecore_jss_1.debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
|
|
44
|
+
return res.status(401).send(`Requests from origin ${(_a = req.headers) === null || _a === void 0 ? void 0 : _a.origin} are not allowed`);
|
|
45
|
+
}
|
|
46
|
+
if (!secret) {
|
|
47
|
+
sitecore_jss_1.debug.editing('missing editing secret - set JSS_EDITING_SECRET environment variable');
|
|
48
|
+
return res
|
|
49
|
+
.status(401)
|
|
50
|
+
.send('Missing editing secret - set JSS_EDITING_SECRET environment variable');
|
|
51
|
+
}
|
|
52
|
+
if (secret !== providedSecret) {
|
|
53
|
+
sitecore_jss_1.debug.editing('invalid editing secret - sent "%s" expected "%s"', providedSecret, secret);
|
|
54
|
+
return res.status(401).send('Missing or invalid secret');
|
|
55
|
+
}
|
|
56
|
+
if (req.method === 'OPTIONS') {
|
|
57
|
+
sitecore_jss_1.debug.editing('preflight request');
|
|
58
|
+
// CORS headers are set by enforceCors
|
|
59
|
+
return res.status(204).send();
|
|
60
|
+
}
|
|
61
|
+
return next();
|
|
62
|
+
});
|
|
63
|
+
exports.editingMiddleware = editingMiddleware;
|
|
64
|
+
/**
|
|
65
|
+
* Middleware to handle invalid method or path
|
|
66
|
+
* @param {Request} req Request
|
|
67
|
+
* @param {Response} res Response
|
|
68
|
+
*/
|
|
69
|
+
const editingNotFoundMiddleware = (req, res) => {
|
|
70
|
+
sitecore_jss_1.debug.editing('invalid method or path - sent %s %s', req.method, req.originalUrl);
|
|
71
|
+
return res.status(405).send(`Invalid request method or path ${req.method} ${req.originalUrl}`);
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Creates a router for editing requests.
|
|
75
|
+
* Supports the following routes:
|
|
76
|
+
* - <routerPath>/render (GET) - renders a route
|
|
77
|
+
* - <routerPath>/config (GET) - returns the current application configuration
|
|
78
|
+
* @param {EditingRouterConfig} options Editing router configuration
|
|
79
|
+
* @returns {Router} Editing router
|
|
80
|
+
*/
|
|
81
|
+
const editingRouter = (options) => {
|
|
82
|
+
const router = (0, express_1.Router)();
|
|
83
|
+
router.use(exports.editingMiddleware);
|
|
84
|
+
router.get(options.config.path || ENDPOINTS.CONFIG, (0, config_1.editingConfigMiddleware)(options.config));
|
|
85
|
+
router.get(options.render.path || ENDPOINTS.RENDER, (0, render_1.editingRenderMiddleware)(options.render));
|
|
86
|
+
router.use(editingNotFoundMiddleware);
|
|
87
|
+
return router;
|
|
88
|
+
};
|
|
89
|
+
exports.editingRouter = editingRouter;
|