Proxy Webpack dev server through to an API server (#245)
Summary: This way, our frontend can talk to a backend that can read from the filesystem (among other things). Paired with @decentralion. Test Plan: ``` $ yarn backend $ SOURCECRED_DIRECTORY=/tmp/srccrd yarn start $ # verify that the browser looks good $ mkdir /tmp/srccrd $ echo hello >/tmp/srccrd/world $ curl localhost:3000/api/v1/data/world hello $ curl localhost:4000/api/v1/data/world hello ``` wchargin-branch: webpack-proxy
This commit is contained in:
parent
62b9f70d00
commit
9ea1f981aa
|
@ -57,6 +57,7 @@ module.exports = {
|
|||
"commands/graph": resolveApp("src/cli/commands/graph.js"),
|
||||
"commands/plugin-graph": resolveApp("src/cli/commands/pluginGraph.js"),
|
||||
"commands/start": resolveApp("src/cli/commands/start.js"),
|
||||
apiApp: resolveApp("src/app/apiApp.js"),
|
||||
sourcecred: resolveApp("src/cli/sourcecred.js"),
|
||||
fetchAndPrintGithubRepo: resolveApp(
|
||||
"src/plugins/github/bin/fetchAndPrintGithubRepo.js"
|
||||
|
|
117
scripts/start.js
117
scripts/start.js
|
@ -15,6 +15,8 @@ process.on("unhandledRejection", (err) => {
|
|||
// Ensure environment variables are read.
|
||||
require("../config/env");
|
||||
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const chalk = require("chalk");
|
||||
const webpack = require("webpack");
|
||||
|
@ -36,13 +38,20 @@ const useYarn = fs.existsSync(paths.yarnLockFile);
|
|||
const isInteractive =
|
||||
typeof process.stdout.isTTY !== "undefined" && process.stdout.isTTY;
|
||||
|
||||
// The following module is generated by running `yarn backend`. We want
|
||||
// to be able to run Flow without having generated the module, and its
|
||||
// types aren't very rich, anyway, so we stub out the type of `require`
|
||||
// itself.
|
||||
const apiApp = /*:: ((require: any) => */ require("../bin/apiApp")
|
||||
.default /*:: )() */;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
|
||||
const DEFAULT_WEBPACK_PORT = parseInt(process.env.PORT, 10) || 3000;
|
||||
const DEFAULT_API_PORT = parseInt(process.env.PORT + 1000, 10) || 4000;
|
||||
const HOST = process.env.HOST || "0.0.0.0";
|
||||
|
||||
if (process.env.HOST) {
|
||||
|
@ -62,48 +71,68 @@ if (process.env.HOST) {
|
|||
|
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||
choosePort(HOST, DEFAULT_PORT)
|
||||
.then((port) => {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
const protocol = process.env.HTTPS === "true" ? "https" : "http";
|
||||
const appName = "sourcecred";
|
||||
const urls = prepareUrls(protocol, HOST, port);
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
|
||||
// Load proxy config
|
||||
const proxySetting = undefined;
|
||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
||||
// Serve webpack assets generated by the compiler over a web sever.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, (err) => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
console.log(chalk.cyan("Starting the development server...\n"));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
async function main() {
|
||||
const webpackPort = await choosePort(HOST, DEFAULT_WEBPACK_PORT);
|
||||
const apiPort = await choosePort(HOST, DEFAULT_API_PORT);
|
||||
|
||||
["SIGINT", "SIGTERM"].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
if (webpackPort == null) {
|
||||
console.error("Could not find a port for the Webpack server.");
|
||||
}
|
||||
if (apiPort == null) {
|
||||
console.error("Could not find a port for the API server.");
|
||||
}
|
||||
|
||||
const sourcecredDirectory =
|
||||
process.env.SOURCECRED_DIRECTORY || path.join(os.tmpdir(), "sourcecred");
|
||||
const apiServer = await new Promise(async (resolve, _unused_reject) => {
|
||||
let server = apiApp(sourcecredDirectory).listen(apiPort, () => {
|
||||
resolve(server);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
console.log(
|
||||
chalk.green(`Server listening on port ${apiServer.address().port}.`)
|
||||
);
|
||||
console.log();
|
||||
|
||||
const protocol = process.env.HTTPS === "true" ? "https" : "http";
|
||||
const appName = "sourcecred";
|
||||
const urls = prepareUrls(protocol, HOST, webpackPort);
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
|
||||
// Load proxy config
|
||||
const proxySetting = {
|
||||
"/api": {
|
||||
target: `${protocol}://localhost:${apiServer.address().port}`,
|
||||
},
|
||||
};
|
||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
||||
// Serve webpack assets generated by the compiler over a web sever.
|
||||
const serverConfig = createDevServerConfig(proxyConfig, urls.lanUrlForConfig);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(webpackPort, HOST, (err) => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
console.log(chalk.cyan("Starting the development server...\n"));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
["SIGINT", "SIGTERM"].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
apiServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// @flow
|
||||
|
||||
import express from "express";
|
||||
|
||||
export default function apiApp(sourcecredDirectory: string) {
|
||||
const app = express();
|
||||
app.use("/api/v1/data", express.static(sourcecredDirectory));
|
||||
return app;
|
||||
}
|
Loading…
Reference in New Issue