@quintype/framework 7.31.1-fix-access-control.0 → 7.31.1-preview-logs.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/package.json +2 -2
- package/server/data-loader-helpers.js +33 -32
- package/server/routes.js +237 -254
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quintype/framework",
|
|
3
|
-
"version": "7.31.1-
|
|
3
|
+
"version": "7.31.1-preview-logs.0",
|
|
4
4
|
"description": "Libraries to help build Quintype Node.js apps",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@ampproject/toolbox-optimizer": "2.8.3",
|
|
34
34
|
"@quintype/amp": "^2.20.0",
|
|
35
|
-
"@quintype/backend": "^2.6.0",
|
|
35
|
+
"@quintype/backend": "^2.6.1-preview-logs.0",
|
|
36
36
|
"@quintype/components": "^3.5.0",
|
|
37
37
|
"@quintype/prerender-node": "^3.2.26",
|
|
38
38
|
"@quintype/seo": "^1.46.1",
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// istanbul ignore file
|
|
2
2
|
// This file is not documented
|
|
3
3
|
|
|
4
|
-
const { Story, Collection } = require(
|
|
5
|
-
const templateOptions = require(
|
|
4
|
+
const { Story, Collection } = require('./api-client')
|
|
5
|
+
const templateOptions = require('./impl/template-options')
|
|
6
6
|
|
|
7
|
-
exports.catalogDataLoader = function catalogDataLoader(client, config) {
|
|
8
|
-
return Story.getStories(client).then(
|
|
7
|
+
exports.catalogDataLoader = function catalogDataLoader (client, config) {
|
|
8
|
+
return Story.getStories(client).then(stories => {
|
|
9
9
|
return {
|
|
10
|
-
stories: stories.map(
|
|
10
|
+
stories: stories.map(story => story.asJson()),
|
|
11
11
|
cacheKeys: [`static`],
|
|
12
|
-
templateOptions
|
|
13
|
-
}
|
|
14
|
-
})
|
|
15
|
-
}
|
|
12
|
+
templateOptions
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
16
|
|
|
17
|
-
exports.homeCollectionOrStories = function homeCollectionOrStories(
|
|
17
|
+
exports.homeCollectionOrStories = function homeCollectionOrStories (
|
|
18
18
|
client,
|
|
19
19
|
depth = 1,
|
|
20
20
|
getStoryLimits,
|
|
@@ -23,12 +23,12 @@ exports.homeCollectionOrStories = function homeCollectionOrStories(
|
|
|
23
23
|
customLayouts = [],
|
|
24
24
|
defaultNestedLimit = null
|
|
25
25
|
) {
|
|
26
|
-
const {qtInternalAppsKey =
|
|
27
|
-
|
|
26
|
+
const { qtInternalAppsKey = '', previewId = '', ...rest } = params || {}
|
|
27
|
+
console.log('homeCollectionOrStories------', params)
|
|
28
28
|
return Collection.getCollectionBySlug(
|
|
29
29
|
client,
|
|
30
|
-
|
|
31
|
-
{
|
|
30
|
+
'home',
|
|
31
|
+
{ 'item-type': 'collection', ...rest },
|
|
32
32
|
{
|
|
33
33
|
depth,
|
|
34
34
|
...(getStoryLimits && { storyLimits: getStoryLimits() }),
|
|
@@ -38,25 +38,26 @@ exports.homeCollectionOrStories = function homeCollectionOrStories(
|
|
|
38
38
|
qtInternalAppsKey,
|
|
39
39
|
previewId
|
|
40
40
|
}
|
|
41
|
-
).then(
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
).then(collection => {
|
|
42
|
+
console.log('collection homeCollectionOrStories---', collection)
|
|
43
|
+
if (collection) return collection
|
|
44
|
+
return Story.getStories(client).then(stories =>
|
|
44
45
|
Collection.build({
|
|
45
|
-
slug:
|
|
46
|
-
name:
|
|
47
|
-
template:
|
|
46
|
+
slug: 'home',
|
|
47
|
+
name: 'Home',
|
|
48
|
+
template: 'default',
|
|
48
49
|
items: [
|
|
49
50
|
{
|
|
50
|
-
type:
|
|
51
|
-
name:
|
|
52
|
-
template:
|
|
53
|
-
items: stories.map(
|
|
54
|
-
type:
|
|
55
|
-
story: story.asJson()
|
|
56
|
-
}))
|
|
57
|
-
}
|
|
58
|
-
]
|
|
51
|
+
type: 'collection',
|
|
52
|
+
name: 'Home',
|
|
53
|
+
template: 'default',
|
|
54
|
+
items: stories.map(story => ({
|
|
55
|
+
type: 'story',
|
|
56
|
+
story: story.asJson()
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
]
|
|
59
60
|
})
|
|
60
|
-
)
|
|
61
|
-
})
|
|
62
|
-
}
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
}
|
package/server/routes.js
CHANGED
|
@@ -6,29 +6,29 @@
|
|
|
6
6
|
* @category Server
|
|
7
7
|
* @module routes
|
|
8
8
|
*/
|
|
9
|
-
const { match } = require(
|
|
10
|
-
const { generateServiceWorker } = require(
|
|
9
|
+
const { match } = require("path-to-regexp");
|
|
10
|
+
const { generateServiceWorker } = require("./handlers/generate-service-worker");
|
|
11
11
|
const {
|
|
12
12
|
handleIsomorphicShell,
|
|
13
13
|
handleIsomorphicDataLoad,
|
|
14
14
|
handleIsomorphicRoute,
|
|
15
15
|
handleStaticRoute,
|
|
16
|
-
notFoundHandler
|
|
17
|
-
} = require(
|
|
18
|
-
|
|
19
|
-
const { oneSignalImport } = require(
|
|
20
|
-
const { customRouteHandler } = require(
|
|
21
|
-
const { handleManifest, handleAssetLink } = require(
|
|
22
|
-
const { redirectStory } = require(
|
|
23
|
-
const { simpleJsonHandler } = require(
|
|
24
|
-
const { makePickComponentSync } = require(
|
|
25
|
-
const { registerFCMTopic } = require(
|
|
26
|
-
const { triggerWebengageNotifications } = require(
|
|
27
|
-
const rp = require(
|
|
28
|
-
const bodyParser = require(
|
|
29
|
-
const get = require(
|
|
30
|
-
const { URL } = require(
|
|
31
|
-
const prerender = require(
|
|
16
|
+
notFoundHandler,
|
|
17
|
+
} = require("./handlers/isomorphic-handler");
|
|
18
|
+
|
|
19
|
+
const { oneSignalImport } = require("./handlers/one-signal");
|
|
20
|
+
const { customRouteHandler } = require("./handlers/custom-route-handler");
|
|
21
|
+
const { handleManifest, handleAssetLink } = require("./handlers/json-manifest-handlers");
|
|
22
|
+
const { redirectStory } = require("./handlers/story-redirect");
|
|
23
|
+
const { simpleJsonHandler } = require("./handlers/simple-json-handler");
|
|
24
|
+
const { makePickComponentSync } = require("../isomorphic/impl/make-pick-component-sync");
|
|
25
|
+
const { registerFCMTopic } = require("./handlers/fcm-registration-handler");
|
|
26
|
+
const { triggerWebengageNotifications } = require("./handlers/webengage-notifications");
|
|
27
|
+
const rp = require("request-promise");
|
|
28
|
+
const bodyParser = require("body-parser");
|
|
29
|
+
const get = require("lodash/get");
|
|
30
|
+
const { URL } = require("url");
|
|
31
|
+
const prerender = require("@quintype/prerender-node");
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* *upstreamQuintypeRoutes* connects various routes directly to the upstream API server.
|
|
@@ -43,7 +43,7 @@ const prerender = require('@quintype/prerender-node')
|
|
|
43
43
|
* @param {boolean} opts.forwardFavicon Forward favicon requests to the CMS (default false)
|
|
44
44
|
* @param {boolean} opts.isSitemapUrlEnabled To enable /news_sitemap/today and /news_sitemap/yesterday sitemap news url (default /news_sitemap.xml)
|
|
45
45
|
*/
|
|
46
|
-
exports.upstreamQuintypeRoutes = function upstreamQuintypeRoutes
|
|
46
|
+
exports.upstreamQuintypeRoutes = function upstreamQuintypeRoutes(
|
|
47
47
|
app,
|
|
48
48
|
{
|
|
49
49
|
forwardAmp = false,
|
|
@@ -51,138 +51,138 @@ exports.upstreamQuintypeRoutes = function upstreamQuintypeRoutes (
|
|
|
51
51
|
extraRoutes = [],
|
|
52
52
|
sMaxAge,
|
|
53
53
|
maxAge,
|
|
54
|
-
config = require(
|
|
55
|
-
getClient = require(
|
|
56
|
-
isSitemapUrlEnabled = false
|
|
54
|
+
config = require("./publisher-config"),
|
|
55
|
+
getClient = require("./api-client").getClient,
|
|
56
|
+
isSitemapUrlEnabled = false,
|
|
57
57
|
} = {}
|
|
58
58
|
) {
|
|
59
|
-
const host = config.sketches_host
|
|
60
|
-
const get = require(
|
|
61
|
-
const apiProxy = require(
|
|
59
|
+
const host = config.sketches_host;
|
|
60
|
+
const get = require("lodash/get");
|
|
61
|
+
const apiProxy = require("http-proxy").createProxyServer({
|
|
62
62
|
target: host,
|
|
63
|
-
ssl: host.startsWith(
|
|
64
|
-
})
|
|
63
|
+
ssl: host.startsWith("https") ? { servername: host.replace(/^https:\/\//, "") } : undefined,
|
|
64
|
+
});
|
|
65
65
|
|
|
66
|
-
apiProxy.on(
|
|
67
|
-
proxyReq.setHeader(
|
|
68
|
-
})
|
|
66
|
+
apiProxy.on("proxyReq", (proxyReq, req, res, options) => {
|
|
67
|
+
proxyReq.setHeader("Host", getClient(req.hostname).getHostname());
|
|
68
|
+
});
|
|
69
69
|
|
|
70
|
-
const _sMaxAge = get(config, [
|
|
71
|
-
const _maxAge = get(config, [
|
|
70
|
+
const _sMaxAge = get(config, ["publisher", "upstreamRoutesSmaxage"], sMaxAge);
|
|
71
|
+
const _maxAge = get(config, ["publisher", "upstreamRoutesMaxage"], maxAge);
|
|
72
72
|
|
|
73
73
|
parseInt(_sMaxAge) > 0 &&
|
|
74
|
-
apiProxy.on(
|
|
75
|
-
const pathName = get(req, [
|
|
76
|
-
const checkForExcludeRoutes = excludeRoutes.some(path => {
|
|
77
|
-
const matchFn = match(path, { decode: decodeURIComponent })
|
|
78
|
-
return matchFn(pathName)
|
|
79
|
-
})
|
|
80
|
-
const getCacheControl = get(proxyRes, [
|
|
81
|
-
if (!checkForExcludeRoutes && getCacheControl.includes(
|
|
82
|
-
proxyRes.headers[
|
|
74
|
+
apiProxy.on("proxyRes", function (proxyRes, req) {
|
|
75
|
+
const pathName = get(req, ["originalUrl"], "").split("?")[0];
|
|
76
|
+
const checkForExcludeRoutes = excludeRoutes.some((path) => {
|
|
77
|
+
const matchFn = match(path, { decode: decodeURIComponent });
|
|
78
|
+
return matchFn(pathName);
|
|
79
|
+
});
|
|
80
|
+
const getCacheControl = get(proxyRes, ["headers", "cache-control"], "");
|
|
81
|
+
if (!checkForExcludeRoutes && getCacheControl.includes("public")) {
|
|
82
|
+
proxyRes.headers["cache-control"] = getCacheControl.replace(/s-maxage=\d*/g, `s-maxage=${_sMaxAge}`);
|
|
83
83
|
}
|
|
84
|
-
})
|
|
84
|
+
});
|
|
85
85
|
parseInt(_maxAge) > 0 &&
|
|
86
|
-
apiProxy.on(
|
|
87
|
-
const pathName = get(req, [
|
|
88
|
-
const checkForExcludeRoutes = excludeRoutes.some(path => {
|
|
89
|
-
const matchFn = match(path, { decode: decodeURIComponent })
|
|
90
|
-
return matchFn(pathName)
|
|
91
|
-
})
|
|
92
|
-
const getCacheControl = get(proxyRes, [
|
|
93
|
-
if (!checkForExcludeRoutes && getCacheControl.includes(
|
|
94
|
-
proxyRes.headers[
|
|
86
|
+
apiProxy.on("proxyRes", function (proxyRes, req) {
|
|
87
|
+
const pathName = get(req, ["originalUrl"], "").split("?")[0];
|
|
88
|
+
const checkForExcludeRoutes = excludeRoutes.some((path) => {
|
|
89
|
+
const matchFn = match(path, { decode: decodeURIComponent });
|
|
90
|
+
return matchFn(pathName);
|
|
91
|
+
});
|
|
92
|
+
const getCacheControl = get(proxyRes, ["headers", "cache-control"], "");
|
|
93
|
+
if (!checkForExcludeRoutes && getCacheControl.includes("public")) {
|
|
94
|
+
proxyRes.headers["cache-control"] = getCacheControl.replace(/max-age=\d*/g, `max-age=${_maxAge}`);
|
|
95
95
|
}
|
|
96
|
-
})
|
|
96
|
+
});
|
|
97
97
|
|
|
98
|
-
const sketchesProxy = (req, res) => apiProxy.web(req, res)
|
|
98
|
+
const sketchesProxy = (req, res) => apiProxy.web(req, res);
|
|
99
99
|
|
|
100
|
-
app.get(
|
|
100
|
+
app.get("/ping", (req, res) => {
|
|
101
101
|
getClient(req.hostname)
|
|
102
102
|
.getConfig()
|
|
103
|
-
.then(() => res.send(
|
|
104
|
-
.catch(() => res.status(503).send({ error: { message:
|
|
105
|
-
})
|
|
103
|
+
.then(() => res.send("pong"))
|
|
104
|
+
.catch(() => res.status(503).send({ error: { message: "Config not loaded" } }));
|
|
105
|
+
});
|
|
106
106
|
|
|
107
107
|
// Mention the routes which don't want to override the s-maxage value and max-age value
|
|
108
108
|
const excludeRoutes = [
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
]
|
|
116
|
-
|
|
117
|
-
app.all(
|
|
118
|
-
app.all(
|
|
119
|
-
app.all(
|
|
120
|
-
app.all(
|
|
121
|
-
app.all(
|
|
122
|
-
app.all(
|
|
123
|
-
app.all(
|
|
124
|
-
app.all(
|
|
125
|
-
app.all(
|
|
126
|
-
app.all(
|
|
127
|
-
app.all(
|
|
128
|
-
app.all(
|
|
129
|
-
app.all(
|
|
130
|
-
app.all(
|
|
131
|
-
app.all(
|
|
109
|
+
"/qlitics.js",
|
|
110
|
+
"/api/v1/breaking-news",
|
|
111
|
+
"/stories.rss",
|
|
112
|
+
"/api/v1/collections/:slug.rss",
|
|
113
|
+
"/api/v1/advanced-search",
|
|
114
|
+
"/api/instant-articles.rss",
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
app.all("/api/*", sketchesProxy);
|
|
118
|
+
app.all("*/api/*", sketchesProxy);
|
|
119
|
+
app.all("/login", sketchesProxy);
|
|
120
|
+
app.all("/qlitics.js", sketchesProxy);
|
|
121
|
+
app.all("/auth.form", sketchesProxy);
|
|
122
|
+
app.all("/auth.callback", sketchesProxy);
|
|
123
|
+
app.all("/auth", sketchesProxy);
|
|
124
|
+
app.all("/admin/*", sketchesProxy);
|
|
125
|
+
app.all("/sitemap.xml", sketchesProxy);
|
|
126
|
+
app.all("/sitemap/*", sketchesProxy);
|
|
127
|
+
app.all("/feed", sketchesProxy);
|
|
128
|
+
app.all("/rss-feed", sketchesProxy);
|
|
129
|
+
app.all("/stories.rss", sketchesProxy);
|
|
130
|
+
app.all("/sso-login", sketchesProxy);
|
|
131
|
+
app.all("/sso-signup", sketchesProxy);
|
|
132
132
|
if (isSitemapUrlEnabled) {
|
|
133
|
-
app.all(
|
|
134
|
-
app.all(
|
|
133
|
+
app.all("/news_sitemap/today.xml", sketchesProxy);
|
|
134
|
+
app.all("/news_sitemap/yesterday.xml", sketchesProxy);
|
|
135
135
|
} else {
|
|
136
|
-
app.all(
|
|
136
|
+
app.all("/news_sitemap.xml", sketchesProxy);
|
|
137
137
|
}
|
|
138
138
|
if (forwardAmp) {
|
|
139
|
-
app.get(
|
|
139
|
+
app.get("/amp/*", sketchesProxy);
|
|
140
140
|
}
|
|
141
141
|
if (forwardFavicon) {
|
|
142
|
-
app.get(
|
|
142
|
+
app.get("/favicon.ico", sketchesProxy);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
extraRoutes.forEach(route => app.all(route, sketchesProxy))
|
|
146
|
-
}
|
|
145
|
+
extraRoutes.forEach((route) => app.all(route, sketchesProxy));
|
|
146
|
+
};
|
|
147
147
|
|
|
148
148
|
// istanbul ignore next
|
|
149
|
-
function renderServiceWorkerFn
|
|
150
|
-
return res.render(layout, params, callback)
|
|
149
|
+
function renderServiceWorkerFn(res, layout, params, callback) {
|
|
150
|
+
return res.render(layout, params, callback);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// istanbul ignore next
|
|
154
|
-
function toFunction
|
|
154
|
+
function toFunction(value, toRequire) {
|
|
155
155
|
if (value === true) {
|
|
156
|
-
value = require(toRequire)
|
|
156
|
+
value = require(toRequire);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
if (typeof value ===
|
|
160
|
-
return value
|
|
159
|
+
if (typeof value === "function") {
|
|
160
|
+
return value;
|
|
161
161
|
}
|
|
162
|
-
return () => value
|
|
162
|
+
return () => value;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
function getDomainSlug
|
|
165
|
+
function getDomainSlug(publisherConfig, hostName) {
|
|
166
166
|
if (!publisherConfig.domain_mapping) {
|
|
167
|
-
return undefined
|
|
167
|
+
return undefined;
|
|
168
168
|
}
|
|
169
|
-
return publisherConfig.domain_mapping[hostName] || null
|
|
169
|
+
return publisherConfig.domain_mapping[hostName] || null;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
function withConfigPartial
|
|
172
|
+
function withConfigPartial(
|
|
173
173
|
getClient,
|
|
174
174
|
logError,
|
|
175
|
-
publisherConfig = require(
|
|
176
|
-
configWrapper = config => config
|
|
175
|
+
publisherConfig = require("./publisher-config"),
|
|
176
|
+
configWrapper = (config) => config
|
|
177
177
|
) {
|
|
178
|
-
return function withConfig
|
|
178
|
+
return function withConfig(f, staticParams) {
|
|
179
179
|
return function (req, res, next) {
|
|
180
|
-
const domainSlug = getDomainSlug(publisherConfig, req.hostname)
|
|
181
|
-
const client = getClient(req.hostname)
|
|
180
|
+
const domainSlug = getDomainSlug(publisherConfig, req.hostname);
|
|
181
|
+
const client = getClient(req.hostname);
|
|
182
182
|
return client
|
|
183
183
|
.getConfig()
|
|
184
|
-
.then(config => configWrapper(config, domainSlug, { req }))
|
|
185
|
-
.then(config =>
|
|
184
|
+
.then((config) => configWrapper(config, domainSlug, { req }))
|
|
185
|
+
.then((config) =>
|
|
186
186
|
f(
|
|
187
187
|
req,
|
|
188
188
|
res,
|
|
@@ -190,52 +190,52 @@ function withConfigPartial (
|
|
|
190
190
|
Object.assign({}, staticParams, {
|
|
191
191
|
config,
|
|
192
192
|
client,
|
|
193
|
-
domainSlug
|
|
193
|
+
domainSlug,
|
|
194
194
|
})
|
|
195
195
|
)
|
|
196
196
|
)
|
|
197
|
-
.catch(logError)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
197
|
+
.catch(logError);
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
exports.withError = function withError
|
|
202
|
+
exports.withError = function withError(handler, logError) {
|
|
203
203
|
return async (req, res, next, opts) => {
|
|
204
204
|
try {
|
|
205
|
-
await handler(req, res, next, opts)
|
|
205
|
+
await handler(req, res, next, opts);
|
|
206
206
|
} catch (e) {
|
|
207
|
-
logError(e)
|
|
208
|
-
res.status(500)
|
|
209
|
-
res.end()
|
|
207
|
+
logError(e);
|
|
208
|
+
res.status(500);
|
|
209
|
+
res.end();
|
|
210
210
|
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
213
|
|
|
214
|
-
function convertToDomain
|
|
214
|
+
function convertToDomain(path) {
|
|
215
215
|
if (!path) {
|
|
216
|
-
return path
|
|
216
|
+
return path;
|
|
217
217
|
}
|
|
218
|
-
return new URL(path).origin
|
|
218
|
+
return new URL(path).origin;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
function wrapLoadDataWithMultiDomain
|
|
222
|
-
return async function loadDataWrapped
|
|
223
|
-
const { domainSlug } = arguments[arguments.length - 1]
|
|
224
|
-
const config = arguments[configPos]
|
|
225
|
-
const primaryHostUrl = convertToDomain(config[
|
|
226
|
-
const domain = (config.domains || []).find(d => d.slug === domainSlug) || {
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
const result = await f.apply(this, arguments)
|
|
221
|
+
function wrapLoadDataWithMultiDomain(publisherConfig, f, configPos) {
|
|
222
|
+
return async function loadDataWrapped() {
|
|
223
|
+
const { domainSlug } = arguments[arguments.length - 1];
|
|
224
|
+
const config = arguments[configPos];
|
|
225
|
+
const primaryHostUrl = convertToDomain(config["sketches-host"]);
|
|
226
|
+
const domain = (config.domains || []).find((d) => d.slug === domainSlug) || {
|
|
227
|
+
"host-url": primaryHostUrl,
|
|
228
|
+
};
|
|
229
|
+
const result = await f.apply(this, arguments);
|
|
230
230
|
return Object.assign(
|
|
231
231
|
{
|
|
232
232
|
domainSlug,
|
|
233
|
-
currentHostUrl: convertToDomain(domain[
|
|
234
|
-
primaryHostUrl
|
|
233
|
+
currentHostUrl: convertToDomain(domain["host-url"]),
|
|
234
|
+
primaryHostUrl,
|
|
235
235
|
},
|
|
236
236
|
result
|
|
237
|
-
)
|
|
238
|
-
}
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
/**
|
|
@@ -257,15 +257,15 @@ function wrapLoadDataWithMultiDomain (publisherConfig, f, configPos) {
|
|
|
257
257
|
* @param {module:routes~Handler} handler The Handler to run
|
|
258
258
|
* @param {Object} opts Options that will be passed to the handler. These options will be merged with a *config* and *client*
|
|
259
259
|
*/
|
|
260
|
-
function getWithConfig
|
|
261
|
-
const configWrapper = opts.configWrapper
|
|
260
|
+
function getWithConfig(app, route, handler, opts = {}) {
|
|
261
|
+
const configWrapper = opts.configWrapper;
|
|
262
262
|
const {
|
|
263
|
-
getClient = require(
|
|
264
|
-
publisherConfig = require(
|
|
265
|
-
logError = require(
|
|
266
|
-
} = opts
|
|
267
|
-
const withConfig = withConfigPartial(getClient, logError, publisherConfig, configWrapper)
|
|
268
|
-
app.get(route, withConfig(handler, opts))
|
|
263
|
+
getClient = require("./api-client").getClient,
|
|
264
|
+
publisherConfig = require("./publisher-config"),
|
|
265
|
+
logError = require("./logger").error,
|
|
266
|
+
} = opts;
|
|
267
|
+
const withConfig = withConfigPartial(getClient, logError, publisherConfig, configWrapper);
|
|
268
|
+
app.get(route, withConfig(handler, opts));
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
/**
|
|
@@ -309,7 +309,7 @@ function getWithConfig (app, route, handler, opts = {}) {
|
|
|
309
309
|
* @param {boolean|function} enableExternalStories If set to true, then for every request an external story api call is made and renders the story-page if the story is found. (default: false)
|
|
310
310
|
* @param {string|function} externalIdPattern This string specifies the external id pattern the in the url. Mention `EXTERNAL_ID` to specify the position of external id in the url. Ex: "/parent-section/child-section/EXTERNAL_ID"
|
|
311
311
|
*/
|
|
312
|
-
exports.isomorphicRoutes = function isomorphicRoutes
|
|
312
|
+
exports.isomorphicRoutes = function isomorphicRoutes(
|
|
313
313
|
app,
|
|
314
314
|
{
|
|
315
315
|
generateRoutes,
|
|
@@ -320,7 +320,7 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
320
320
|
seo,
|
|
321
321
|
manifestFn,
|
|
322
322
|
assetLinkFn,
|
|
323
|
-
ampPageBasePath =
|
|
323
|
+
ampPageBasePath = "/amp/story",
|
|
324
324
|
|
|
325
325
|
oneSignalServiceWorkers = false,
|
|
326
326
|
staticRoutes = [],
|
|
@@ -334,74 +334,57 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
334
334
|
mobileConfigFields = {},
|
|
335
335
|
templateOptions = false,
|
|
336
336
|
lightPages = false,
|
|
337
|
-
cdnProvider =
|
|
338
|
-
serviceWorkerPaths = [
|
|
339
|
-
maxConfigVersion = config => get(config, [
|
|
340
|
-
configWrapper = config => config,
|
|
337
|
+
cdnProvider = "cloudflare",
|
|
338
|
+
serviceWorkerPaths = ["/service-worker.js"],
|
|
339
|
+
maxConfigVersion = (config) => get(config, ["theme-attributes", "cache-burst"], 0),
|
|
340
|
+
configWrapper = (config) => config,
|
|
341
341
|
|
|
342
342
|
// The below are primarily for testing
|
|
343
|
-
logError = require(
|
|
344
|
-
assetHelper = require(
|
|
345
|
-
getClient = require(
|
|
343
|
+
logError = require("./logger").error,
|
|
344
|
+
assetHelper = require("./asset-helper"),
|
|
345
|
+
getClient = require("./api-client").getClient,
|
|
346
346
|
renderServiceWorker = renderServiceWorkerFn,
|
|
347
|
-
publisherConfig = require(
|
|
347
|
+
publisherConfig = require("./publisher-config"),
|
|
348
348
|
redirectUrls = [],
|
|
349
|
-
prerenderServiceUrl =
|
|
349
|
+
prerenderServiceUrl = "",
|
|
350
350
|
redirectToLowercaseSlugs = false,
|
|
351
351
|
shouldEncodeAmpUri,
|
|
352
352
|
sMaxAge = 900,
|
|
353
353
|
maxAge = 15,
|
|
354
|
-
appLoadingPlaceholder =
|
|
355
|
-
fcmServerKey =
|
|
354
|
+
appLoadingPlaceholder = "",
|
|
355
|
+
fcmServerKey = "",
|
|
356
356
|
webengageConfig = {},
|
|
357
|
-
externalIdPattern =
|
|
357
|
+
externalIdPattern = "",
|
|
358
358
|
enableExternalStories = false,
|
|
359
|
-
lazyLoadImageMargin
|
|
359
|
+
lazyLoadImageMargin,
|
|
360
360
|
}
|
|
361
361
|
) {
|
|
362
|
-
const withConfig = withConfigPartial(getClient, logError, publisherConfig, configWrapper)
|
|
362
|
+
const withConfig = withConfigPartial(getClient, logError, publisherConfig, configWrapper);
|
|
363
363
|
|
|
364
|
-
const _sMaxAge = parseInt(get(publisherConfig, [
|
|
364
|
+
const _sMaxAge = parseInt(get(publisherConfig, ["publisher", "isomorphicRoutesSmaxage"], sMaxAge));
|
|
365
365
|
|
|
366
|
-
const _maxAge = parseInt(get(publisherConfig, [
|
|
366
|
+
const _maxAge = parseInt(get(publisherConfig, ["publisher", "isomorphicRoutesMaxage"], maxAge));
|
|
367
367
|
|
|
368
|
-
pickComponent = makePickComponentSync(pickComponent)
|
|
369
|
-
loadData = wrapLoadDataWithMultiDomain(publisherConfig, loadData, 2)
|
|
370
|
-
loadErrorData = wrapLoadDataWithMultiDomain(publisherConfig, loadErrorData, 1)
|
|
368
|
+
pickComponent = makePickComponentSync(pickComponent);
|
|
369
|
+
loadData = wrapLoadDataWithMultiDomain(publisherConfig, loadData, 2);
|
|
370
|
+
loadErrorData = wrapLoadDataWithMultiDomain(publisherConfig, loadErrorData, 1);
|
|
371
371
|
|
|
372
372
|
if (prerenderServiceUrl) {
|
|
373
373
|
app.use((req, res, next) => {
|
|
374
374
|
if (req.query.prerender) {
|
|
375
375
|
try {
|
|
376
376
|
// eslint-disable-next-line global-require
|
|
377
|
-
prerender.set(
|
|
378
|
-
prerender.set(
|
|
377
|
+
prerender.set("protocol", "https");
|
|
378
|
+
prerender.set("prerenderServiceUrl", prerenderServiceUrl)(req, res, next);
|
|
379
379
|
} catch (e) {
|
|
380
|
-
logError(e)
|
|
380
|
+
logError(e);
|
|
381
381
|
}
|
|
382
382
|
} else {
|
|
383
|
-
next()
|
|
383
|
+
next();
|
|
384
384
|
}
|
|
385
|
-
})
|
|
385
|
+
});
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
app.use((req, res, next) => {
|
|
389
|
-
const origin = req.headers.origin
|
|
390
|
-
const allowedOriginRegex = /^https?:\/\/([a-zA-Z0-9-]+\.)*quintype\.com$/
|
|
391
|
-
|
|
392
|
-
if (allowedOriginRegex.test(origin)) {
|
|
393
|
-
res.setHeader('Access-Control-Allow-Origin', origin)
|
|
394
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET')
|
|
395
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
|
396
|
-
|
|
397
|
-
if (req.method === 'OPTIONS') {
|
|
398
|
-
res.sendStatus(204)
|
|
399
|
-
return
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
next()
|
|
403
|
-
})
|
|
404
|
-
|
|
405
388
|
if (serviceWorkerPaths.length > 0) {
|
|
406
389
|
app.get(
|
|
407
390
|
serviceWorkerPaths,
|
|
@@ -409,36 +392,36 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
409
392
|
generateRoutes,
|
|
410
393
|
assetHelper,
|
|
411
394
|
renderServiceWorker,
|
|
412
|
-
maxConfigVersion
|
|
395
|
+
maxConfigVersion,
|
|
413
396
|
})
|
|
414
|
-
)
|
|
397
|
+
);
|
|
415
398
|
}
|
|
416
399
|
|
|
417
400
|
if (oneSignalServiceWorkers) {
|
|
418
401
|
app.get(
|
|
419
|
-
|
|
402
|
+
"/OneSignalSDKWorker.js",
|
|
420
403
|
withConfig(generateServiceWorker, {
|
|
421
404
|
generateRoutes,
|
|
422
405
|
renderServiceWorker,
|
|
423
406
|
assetHelper,
|
|
424
407
|
appendFn: oneSignalImport,
|
|
425
|
-
maxConfigVersion
|
|
408
|
+
maxConfigVersion,
|
|
426
409
|
})
|
|
427
|
-
)
|
|
410
|
+
);
|
|
428
411
|
app.get(
|
|
429
|
-
|
|
412
|
+
"/OneSignalSDKUpdaterWorker.js",
|
|
430
413
|
withConfig(generateServiceWorker, {
|
|
431
414
|
generateRoutes,
|
|
432
415
|
renderServiceWorker,
|
|
433
416
|
assetHelper,
|
|
434
417
|
appendFn: oneSignalImport,
|
|
435
|
-
maxConfigVersion
|
|
418
|
+
maxConfigVersion,
|
|
436
419
|
})
|
|
437
|
-
)
|
|
420
|
+
);
|
|
438
421
|
}
|
|
439
422
|
|
|
440
423
|
app.get(
|
|
441
|
-
|
|
424
|
+
"/shell.html",
|
|
442
425
|
withConfig(handleIsomorphicShell, {
|
|
443
426
|
seo,
|
|
444
427
|
renderLayout,
|
|
@@ -448,11 +431,11 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
448
431
|
logError,
|
|
449
432
|
preloadJs,
|
|
450
433
|
maxConfigVersion,
|
|
451
|
-
appLoadingPlaceholder
|
|
434
|
+
appLoadingPlaceholder,
|
|
452
435
|
})
|
|
453
|
-
)
|
|
436
|
+
);
|
|
454
437
|
app.get(
|
|
455
|
-
|
|
438
|
+
"/route-data.json",
|
|
456
439
|
withConfig(handleIsomorphicDataLoad, {
|
|
457
440
|
generateRoutes,
|
|
458
441
|
loadData,
|
|
@@ -465,27 +448,27 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
465
448
|
redirectToLowercaseSlugs,
|
|
466
449
|
sMaxAge: _sMaxAge,
|
|
467
450
|
maxAge: _maxAge,
|
|
468
|
-
networkOnly: true
|
|
451
|
+
networkOnly: true,
|
|
469
452
|
})
|
|
470
|
-
)
|
|
453
|
+
);
|
|
471
454
|
|
|
472
|
-
app.post(
|
|
455
|
+
app.post("/register-fcm-topic", bodyParser.json(), withConfig(registerFCMTopic, { publisherConfig, fcmServerKey }));
|
|
473
456
|
|
|
474
457
|
if (webengageConfig.enableWebengage) {
|
|
475
458
|
app.post(
|
|
476
|
-
|
|
459
|
+
"/integrations/webengage/trigger-notification",
|
|
477
460
|
bodyParser.json(),
|
|
478
461
|
withConfig(triggerWebengageNotifications, webengageConfig)
|
|
479
|
-
)
|
|
462
|
+
);
|
|
480
463
|
}
|
|
481
464
|
|
|
482
465
|
if (manifestFn) {
|
|
483
|
-
app.get(
|
|
466
|
+
app.get("/manifest.json", withConfig(handleManifest, { manifestFn, logError }));
|
|
484
467
|
}
|
|
485
468
|
|
|
486
469
|
if (mobileApiEnabled) {
|
|
487
470
|
app.get(
|
|
488
|
-
|
|
471
|
+
"/mobile-data.json",
|
|
489
472
|
withConfig(handleIsomorphicDataLoad, {
|
|
490
473
|
generateRoutes,
|
|
491
474
|
loadData,
|
|
@@ -499,25 +482,25 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
499
482
|
cdnProvider,
|
|
500
483
|
redirectToLowercaseSlugs,
|
|
501
484
|
sMaxAge: _sMaxAge,
|
|
502
|
-
maxAge: _maxAge
|
|
485
|
+
maxAge: _maxAge,
|
|
503
486
|
})
|
|
504
|
-
)
|
|
487
|
+
);
|
|
505
488
|
}
|
|
506
489
|
|
|
507
490
|
if (assetLinkFn) {
|
|
508
|
-
app.get(
|
|
491
|
+
app.get("/.well-known/assetlinks.json", withConfig(handleAssetLink, { assetLinkFn, logError }));
|
|
509
492
|
}
|
|
510
493
|
|
|
511
494
|
if (templateOptions) {
|
|
512
495
|
app.get(
|
|
513
|
-
|
|
496
|
+
"/template-options.json",
|
|
514
497
|
withConfig(simpleJsonHandler, {
|
|
515
|
-
jsonData: toFunction(templateOptions,
|
|
498
|
+
jsonData: toFunction(templateOptions, "./impl/template-options"),
|
|
516
499
|
})
|
|
517
|
-
)
|
|
500
|
+
);
|
|
518
501
|
}
|
|
519
502
|
|
|
520
|
-
staticRoutes.forEach(route => {
|
|
503
|
+
staticRoutes.forEach((route) => {
|
|
521
504
|
app.get(
|
|
522
505
|
route.path,
|
|
523
506
|
withConfig(
|
|
@@ -533,16 +516,16 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
533
516
|
oneSignalServiceWorkers,
|
|
534
517
|
publisherConfig,
|
|
535
518
|
sMaxAge: _sMaxAge,
|
|
536
|
-
maxAge: _maxAge
|
|
519
|
+
maxAge: _maxAge,
|
|
537
520
|
},
|
|
538
521
|
route
|
|
539
522
|
)
|
|
540
523
|
)
|
|
541
|
-
)
|
|
542
|
-
})
|
|
524
|
+
);
|
|
525
|
+
});
|
|
543
526
|
|
|
544
527
|
app.get(
|
|
545
|
-
|
|
528
|
+
"/*",
|
|
546
529
|
withConfig(handleIsomorphicRoute, {
|
|
547
530
|
generateRoutes,
|
|
548
531
|
loadData,
|
|
@@ -566,17 +549,17 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
566
549
|
ampPageBasePath,
|
|
567
550
|
externalIdPattern,
|
|
568
551
|
enableExternalStories,
|
|
569
|
-
lazyLoadImageMargin
|
|
552
|
+
lazyLoadImageMargin,
|
|
570
553
|
})
|
|
571
|
-
)
|
|
554
|
+
);
|
|
572
555
|
|
|
573
556
|
if (redirectRootLevelStories) {
|
|
574
|
-
app.get(
|
|
557
|
+
app.get("/:storySlug", withConfig(redirectStory, { logError, cdnProvider, sMaxAge: _sMaxAge, maxAge: _maxAge }));
|
|
575
558
|
}
|
|
576
559
|
|
|
577
560
|
if (handleCustomRoute) {
|
|
578
561
|
app.get(
|
|
579
|
-
|
|
562
|
+
"/*",
|
|
580
563
|
withConfig(customRouteHandler, {
|
|
581
564
|
loadData,
|
|
582
565
|
renderLayout,
|
|
@@ -584,27 +567,27 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
584
567
|
seo,
|
|
585
568
|
cdnProvider,
|
|
586
569
|
sMaxAge: _sMaxAge,
|
|
587
|
-
maxAge: _maxAge
|
|
570
|
+
maxAge: _maxAge,
|
|
588
571
|
})
|
|
589
|
-
)
|
|
572
|
+
);
|
|
590
573
|
}
|
|
591
574
|
|
|
592
575
|
if (handleNotFound) {
|
|
593
576
|
app.get(
|
|
594
|
-
|
|
577
|
+
"/*",
|
|
595
578
|
withConfig(notFoundHandler, {
|
|
596
579
|
renderLayout,
|
|
597
580
|
pickComponent,
|
|
598
581
|
loadErrorData,
|
|
599
582
|
logError,
|
|
600
583
|
seo,
|
|
601
|
-
assetHelper
|
|
584
|
+
assetHelper,
|
|
602
585
|
})
|
|
603
|
-
)
|
|
586
|
+
);
|
|
604
587
|
}
|
|
605
|
-
}
|
|
588
|
+
};
|
|
606
589
|
|
|
607
|
-
exports.getWithConfig = getWithConfig
|
|
590
|
+
exports.getWithConfig = getWithConfig;
|
|
608
591
|
|
|
609
592
|
/**
|
|
610
593
|
* *proxyGetRequest* can be used to forward requests to another host, and cache the results on our CDN. This can be done as follows in `app/server/app.js`.
|
|
@@ -624,52 +607,52 @@ exports.getWithConfig = getWithConfig
|
|
|
624
607
|
* @param opts.cacheControl The cache control header to set on proxied requests (default: *"public,max-age=15,s-maxage=240,stale-while-revalidate=300,stale-if-error=3600"*)
|
|
625
608
|
*/
|
|
626
609
|
exports.proxyGetRequest = function (app, route, handler, opts = {}) {
|
|
627
|
-
const { logError = require(
|
|
628
|
-
const { cacheControl =
|
|
610
|
+
const { logError = require("./logger").error } = opts;
|
|
611
|
+
const { cacheControl = "public,max-age=15,s-maxage=240,stale-while-revalidate=300,stale-if-error=3600" } = opts;
|
|
629
612
|
|
|
630
|
-
getWithConfig(app, route, proxyHandler, opts)
|
|
613
|
+
getWithConfig(app, route, proxyHandler, opts);
|
|
631
614
|
|
|
632
|
-
async function proxyHandler
|
|
615
|
+
async function proxyHandler(req, res, next, { config, client }) {
|
|
633
616
|
try {
|
|
634
|
-
const result = await handler(req.params, { config, client })
|
|
635
|
-
if (typeof result ===
|
|
636
|
-
sendResult(await rp(result, { json: true }))
|
|
617
|
+
const result = await handler(req.params, { config, client });
|
|
618
|
+
if (typeof result === "string" && result.startsWith("http")) {
|
|
619
|
+
sendResult(await rp(result, { json: true }));
|
|
637
620
|
} else {
|
|
638
|
-
sendResult(result)
|
|
621
|
+
sendResult(result);
|
|
639
622
|
}
|
|
640
623
|
} catch (e) {
|
|
641
|
-
logError(e)
|
|
642
|
-
sendResult(null)
|
|
624
|
+
logError(e);
|
|
625
|
+
sendResult(null);
|
|
643
626
|
}
|
|
644
627
|
|
|
645
|
-
function sendResult
|
|
628
|
+
function sendResult(result) {
|
|
646
629
|
if (result) {
|
|
647
|
-
res.setHeader(
|
|
648
|
-
res.setHeader(
|
|
649
|
-
res.json(result)
|
|
630
|
+
res.setHeader("Cache-Control", cacheControl);
|
|
631
|
+
res.setHeader("Vary", "Accept-Encoding");
|
|
632
|
+
res.json(result);
|
|
650
633
|
} else {
|
|
651
|
-
res.status(503)
|
|
652
|
-
res.end()
|
|
634
|
+
res.status(503);
|
|
635
|
+
res.end();
|
|
653
636
|
}
|
|
654
637
|
}
|
|
655
638
|
}
|
|
656
|
-
}
|
|
639
|
+
};
|
|
657
640
|
|
|
658
641
|
// This could also be done using express's mount point, but /ping stops working
|
|
659
642
|
exports.mountQuintypeAt = function (app, mountAt) {
|
|
660
643
|
app.use(function (req, res, next) {
|
|
661
|
-
const mountPoint = typeof mountAt ===
|
|
644
|
+
const mountPoint = typeof mountAt === "function" ? mountAt(req.hostname) : mountAt;
|
|
662
645
|
|
|
663
646
|
if (mountPoint && req.url.startsWith(mountPoint)) {
|
|
664
|
-
req.url = req.url.slice(mountPoint.length) ||
|
|
665
|
-
next()
|
|
666
|
-
} else if (mountPoint && req.url !==
|
|
667
|
-
res.status(404).send(`Not Found: Quintype has been mounted at ${mountPoint}`)
|
|
647
|
+
req.url = req.url.slice(mountPoint.length) || "/";
|
|
648
|
+
next();
|
|
649
|
+
} else if (mountPoint && req.url !== "/ping") {
|
|
650
|
+
res.status(404).send(`Not Found: Quintype has been mounted at ${mountPoint}`);
|
|
668
651
|
} else {
|
|
669
|
-
next()
|
|
652
|
+
next();
|
|
670
653
|
}
|
|
671
|
-
})
|
|
672
|
-
}
|
|
654
|
+
});
|
|
655
|
+
};
|
|
673
656
|
|
|
674
657
|
/**
|
|
675
658
|
* *ampRoutes* handles all the amp page routes using the *[@quintype/amp](https://developers.quintype.com/quintype-node-amp)* library
|
|
@@ -692,9 +675,9 @@ exports.mountQuintypeAt = function (app, mountAt) {
|
|
|
692
675
|
*
|
|
693
676
|
*/
|
|
694
677
|
exports.ampRoutes = (app, opts = {}) => {
|
|
695
|
-
const { ampStoryPageHandler, storyPageInfiniteScrollHandler } = require(
|
|
678
|
+
const { ampStoryPageHandler, storyPageInfiniteScrollHandler } = require("./amp/handlers");
|
|
696
679
|
|
|
697
|
-
getWithConfig(app,
|
|
698
|
-
getWithConfig(app,
|
|
699
|
-
getWithConfig(app,
|
|
700
|
-
}
|
|
680
|
+
getWithConfig(app, "/amp/api/v1/amp-infinite-scroll", storyPageInfiniteScrollHandler, opts);
|
|
681
|
+
getWithConfig(app, "/ampstories/*", ampStoryPageHandler, { ...opts, isVisualStory: true });
|
|
682
|
+
getWithConfig(app, "/*", ampStoryPageHandler, opts);
|
|
683
|
+
};
|