Skip to content

Commit ea41cc6

Browse files
refactor(@embark/api): in dev use cockpit redirect instead of proxy
Embark API server's development proxy from port 55555 to 3000 was attempting to inappropriately forward an `/embark-api/` endpoint for the blockchain process logs to Create React App's development server. Why it was only happening for the one endpoint is not known but probably has to do with timing around registration of the API server's express routes. The problem can be fixed with a one-line `filter:` function in the options for `express-http-proxy`. However, it was realized that to fix an unrelated problem, whereby the proxy doesn't forward websockets to CRA such that hot reload doesn't work when accessing `embark-ui` in development on port 55555, a switch to `http-proxy-middleware` would be required. That was quickly attempted (easy switch) but there are outstanding [difficulties][bug] with `webpack-dev-server` and `node-http-proxy` that cause CRA to crash. Switch strategies and refactor the API module to serve a page on port 55555 (in development only) that alerts the developer `embark-ui` should be accessed on port 3000. The page redirects (client-side) after 10 seconds, with URL query params and/or hash preserved. A future version could instead do client-side polling of port 3000 with `fetch` and then redirect only once it's available. The reason for not redirecting immediately is that the intermediate page makes it more obvious what needs to be done, e.g. CRA dev server may need to be started with `yarn start`. [bug]: webpack/webpack-dev-server#1642
1 parent 3988fb4 commit ea41cc6

File tree

3 files changed

+74
-70
lines changed

3 files changed

+74
-70
lines changed

packages/embark/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@
191191
"@types/body-parser": "1.17.0",
192192
"@types/cors": "2.8.4",
193193
"@types/express": "4.16.0",
194-
"@types/express-http-proxy": "1.5.1",
195194
"@types/express-ws": "3.0.0",
196195
"@types/find-up": "2.1.0",
197196
"@types/globule": "1.1.3",

packages/embark/src/lib/modules/api/server.ts

+74-61
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import bodyParser from "body-parser";
22
import cors from "cors";
33
import {Embark, Plugins} from "embark";
44
import express, {NextFunction, Request, Response} from "express";
5-
import proxy from "express-http-proxy";
65
import expressWs from "express-ws";
76
import findUp from "find-up";
87
import helmet from "helmet";
@@ -87,15 +86,12 @@ export default class Server {
8786
});
8887
}
8988

90-
private makePage(reloadSeconds: number, body: string) {
89+
private makePage(body: string) {
9190
return (`
9291
<!doctype html>
9392
<html lang="en">
9493
<head>
9594
<meta charset="utf-8" />
96-
${this.isInsideMonorepo ? `
97-
<meta http-equiv="refresh" content="${reloadSeconds}">
98-
` : ""}
9995
<title>Embark API Server</title>
10096
<style type="text/css">
10197
code {
@@ -112,47 +108,78 @@ export default class Server {
112108
</head>
113109
<body>
114110
${body}
115-
${this.isInsideMonorepo ? `
116-
<p>this page will automatically reload
117-
in <span id="timer">${reloadSeconds}</span> seconds</p>
118-
<script>
119-
let timeLeft = ${reloadSeconds};
120-
const span = document.querySelector("#timer");
121-
setInterval(() => {
122-
if (timeLeft >= 1) { timeLeft -= 1; }
123-
span.innerText = \`\${timeLeft}\`;
124-
}, 1000);
125-
</script>
126-
` : ""}
127111
</body>
128112
</html>
129113
`.trim().split("\n").map((str) => str.trim()).filter((str) => str).join("\n"));
130114
}
131115

132116
private makePage404(reloadSeconds: number, envReport: string, inside: string, notice: string) {
133-
return this.makePage(reloadSeconds, `
117+
return this.makePage(`
134118
${envReport}
135119
<p>missing build for package <code>embark-ui</code> ${inside}</p>
136120
${notice}
121+
${this.isInsideMonorepo ? `
122+
<p>this page will automatically reload
123+
in <span id="timer">${reloadSeconds}</span> seconds</p>
124+
<script>
125+
let timeLeft = ${reloadSeconds};
126+
const span = document.querySelector("#timer");
127+
const timer = window.setInterval(() => {
128+
if (timeLeft >= 1) {
129+
timeLeft -= 1;
130+
span.innerText = \`\${timeLeft}\`;
131+
}
132+
if (!timeLeft) {
133+
window.clearInterval(timer);
134+
window.location.reload(true);
135+
}
136+
}, 1000);
137+
</script>
138+
` : ""}
137139
`);
138140
}
139141

140-
private makePageEConnError(reloadSeconds: number, waitingFor: string) {
141-
return this.makePage(reloadSeconds, `
142-
<p><code>lib/modules/api/server</code> inside the monorepo at
143-
<code>${path.join(this.monorepoRootDir, "packages/embark")}</code> is
144-
waiting for the Create React App development server of package
145-
<code>embark-ui</code> to ${waitingFor} at
146-
<code>localhost:55555</code></p>
147-
${waitingFor === "become available" ? `
148-
<p>please run either:</p>
149-
<p><code>cd ${this.monorepoRootDir} && yarn start</code><br />
150-
or<br />
151-
<code>cd ${path.join(this.monorepoRootDir, "packages/embark-ui")}
152-
&& yarn start</code></p>
142+
private makePage503(redirectSeconds: number) {
143+
return this.makePage(`
144+
<p><code>lib/modules/api/server</code> is inside the monorepo at
145+
<code>${path.join(this.monorepoRootDir, "packages/embark")}</code></p>
146+
<p>to access <code>embark-ui</code> in development use port
147+
<code>3000</code></p>
148+
<p>if you haven't already, please run either:</p>
149+
<p><code>cd ${this.monorepoRootDir} && yarn start</code><br />
150+
or<br />
151+
<code>cd ${path.join(this.monorepoRootDir, "packages/embark-ui")} &&
152+
yarn start</code></p>
153153
<p>to instead use a static build from the monorepo, restart embark with:
154154
<code>EMBARK_UI_STATIC=t embark run</code></p>
155-
` : ""}
155+
<p>this page will automatically redirect to <a id="redirect" href=""></a>
156+
in <span id="timer">${redirectSeconds}</span> seconds</p>
157+
<script>
158+
window.embarkApiRedirect = window.location.href.replace(
159+
\`http://\${window.location.hostname}:55555\`,
160+
\`http://\${window.location.hostname}:3000\`
161+
);
162+
document.querySelector("#redirect").href = window.embarkApiRedirect;
163+
let displayLink = window.embarkApiRedirect.slice(7);
164+
if (displayLink.endsWith(\`\${window.location.hostname}:3000/\`)) {
165+
displayLink = displayLink.slice(0, -1);
166+
}
167+
document.querySelector("#redirect").innerText = displayLink;
168+
</script>
169+
<script>
170+
let timeLeft = ${redirectSeconds};
171+
const span = document.querySelector("#timer");
172+
const timer = window.setInterval(() => {
173+
if (timeLeft >= 1) {
174+
timeLeft -= 1;
175+
span.innerText = \`\${timeLeft}\`;
176+
}
177+
if (!timeLeft) {
178+
window.clearInterval(timer);
179+
window.location.href = window.embarkApiRedirect;
180+
}
181+
}, 1000);
182+
</script>
156183
`);
157184
}
158185

@@ -195,7 +222,7 @@ export default class Server {
195222
if (!this.isInsideMonorepo || process.env.EMBARK_UI_STATIC) {
196223
if (existsSync(path.join(this.embarkUiBuildDir, "index.html"))) {
197224
instance.app.use("/", express.static(this.embarkUiBuildDir));
198-
instance.app.get("/*", (_req, res) => {
225+
instance.app.get(/^\/(?!embark-api).*$/, (_req, res) => {
199226
res.sendFile(path.join(this.embarkUiBuildDir, "index.html"));
200227
});
201228
} else {
@@ -204,7 +231,9 @@ export default class Server {
204231
in <code>${path.dirname(this.embarkUiBuildDir)}</code>
205232
`;
206233
let notice = `
207-
<p>this distribution of <code>embark-ui</code> appears to be broken</p>
234+
<p>this distribution of <code>embark-ui</code> appears to be broken,
235+
please <a href="https://github.com/embark-framework/embark/issues">
236+
file an issue</a></p>
208237
`;
209238
if (this.isInsideMonorepo) {
210239
envReport = `
@@ -223,40 +252,24 @@ export default class Server {
223252
&& yarn build</code></p>
224253
<p>restart <code>embark run</code> after building
225254
<code>embark-ui</code></p>
226-
<p>to instead use a live development build from the monorepo, unset
227-
the environment variable <code>EMBARK_UI_STATIC</code> and restart
228-
embark</p>
255+
<p>to instead use a live development build from the monorepo: unset
256+
the environment variable <code>EMBARK_UI_STATIC</code>, restart
257+
embark, and visit
258+
<a href="http://localhost:3000">http://localhost:3000</a></p>
229259
`;
230260
}
231-
const page404 = this.makePage404(3, envReport, inside, notice);
261+
const page404 = this.makePage404(10, envReport, inside, notice);
232262
const missingBuildHandler = (_req: Request, res: Response) => {
233263
res.status(404).send(page404);
234264
};
235-
instance.app.get("/", missingBuildHandler);
236-
instance.app.get("/*", missingBuildHandler);
265+
instance.app.get(/^\/(?!embark-api).*$/, missingBuildHandler);
237266
}
238267
} else {
239-
const page503 = this.makePageEConnError(3, "become available");
240-
const page504 = this.makePageEConnError(3, "become responsive");
241-
instance.app.use("/", proxy("http://localhost:3000", {
242-
// @ts-ignore
243-
proxyErrorHandler: (err, res, next) => {
244-
switch (err && err.code) {
245-
case "ECONNREFUSED": {
246-
return res.status(503).send(page503);
247-
}
248-
case "ECONNRESET": {
249-
if (err.message === "socket hang up") {
250-
return res.status(504).send(page504);
251-
}
252-
}
253-
default: {
254-
next(err);
255-
}
256-
}
257-
},
258-
timeout: 1000,
259-
}));
268+
const page503 = this.makePage503(10);
269+
const unavailableBuildHandler = (_req: Request, res: Response) => {
270+
res.status(503).send(page503);
271+
};
272+
instance.app.get(/^\/(?!embark-api).*$/, unavailableBuildHandler);
260273
}
261274

262275
return instance;

yarn.lock

-8
Original file line numberDiff line numberDiff line change
@@ -2723,14 +2723,6 @@
27232723
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
27242724
integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==
27252725

2726-
"@types/express-http-proxy@1.5.1":
2727-
version "1.5.1"
2728-
resolved "https://registry.yarnpkg.com/@types/express-http-proxy/-/express-http-proxy-1.5.1.tgz#0184017b1cfc8ab2a4954d35f90c9b4cc3d7ffcc"
2729-
integrity sha512-9SOGqwVzbudT5nzF4TjKOu0cWE0HRaTVVivwxUxYMN/7mas6Wt/W5pz53dZIs7Y0fZBjAI3RTDDr+dXtXrv+hA==
2730-
dependencies:
2731-
"@types/express" "*"
2732-
"@types/express-serve-static-core" "*"
2733-
27342726
"@types/express-serve-static-core@*":
27352727
version "4.16.0"
27362728
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz#fdfe777594ddc1fe8eb8eccce52e261b496e43e7"

0 commit comments

Comments
 (0)