FIX: Ember CLI was losing some preloaded data (#13406)

The `bootstrap.json` contains most preloaded information but some routes
provide extra information, such as invites.

This fixes the issue by having the preload request pass on the preloaded
data from the source page, which is then merged with the bootstrap's
preloaded data for the final HTML payload.
This commit is contained in:
Robin Ward
2021-06-16 13:45:02 -04:00
committed by GitHub
parent 20dbcbf022
commit 651b8a23b8
2 changed files with 21 additions and 11 deletions

View File

@ -132,10 +132,10 @@ function preloaded(buffer, bootstrap) {
const BUILDERS = { const BUILDERS = {
"html-tag": htmlTag, "html-tag": htmlTag,
"before-script-load": beforeScriptLoad, "before-script-load": beforeScriptLoad,
head: head, head,
body: body, body,
"hidden-login-form": hiddenLoginForm, "hidden-login-form": hiddenLoginForm,
preloaded: preloaded, preloaded,
"body-footer": bodyFooter, "body-footer": bodyFooter,
"locale-script": localeScript, "locale-script": localeScript,
}; };
@ -148,14 +148,20 @@ function replaceIn(bootstrap, template, id, headers) {
return template.replace(`<bootstrap-content key="${id}">`, contents); return template.replace(`<bootstrap-content key="${id}">`, contents);
} }
function applyBootstrap(bootstrap, template, headers) { async function applyBootstrap(bootstrap, template, response) {
// If our initial page added some preload data let's not lose that.
let json = await response.json();
if (json && json.preloaded) {
bootstrap.preloaded = Object.assign(json.preloaded, bootstrap.preloaded);
}
Object.keys(BUILDERS).forEach((id) => { Object.keys(BUILDERS).forEach((id) => {
template = replaceIn(bootstrap, template, id, headers); template = replaceIn(bootstrap, template, id, response);
}); });
return template; return template;
} }
function buildFromBootstrap(assetPath, proxy, baseURL, req, headers) { function buildFromBootstrap(assetPath, proxy, baseURL, req, response) {
// eslint-disable-next-line // eslint-disable-next-line
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.readFile( fs.readFile(
@ -170,8 +176,9 @@ function buildFromBootstrap(assetPath, proxy, baseURL, req, headers) {
getJSON(url, null, req.headers) getJSON(url, null, req.headers)
.then((json) => { .then((json) => {
resolve(applyBootstrap(json.bootstrap, template, headers)); return applyBootstrap(json.bootstrap, template, response);
}) })
.then(resolve)
.catch((e) => { .catch((e) => {
reject( reject(
`Could not get ${proxy}${baseURL}bootstrap.json\n\n${e.toString()}` `Could not get ${proxy}${baseURL}bootstrap.json\n\n${e.toString()}`
@ -203,16 +210,17 @@ async function handleRequest(assetPath, proxy, baseURL, req, res) {
let get = bent("GET", [200, 301, 302, 303, 307, 308, 404, 403, 500]); let get = bent("GET", [200, 301, 302, 303, 307, 308, 404, 403, 500]);
let response = await get(url, null, req.headers); let response = await get(url, null, req.headers);
res.set(response.headers); res.set(response.headers);
res.set("content-type", "text/html");
if (response.headers["x-discourse-bootstrap-required"] === "true") { if (response.headers["x-discourse-bootstrap-required"] === "true") {
req.headers["X-Discourse-Asset-Path"] = req.path; req.headers["X-Discourse-Asset-Path"] = req.path;
let json = await buildFromBootstrap( let html = await buildFromBootstrap(
assetPath, assetPath,
proxy, proxy,
baseURL, baseURL,
req, req,
response.headers response
); );
return res.send(json); return res.send(html);
} }
res.status(response.status); res.status(response.status);
res.send(await response.text()); res.send(await response.text());

View File

@ -322,7 +322,9 @@ class ApplicationController < ActionController::Base
end end
def send_ember_cli_bootstrap def send_ember_cli_bootstrap
head 200, content_type: "text/html", "X-Discourse-Bootstrap-Required": true response.headers['X-Discourse-Bootstrap-Required'] = true
response.headers['Content-Type'] = "application/json"
render json: { preloaded: @preloaded }
end end
# If a controller requires a plugin, it will raise an exception if that plugin is # If a controller requires a plugin, it will raise an exception if that plugin is