@quintype/framework 7.33.6-jw-player.3 → 7.34.0-amp-chart-beat-changes.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/CHANGELOG.md +0 -15
- package/README.md +1 -1
- package/client/impl/fcm.js +3 -2
- package/package.json +7 -12
- package/server/amp/handlers/story-page.js +4 -1
- package/server/handlers/fcm-registration-handler.js +25 -21
- package/server/routes.js +270 -249
package/CHANGELOG.md
CHANGED
|
@@ -2,21 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
### [7.33.6](https://github.com/quintype/quintype-node-framework/compare/v7.33.5...v7.33.6) (2025-03-17)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
### Bug Fixes
|
|
9
|
-
|
|
10
|
-
* **FCM:** remove deprecated server key & use firebase admin to register to topic ([#458](https://github.com/quintype/quintype-node-framework/issues/458)) ([b179dd4](https://github.com/quintype/quintype-node-framework/commit/b179dd4594e94cf5639b020b1991a061c6c11105))
|
|
11
|
-
|
|
12
|
-
### [7.33.5](https://github.com/quintype/quintype-node-framework/compare/v7.33.4...v7.33.5) (2025-02-13)
|
|
13
|
-
|
|
14
|
-
### [7.33.4](https://github.com/quintype/quintype-node-framework/compare/v7.33.3...v7.33.4) (2025-02-13)
|
|
15
|
-
|
|
16
|
-
### [7.33.3](https://github.com/quintype/quintype-node-framework/compare/v7.33.2...v7.33.3) (2025-02-13)
|
|
17
|
-
|
|
18
|
-
### [7.33.2](https://github.com/quintype/quintype-node-framework/compare/v7.33.1...v7.33.2) (2025-02-13)
|
|
19
|
-
|
|
20
5
|
### [7.33.1](https://github.com/quintype/quintype-node-framework/compare/v7.33.0...v7.33.1) (2025-01-20)
|
|
21
6
|
|
|
22
7
|
|
package/README.md
CHANGED
|
@@ -171,7 +171,7 @@ startApp(renderApplication,
|
|
|
171
171
|
```js
|
|
172
172
|
isomorphicRoutes(app, {
|
|
173
173
|
...
|
|
174
|
-
|
|
174
|
+
fcmServerKey: (config) => <ServerKey> || fcmServerKey: <ServerKey> {(function|string)}
|
|
175
175
|
...
|
|
176
176
|
});
|
|
177
177
|
|
package/client/impl/fcm.js
CHANGED
|
@@ -13,13 +13,14 @@ export function initializeFCM(firebaseConfig) {
|
|
|
13
13
|
appId: firebaseConfig.appId,
|
|
14
14
|
});
|
|
15
15
|
const messaging = m.getMessaging(app);
|
|
16
|
-
return m.getToken(messaging
|
|
16
|
+
return m.getToken(messaging);
|
|
17
|
+
// No need to refresh token https://github.com/firebase/firebase-js-sdk/issues/4132
|
|
17
18
|
})
|
|
18
19
|
.then((token) => {
|
|
19
20
|
return registerFCMTopic(token);
|
|
20
21
|
})
|
|
21
22
|
.catch((err) => {
|
|
22
|
-
console.error(
|
|
23
|
+
console.error(err);
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quintype/framework",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.34.0-amp-chart-beat-changes.0",
|
|
4
4
|
"description": "Libraries to help build Quintype Node.js apps",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": "^
|
|
7
|
+
"node": "^16.0.0",
|
|
8
8
|
"npm": "^8.5.0"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
@@ -31,9 +31,7 @@
|
|
|
31
31
|
"homepage": "https://github.com/quintype/quintype-node-framework#readme",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@ampproject/toolbox-optimizer": "2.8.3",
|
|
34
|
-
"@
|
|
35
|
-
"@jsdoc/salty": "^0.2.9",
|
|
36
|
-
"@quintype/amp": "^2.20.1-jw-player.2",
|
|
34
|
+
"@quintype/amp": "^2.21.0-also-in-package-story-element.0",
|
|
37
35
|
"@quintype/backend": "^2.7.0",
|
|
38
36
|
"@quintype/components": "^3.5.0",
|
|
39
37
|
"@quintype/prerender-node": "^3.2.26",
|
|
@@ -45,9 +43,9 @@
|
|
|
45
43
|
"compression": "^1.7.4",
|
|
46
44
|
"ejs": "^3.1.6",
|
|
47
45
|
"express": "^4.17.1",
|
|
48
|
-
"firebase": "^
|
|
49
|
-
"firebase-admin": "^13.1.0",
|
|
46
|
+
"firebase": "^9.6.10",
|
|
50
47
|
"get-youtube-id": "^1.0.1",
|
|
48
|
+
"grpc": "^1.21.1",
|
|
51
49
|
"http-proxy": "^1.18.1",
|
|
52
50
|
"js-yaml": "^4.1.0",
|
|
53
51
|
"lodash": "^4.17.21",
|
|
@@ -67,7 +65,7 @@
|
|
|
67
65
|
"@babel/eslint-parser": "^7.15.7",
|
|
68
66
|
"@loadable/component": "^5.15.0",
|
|
69
67
|
"@loadable/server": "^5.15.1",
|
|
70
|
-
"@quintype/build": "^4.0.
|
|
68
|
+
"@quintype/build": "^4.0.0",
|
|
71
69
|
"babel-plugin-quintype-assets": "^1.1.1",
|
|
72
70
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
|
73
71
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
|
@@ -75,7 +73,7 @@
|
|
|
75
73
|
"babel-preset-es2015-tree-shaking": "^1.0.1",
|
|
76
74
|
"babel-preset-react": "^6.24.1",
|
|
77
75
|
"babel-register": "^6.26.0",
|
|
78
|
-
"better-docs": "^2.
|
|
76
|
+
"better-docs": "^2.3.2",
|
|
79
77
|
"eslint": "^7.32.0",
|
|
80
78
|
"eslint-config-prettier": "^8.3.0",
|
|
81
79
|
"eslint-config-standard": "^16.0.3",
|
|
@@ -122,8 +120,5 @@
|
|
|
122
120
|
"npx eslint --fix",
|
|
123
121
|
"git add"
|
|
124
122
|
]
|
|
125
|
-
},
|
|
126
|
-
"overrides": {
|
|
127
|
-
"grpc": "@grpc/grpc-js"
|
|
128
123
|
}
|
|
129
124
|
}
|
|
@@ -57,7 +57,10 @@ async function ampStoryPageHandler(
|
|
|
57
57
|
const story = await Story.getStoryBySlug(client, req.params["0"]);
|
|
58
58
|
const isAmpDisabled = get(story, ["metadata", "story-attributes", "disable-amp-for-single-story", "0"], "false");
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
const getDisableAmpUnit = get(opts, ["disableAmpUnit"], false);
|
|
61
|
+
const disableAmpUnit = typeof getDisableAmpUnit === "function" && opts.disableAmpUnit(story);
|
|
62
|
+
|
|
63
|
+
if (disableAmpUnit || (!isVisualStory && (!enableAmp || isAmpDisabled === "true"))) {
|
|
61
64
|
const ampPageBasePath = getAmpPageBasePath(opts, config);
|
|
62
65
|
const redirectUrl = `/${req.params[0]}`.startsWith(ampPageBasePath)
|
|
63
66
|
? `/${req.params[0]}`.replace(ampPageBasePath, "")
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
const { get } = require(
|
|
2
|
-
const
|
|
3
|
-
const logger = require('../logger')
|
|
1
|
+
const { get } = require("lodash");
|
|
2
|
+
const request = require("request-promise");
|
|
4
3
|
|
|
5
|
-
exports.registerFCMTopic = async function registerFCM
|
|
4
|
+
exports.registerFCMTopic = async function registerFCM(
|
|
6
5
|
req,
|
|
7
6
|
res,
|
|
8
7
|
next,
|
|
9
|
-
{ config, client, publisherConfig,
|
|
8
|
+
{ config, client, publisherConfig, fcmServerKey }
|
|
10
9
|
) {
|
|
11
|
-
|
|
12
|
-
const token = get(req, ['body', 'token'], null)
|
|
10
|
+
const token = get(req, ["body", "token"], null);
|
|
13
11
|
if (!token) {
|
|
14
|
-
res.status(400).send(
|
|
15
|
-
return
|
|
12
|
+
res.status(400).send("No Token Found");
|
|
13
|
+
return;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
const
|
|
16
|
+
const serverKey = typeof fcmServerKey === "function" ? await fcmServerKey(config) : fcmServerKey;
|
|
19
17
|
|
|
20
|
-
if (!
|
|
21
|
-
|
|
18
|
+
if (!serverKey) {
|
|
19
|
+
res.status(500).send("Server Key is not available");
|
|
20
|
+
return;
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
const url = `https://iid.googleapis.com/iid/v1/${token}/rel/topics/all`;
|
|
24
23
|
try {
|
|
25
|
-
await
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
await request({
|
|
25
|
+
uri: url,
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: `key=${serverKey}`,
|
|
29
|
+
"content-type": "application/json",
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
res.status(200).send("Registration Done Suceessfuly");
|
|
33
|
+
return;
|
|
28
34
|
} catch (error) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
logger.error(`Fcm register to topic error for publisher ${publisherId}: ${error}`);
|
|
32
|
-
return
|
|
35
|
+
res.status(500).send("FCM Subscription Failed");
|
|
36
|
+
return;
|
|
33
37
|
}
|
|
34
|
-
}
|
|
38
|
+
};
|
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
|
/**
|
|
@@ -304,12 +304,12 @@ function getWithConfig (app, route, handler, opts = {}) {
|
|
|
304
304
|
* @param {boolean|function} shouldEncodeAmpUri If set to true, then for every story-page request the slug will be encoded, in case of a vernacular slug this should be set to false. Receives path as param (default: true)
|
|
305
305
|
* @param {number} sMaxAge Overrides the s-maxage value, the default value is set to 900 seconds. We can set `isomorphicRoutesSmaxage: 900` under `publisher` in publisher.yml config file that comes from BlackKnight or pass sMaxAge as a param.
|
|
306
306
|
* @param {number} maxAge Overrides the max-age value, the default value is set to 15 seconds. We can set `isomorphicRoutesMaxage: 15` under `publisher` in publisher.yml config file that comes from BlackKnight or pass maxAge as a param.
|
|
307
|
-
* @param {(
|
|
307
|
+
* @param {(string|function)} fcmServerKey FCM serverKey is used for registering FCM Topic.
|
|
308
308
|
* @param {string} appLoadingPlaceholder This string gets injected into the app container when the page is loaded via service worker. Can be used to show skeleton layouts, animations or other progress indicators before it is replaced by the page content.
|
|
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,73 +334,73 @@ 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
|
-
|
|
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
388
|
app.use((req, res, next) => {
|
|
389
|
-
const origin = req.headers.origin
|
|
390
|
-
const allowedOriginRegex = /^https?:\/\/([a-zA-Z0-9-]+\.)*quintype\.com
|
|
389
|
+
const origin = req.headers.origin;
|
|
390
|
+
const allowedOriginRegex = /^https?:\/\/([a-zA-Z0-9-]+\.)*quintype\.com$/;
|
|
391
391
|
|
|
392
392
|
if (allowedOriginRegex.test(origin)) {
|
|
393
|
-
res.setHeader(
|
|
394
|
-
res.setHeader(
|
|
395
|
-
res.setHeader(
|
|
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
396
|
|
|
397
|
-
if (req.method ===
|
|
398
|
-
res.sendStatus(204)
|
|
399
|
-
return
|
|
397
|
+
if (req.method === "OPTIONS") {
|
|
398
|
+
res.sendStatus(204);
|
|
399
|
+
return;
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
|
-
next()
|
|
403
|
-
})
|
|
402
|
+
next();
|
|
403
|
+
});
|
|
404
404
|
|
|
405
405
|
if (serviceWorkerPaths.length > 0) {
|
|
406
406
|
app.get(
|
|
@@ -409,36 +409,36 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
409
409
|
generateRoutes,
|
|
410
410
|
assetHelper,
|
|
411
411
|
renderServiceWorker,
|
|
412
|
-
maxConfigVersion
|
|
412
|
+
maxConfigVersion,
|
|
413
413
|
})
|
|
414
|
-
)
|
|
414
|
+
);
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
if (oneSignalServiceWorkers) {
|
|
418
418
|
app.get(
|
|
419
|
-
|
|
419
|
+
"/OneSignalSDKWorker.js",
|
|
420
420
|
withConfig(generateServiceWorker, {
|
|
421
421
|
generateRoutes,
|
|
422
422
|
renderServiceWorker,
|
|
423
423
|
assetHelper,
|
|
424
424
|
appendFn: oneSignalImport,
|
|
425
|
-
maxConfigVersion
|
|
425
|
+
maxConfigVersion,
|
|
426
426
|
})
|
|
427
|
-
)
|
|
427
|
+
);
|
|
428
428
|
app.get(
|
|
429
|
-
|
|
429
|
+
"/OneSignalSDKUpdaterWorker.js",
|
|
430
430
|
withConfig(generateServiceWorker, {
|
|
431
431
|
generateRoutes,
|
|
432
432
|
renderServiceWorker,
|
|
433
433
|
assetHelper,
|
|
434
434
|
appendFn: oneSignalImport,
|
|
435
|
-
maxConfigVersion
|
|
435
|
+
maxConfigVersion,
|
|
436
436
|
})
|
|
437
|
-
)
|
|
437
|
+
);
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
app.get(
|
|
441
|
-
|
|
441
|
+
"/shell.html",
|
|
442
442
|
withConfig(handleIsomorphicShell, {
|
|
443
443
|
seo,
|
|
444
444
|
renderLayout,
|
|
@@ -448,11 +448,11 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
448
448
|
logError,
|
|
449
449
|
preloadJs,
|
|
450
450
|
maxConfigVersion,
|
|
451
|
-
appLoadingPlaceholder
|
|
451
|
+
appLoadingPlaceholder,
|
|
452
452
|
})
|
|
453
|
-
)
|
|
453
|
+
);
|
|
454
454
|
app.get(
|
|
455
|
-
|
|
455
|
+
"/route-data.json",
|
|
456
456
|
withConfig(handleIsomorphicDataLoad, {
|
|
457
457
|
generateRoutes,
|
|
458
458
|
loadData,
|
|
@@ -465,27 +465,27 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
465
465
|
redirectToLowercaseSlugs,
|
|
466
466
|
sMaxAge: _sMaxAge,
|
|
467
467
|
maxAge: _maxAge,
|
|
468
|
-
networkOnly: true
|
|
468
|
+
networkOnly: true,
|
|
469
469
|
})
|
|
470
|
-
)
|
|
470
|
+
);
|
|
471
471
|
|
|
472
|
-
app.post(
|
|
472
|
+
app.post("/register-fcm-topic", bodyParser.json(), withConfig(registerFCMTopic, { publisherConfig, fcmServerKey }));
|
|
473
473
|
|
|
474
474
|
if (webengageConfig.enableWebengage) {
|
|
475
475
|
app.post(
|
|
476
|
-
|
|
476
|
+
"/integrations/webengage/trigger-notification",
|
|
477
477
|
bodyParser.json(),
|
|
478
478
|
withConfig(triggerWebengageNotifications, webengageConfig)
|
|
479
|
-
)
|
|
479
|
+
);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
if (manifestFn) {
|
|
483
|
-
app.get(
|
|
483
|
+
app.get("/manifest.json", withConfig(handleManifest, { manifestFn, logError }));
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
if (mobileApiEnabled) {
|
|
487
487
|
app.get(
|
|
488
|
-
|
|
488
|
+
"/mobile-data.json",
|
|
489
489
|
withConfig(handleIsomorphicDataLoad, {
|
|
490
490
|
generateRoutes,
|
|
491
491
|
loadData,
|
|
@@ -499,25 +499,25 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
499
499
|
cdnProvider,
|
|
500
500
|
redirectToLowercaseSlugs,
|
|
501
501
|
sMaxAge: _sMaxAge,
|
|
502
|
-
maxAge: _maxAge
|
|
502
|
+
maxAge: _maxAge,
|
|
503
503
|
})
|
|
504
|
-
)
|
|
504
|
+
);
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
if (assetLinkFn) {
|
|
508
|
-
app.get(
|
|
508
|
+
app.get("/.well-known/assetlinks.json", withConfig(handleAssetLink, { assetLinkFn, logError }));
|
|
509
509
|
}
|
|
510
510
|
|
|
511
511
|
if (templateOptions) {
|
|
512
512
|
app.get(
|
|
513
|
-
|
|
513
|
+
"/template-options.json",
|
|
514
514
|
withConfig(simpleJsonHandler, {
|
|
515
|
-
jsonData: toFunction(templateOptions,
|
|
515
|
+
jsonData: toFunction(templateOptions, "./impl/template-options"),
|
|
516
516
|
})
|
|
517
|
-
)
|
|
517
|
+
);
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
staticRoutes.forEach(route => {
|
|
520
|
+
staticRoutes.forEach((route) => {
|
|
521
521
|
app.get(
|
|
522
522
|
route.path,
|
|
523
523
|
withConfig(
|
|
@@ -533,16 +533,16 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
533
533
|
oneSignalServiceWorkers,
|
|
534
534
|
publisherConfig,
|
|
535
535
|
sMaxAge: _sMaxAge,
|
|
536
|
-
maxAge: _maxAge
|
|
536
|
+
maxAge: _maxAge,
|
|
537
537
|
},
|
|
538
538
|
route
|
|
539
539
|
)
|
|
540
540
|
)
|
|
541
|
-
)
|
|
542
|
-
})
|
|
541
|
+
);
|
|
542
|
+
});
|
|
543
543
|
|
|
544
544
|
app.get(
|
|
545
|
-
|
|
545
|
+
"/*",
|
|
546
546
|
withConfig(handleIsomorphicRoute, {
|
|
547
547
|
generateRoutes,
|
|
548
548
|
loadData,
|
|
@@ -566,17 +566,17 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
566
566
|
ampPageBasePath,
|
|
567
567
|
externalIdPattern,
|
|
568
568
|
enableExternalStories,
|
|
569
|
-
lazyLoadImageMargin
|
|
569
|
+
lazyLoadImageMargin,
|
|
570
570
|
})
|
|
571
|
-
)
|
|
571
|
+
);
|
|
572
572
|
|
|
573
573
|
if (redirectRootLevelStories) {
|
|
574
|
-
app.get(
|
|
574
|
+
app.get("/:storySlug", withConfig(redirectStory, { logError, cdnProvider, sMaxAge: _sMaxAge, maxAge: _maxAge }));
|
|
575
575
|
}
|
|
576
576
|
|
|
577
577
|
if (handleCustomRoute) {
|
|
578
578
|
app.get(
|
|
579
|
-
|
|
579
|
+
"/*",
|
|
580
580
|
withConfig(customRouteHandler, {
|
|
581
581
|
loadData,
|
|
582
582
|
renderLayout,
|
|
@@ -584,27 +584,27 @@ exports.isomorphicRoutes = function isomorphicRoutes (
|
|
|
584
584
|
seo,
|
|
585
585
|
cdnProvider,
|
|
586
586
|
sMaxAge: _sMaxAge,
|
|
587
|
-
maxAge: _maxAge
|
|
587
|
+
maxAge: _maxAge,
|
|
588
588
|
})
|
|
589
|
-
)
|
|
589
|
+
);
|
|
590
590
|
}
|
|
591
591
|
|
|
592
592
|
if (handleNotFound) {
|
|
593
593
|
app.get(
|
|
594
|
-
|
|
594
|
+
"/*",
|
|
595
595
|
withConfig(notFoundHandler, {
|
|
596
596
|
renderLayout,
|
|
597
597
|
pickComponent,
|
|
598
598
|
loadErrorData,
|
|
599
599
|
logError,
|
|
600
600
|
seo,
|
|
601
|
-
assetHelper
|
|
601
|
+
assetHelper,
|
|
602
602
|
})
|
|
603
|
-
)
|
|
603
|
+
);
|
|
604
604
|
}
|
|
605
|
-
}
|
|
605
|
+
};
|
|
606
606
|
|
|
607
|
-
exports.getWithConfig = getWithConfig
|
|
607
|
+
exports.getWithConfig = getWithConfig;
|
|
608
608
|
|
|
609
609
|
/**
|
|
610
610
|
* *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 +624,52 @@ exports.getWithConfig = getWithConfig
|
|
|
624
624
|
* @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
625
|
*/
|
|
626
626
|
exports.proxyGetRequest = function (app, route, handler, opts = {}) {
|
|
627
|
-
const { logError = require(
|
|
628
|
-
const { cacheControl =
|
|
627
|
+
const { logError = require("./logger").error } = opts;
|
|
628
|
+
const { cacheControl = "public,max-age=15,s-maxage=240,stale-while-revalidate=300,stale-if-error=3600" } = opts;
|
|
629
629
|
|
|
630
|
-
getWithConfig(app, route, proxyHandler, opts)
|
|
630
|
+
getWithConfig(app, route, proxyHandler, opts);
|
|
631
631
|
|
|
632
|
-
async function proxyHandler
|
|
632
|
+
async function proxyHandler(req, res, next, { config, client }) {
|
|
633
633
|
try {
|
|
634
|
-
const result = await handler(req.params, { config, client })
|
|
635
|
-
if (typeof result ===
|
|
636
|
-
sendResult(await rp(result, { json: true }))
|
|
634
|
+
const result = await handler(req.params, { config, client });
|
|
635
|
+
if (typeof result === "string" && result.startsWith("http")) {
|
|
636
|
+
sendResult(await rp(result, { json: true }));
|
|
637
637
|
} else {
|
|
638
|
-
sendResult(result)
|
|
638
|
+
sendResult(result);
|
|
639
639
|
}
|
|
640
640
|
} catch (e) {
|
|
641
|
-
logError(e)
|
|
642
|
-
sendResult(null)
|
|
641
|
+
logError(e);
|
|
642
|
+
sendResult(null);
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
-
function sendResult
|
|
645
|
+
function sendResult(result) {
|
|
646
646
|
if (result) {
|
|
647
|
-
res.setHeader(
|
|
648
|
-
res.setHeader(
|
|
649
|
-
res.json(result)
|
|
647
|
+
res.setHeader("Cache-Control", cacheControl);
|
|
648
|
+
res.setHeader("Vary", "Accept-Encoding");
|
|
649
|
+
res.json(result);
|
|
650
650
|
} else {
|
|
651
|
-
res.status(503)
|
|
652
|
-
res.end()
|
|
651
|
+
res.status(503);
|
|
652
|
+
res.end();
|
|
653
653
|
}
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
|
-
}
|
|
656
|
+
};
|
|
657
657
|
|
|
658
658
|
// This could also be done using express's mount point, but /ping stops working
|
|
659
659
|
exports.mountQuintypeAt = function (app, mountAt) {
|
|
660
660
|
app.use(function (req, res, next) {
|
|
661
|
-
const mountPoint = typeof mountAt ===
|
|
661
|
+
const mountPoint = typeof mountAt === "function" ? mountAt(req.hostname) : mountAt;
|
|
662
662
|
|
|
663
663
|
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}`)
|
|
664
|
+
req.url = req.url.slice(mountPoint.length) || "/";
|
|
665
|
+
next();
|
|
666
|
+
} else if (mountPoint && req.url !== "/ping") {
|
|
667
|
+
res.status(404).send(`Not Found: Quintype has been mounted at ${mountPoint}`);
|
|
668
668
|
} else {
|
|
669
|
-
next()
|
|
669
|
+
next();
|
|
670
670
|
}
|
|
671
|
-
})
|
|
672
|
-
}
|
|
671
|
+
});
|
|
672
|
+
};
|
|
673
673
|
|
|
674
674
|
/**
|
|
675
675
|
* *ampRoutes* handles all the amp page routes using the *[@quintype/amp](https://developers.quintype.com/quintype-node-amp)* library
|
|
@@ -677,9 +677,30 @@ exports.mountQuintypeAt = function (app, mountAt) {
|
|
|
677
677
|
* GET - "/amp/:slug"* returns amp story page
|
|
678
678
|
* GET - "/amp/api/v1/amp-infinite-scroll" returns the infinite scroll config JSON. Passed to <amp-next-page> component's `src` attribute
|
|
679
679
|
*
|
|
680
|
-
* To disable amp version for a specific story, you need to create a story attribute in bold with the slug {disable-amp-for-single-story} and values {true} and {false}. Set its value to "true" in the story which you want to disable amp. Please make sure to name the attributes and values in the exact same way as mentioned
|
|
680
|
+
* To disable amp version for a "specific story", you need to create a story attribute in bold with the slug {disable-amp-for-single-story} and values {true} and {false}. Set its value to "true" in the story which you want to disable amp. Please make sure to name the attributes and values in the exact same way as mentioned
|
|
681
681
|
* attribute slug: "disable-amp-for-single-story" values: "true" , "false". This will redirect '<amp-page-base-path>/:slug' to the non-amp page
|
|
682
682
|
*
|
|
683
|
+
* To disable the AMP version for a specific story template or section, pass opts.disableAmpUnit as a function that always returns a boolean value. When the function returns true, AMP will be disabled for the specified scenario.
|
|
684
|
+
* Note: Ensure that disableAmpUnit is always a function and returns a boolean value, as demonstrated below.
|
|
685
|
+
*
|
|
686
|
+
* Under app/server/app.js
|
|
687
|
+
*
|
|
688
|
+
* ``` const getTemplate = (story) => {
|
|
689
|
+
* return story.["story-template"] === "template-name";
|
|
690
|
+
* };
|
|
691
|
+
*
|
|
692
|
+
* ....
|
|
693
|
+
*
|
|
694
|
+
* ampRoutes(app, {
|
|
695
|
+
* seo: generateSeo,
|
|
696
|
+
* disableAmpUnit: (story) => getTemplate(story),
|
|
697
|
+
* featureConfig: {
|
|
698
|
+
* .....
|
|
699
|
+
* }
|
|
700
|
+
* ...
|
|
701
|
+
* .....
|
|
702
|
+
* })```
|
|
703
|
+
*
|
|
683
704
|
* @param {Express} app Express app to add the routes to
|
|
684
705
|
* @param {Object} opts Options object used to configure amp. Passing this is optional
|
|
685
706
|
* @param {Object} opts.templates An object that's used to pass custom templates. Each key corresponds to the template name and corresponding value is the template
|
|
@@ -692,9 +713,9 @@ exports.mountQuintypeAt = function (app, mountAt) {
|
|
|
692
713
|
*
|
|
693
714
|
*/
|
|
694
715
|
exports.ampRoutes = (app, opts = {}) => {
|
|
695
|
-
const { ampStoryPageHandler, storyPageInfiniteScrollHandler } = require(
|
|
716
|
+
const { ampStoryPageHandler, storyPageInfiniteScrollHandler } = require("./amp/handlers");
|
|
696
717
|
|
|
697
|
-
getWithConfig(app,
|
|
698
|
-
getWithConfig(app,
|
|
699
|
-
getWithConfig(app,
|
|
700
|
-
}
|
|
718
|
+
getWithConfig(app, "/amp/api/v1/amp-infinite-scroll", storyPageInfiniteScrollHandler, opts);
|
|
719
|
+
getWithConfig(app, "/ampstories/*", ampStoryPageHandler, { ...opts, isVisualStory: true });
|
|
720
|
+
getWithConfig(app, "/*", ampStoryPageHandler, opts);
|
|
721
|
+
};
|