diff --git a/alfalfa_web/alfalfa.svg b/alfalfa_web/alfalfa.svg
deleted file mode 100644
index 04a1a8ad..00000000
--- a/alfalfa_web/alfalfa.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/alfalfa_web/components/App/App.js b/alfalfa_web/components/App/App.js
index c27b4091..0b7ac16b 100644
--- a/alfalfa_web/components/App/App.js
+++ b/alfalfa_web/components/App/App.js
@@ -17,7 +17,7 @@ export const App = () => {
-
+
diff --git a/alfalfa_web/app.js b/alfalfa_web/components/app.js
similarity index 76%
rename from alfalfa_web/app.js
rename to alfalfa_web/components/app.js
index f21a59d2..0602f038 100644
--- a/alfalfa_web/app.js
+++ b/alfalfa_web/components/app.js
@@ -4,11 +4,13 @@ import "@fontsource/roboto";
import "normalize.css/normalize.css";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
-import { App } from "./components/App/App.js";
+import { App } from "./App/App.js";
ReactDOM.render(
-
+
+
+
,
document.getElementById("root")
);
diff --git a/alfalfa_web/generate-docs.js b/alfalfa_web/generate-docs.js
index f0bb8fe8..293f6000 100644
--- a/alfalfa_web/generate-docs.js
+++ b/alfalfa_web/generate-docs.js
@@ -17,7 +17,7 @@ const openapiSpecification = swaggerJsdoc({
description:
"Alfalfa transforms Building Energy Models (BEMs) into virtual buildings by providing industry standard building control interfaces for interacting with models as they run",
"x-logo": {
- url: "/assets/alfalfa.svg"
+ url: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgOTIuMzYzIj48cGF0aCBmaWxsPSIjODM5NGEwIiBkPSJNMzUuNDE4IDg3LjM2MWMuMDAyLTEyLjcyMS4wMTMtMzEuNTE5LS4wMDItNDQuMjQxIDUuMzUzIDMuNTM4IDEwLjY1OSA3LjE0NiAxNi4wMTEgMTAuNjgzLjAxMSAzLjE1LS4wMDMgMzAuNDEuMDAzIDMzLjU1OEgzNS40MThabS0xLjQwNyAwYzAtMy41My0uMDA2LTguOTQ0LS4wMDItMTIuNDc1LS4wMDctLjM5LjAzOC0uNzg5LS4wNDMtMS4xNzNsLS4xMzUtLjEyOGMtMi42MDgtMS45NjUtNS4yMzItMy45MTMtNy44NDktNS44NjktMi4yMjMgMS42MjgtNC40MDcgMy4zMDctNi42MjIgNC45NDYtLjQ1Ni4zMzUtLjkyMi42Ni0xLjM1OSAxLjAyMWwuMDA0IDEzLjY3OGgxNi4wMDZaIi8+PHBhdGggZmlsbD0iIzAwNTE5MSIgZD0iTTEwOS4xNDQgODcuMzYyaC03LjE5NWwtMi4wMDMtNS42ODNIODkuODg5bC0yLjAwMyA1LjY4M2gtNi44NjhsMTAuNjI5LTI4LjI0OWg2Ljg2OGwxMC42MjkgMjguMjQ5Wk05OC40MzMgNzYuNTI4bC0zLjUxNi0xMC4xMzktMy41MTYgMTAuMTM5aDcuMDMyWm0yOC43MzkgMTAuODM0aC0xNi4zNTNWNTkuMTU0aDYuNTgydjIyLjczaDkuNzcxdjUuNDc4Wm0xOS44NjgtMjIuOTM0aC0xMS4wMzh2Ni45MDloMTAuMzg0djUuMjc0aC0xMC4zODR2MTAuNzUyaC02LjU4MlY1OS4xNTVoMTcuNjJ2NS4yNzRabTI2LjUzMiAyMi45MzNoLTcuMTk1bC0yLjAwMy01LjY4M2gtMTAuMDU3bC0yLjAwMyA1LjY4M2gtNi44NjhsMTAuNjI5LTI4LjI0OWg2Ljg2OGwxMC42MjkgMjguMjQ5Wm0tMTAuNzExLTEwLjgzNC0zLjUxNi0xMC4xMzktMy41MTYgMTAuMTM5aDcuMDMyWk0xOTEuNiA4Ny4zNjFoLTE2LjM1M1Y1OS4xNTNoNi41ODJ2MjIuNzNoOS43NzF2NS40NzhabTE5Ljg2OC0yMi45MzRIMjAwLjQzdjYuOTA5aDEwLjM4NHY1LjI3NEgyMDAuNDN2MTAuNzUyaC02LjU4MlY1OS4xNTRoMTcuNjJ2NS4yNzRaTTIzOCA4Ny4zNmgtNy4xOTVsLTIuMDAzLTUuNjgzaC0xMC4wNTdsLTIuMDAzIDUuNjgzaC02Ljg2OGwxMC42MjktMjguMjQ5aDYuODY4TDIzOCA4Ny4zNlptLTEwLjcxMS0xMC44MzQtMy41MTYtMTAuMTM5LTMuNTE2IDEwLjEzOWg3LjAzMloiLz48cGF0aCBmaWxsPSIjNDFhN2RiIiBkPSJNNzIuMzY2IDQxLjM5N3YzLjg3MmgtLjAwNmMtMy44MiAwLTYuOTQxIDMuMTEtNi45NDEgNi45NDFzMy4xMjEgNi45NDEgNi45NDEgNi45NDFoLjAwNXYzLjg3M2MtLjgyNi0uMDAyLTEuNjg3LS4xMzQtMy4xOC0uNDA3bC0xLjgyOSAzLjE2NGExNC4zNjcgMTQuMzY3IDAgMCAxLTQuMjUxLTIuNDU0bDEuODI5LTMuMTY0Yy0xLjk2OS0yLjMwMy0yLjE2My0yLjYzNy0zLjE3NS01LjQ5OUg1OC4xYTE1LjAyIDE1LjAyIDAgMCAxIDAtNC45MDdoMy42NTljMS4wMjItMi44ODQgMS4yNDgtMy4yNSAzLjE3NS01LjQ5OWwtMS44MjktMy4xNjRhMTQuMzY3IDE0LjM2NyAwIDAgMSA0LjI1MS0yLjQ1NGwxLjgyOSAzLjE2NGMxLjQ4Mi0uMjcxIDIuMzExLS40MTEgMy4xODEtLjQwN1pNNjguODYyIDUyLjIxYTMuNTA0IDMuNTA0IDAgMCAwIDMuNDk3IDMuNDk3aC4wMDV2LTYuOTk1aC0uMDA2YTMuNTA0IDMuNTA0IDAgMCAwLTMuNDk3IDMuNDk3Wm0xLjEwOCAxNS45NmEyLjUxNSAyLjUxNSAwIDAgMS0yLjkyNyAxLjE0MSAxOC4xNjUgMTguMTY1IDAgMCAxLTYuODIzLTMuOTQ5IDIuNDc0IDIuNDc0IDAgMCAxLS40NzMtMy4wODhsMS4wMjItMS43NzZhMTMuODY5IDEzLjg2OSAwIDAgMS0xLjM3Ny0yLjM4OWgtMi4wNDVhMi40OTggMi40OTggMCAwIDEtMi40NDMtMS45NTkgMTguMDE3IDE4LjAxNyAwIDAgMSAwLTcuODc3IDIuNDkgMi40OSAwIDAgMSAyLjQ0My0xLjk1OWgyLjA0NWMuMzc3LS44MzkuODM5LTEuNjM2IDEuMzc3LTIuMzg5bC0xLjAyMi0xLjc3NmEyLjQ3NyAyLjQ3NyAwIDAgMSAuNDczLTMuMDg4IDE3LjgxNSAxNy44MTUgMCAwIDEgNi44MzMtMy45NDkgMi40OCAyLjQ4IDAgMCAxIDIuOTA2IDEuMTNsMS4wMjIgMS43ODZjLjQ2LS4wNDMuOTIyLS4wNjUgMS4zODQtLjA2NHYtNS4xODJjLTEuMzc0LS4wMTEtMi43NTEtLjAwMS00LjEyNy0uMDA1di0zLjY5NGgtMi41ODZ2LTMuNjk0aC0xLjkwN2MtLjAwNC0yLjQ2MiAwLTQuOTI1IDAtNy4zODhoLTIuMjc4djcuMzg4Yy0uNjM3LS4wMDItMS4yNzQgMC0xLjkxMS4wMDUuMDAyIDEuMjI5LjAwNCAyLjQ1OSAwIDMuNjg4LS44ODMuMDAyLTEuNzY1IDAtMi42NDYuMDAxdjMuNjk0aC00LjA2M3Y1NC41ODZoMTkuNTJjLS4wMDItMy4yOC0uMDAzLTExLjYyNS0uMDAzLTIwLjkwNC0uNDU4IDAtLjkxNi0uMDIyLTEuMzcyLS4wNjRsLTEuMDIyIDEuNzc2Wm05LjMzLTE1Ljk2YzAtMy44MjktMy4xMTctNi45MzctNi45MzUtNi45NDF2My40NDRjMS45MjQuMDAzIDMuNDkyIDEuNTczIDMuNDkyIDMuNDk3cy0xLjU2OCAzLjQ5NC0zLjQ5MiAzLjQ5N3YzLjQ0NGMzLjgxOC0uMDAzIDYuOTM2LTMuMTEyIDYuOTM2LTYuOTQxWm0xMC41MzUtMy45MzlhMi41IDIuNSAwIDAgMC0yLjQ0My0xLjk1OWgtMi4wNDVjLS4zNzktLjg0LS44NC0xLjY0LTEuMzc3LTIuMzg5bDEuMDIyLTEuNzc2YTIuNTA2IDIuNTA2IDAgMCAwLS40NzMtMy4wODggMTguMTMxIDE4LjEzMSAwIDAgMC02LjgyMy0zLjk0OSAyLjUzIDIuNTMgMCAwIDAtMi45MzggMS4xNDFsLTEuMDIyIDEuNzc2YTE0LjU3NCAxNC41NzQgMCAwIDAtMS4zNy0uMDY0djMuNDM0Yy44MjMuMDAzIDEuNjgzLjEzNSAzLjE2OC40MDdsMS44MjktMy4xNjRhMTQuMjQ0IDE0LjI0NCAwIDAgMSA0LjI1MSAyLjQ1NGwtMS44MjkgMy4xNjRjMS45NjkgMi4zMDMgMi4xNjMgMi42MzYgMy4xNzUgNS40OTloMy42NTljLjI4IDEuNjI1LjI4IDMuMjkzIDAgNC45MDdIODIuOTZjLTEuMDIyIDIuODk1LTEuMjU5IDMuMjUtMy4xNzUgNS40OTlsMS44MjkgMy4xNjRhMTQuMzY3IDE0LjM2NyAwIDAgMS00LjI1MSAyLjQ1NGwtMS44MjktMy4xNjRjLTEuNDc2LjI3LTIuMzAzLjQxLTMuMTY5LjQwN3YzLjQzNGMuNDYyIDAgLjkyNC0uMDIxIDEuMzgzLS4wNjRsMS4wMjIgMS43ODZjLjU4MSAxLjAwMSAxLjgxOSAxLjQ4NSAyLjkxNiAxLjEzYTE3LjgxNSAxNy44MTUgMCAwIDAgNi44MzMtMy45NDkgMi41IDIuNSAwIDAgMCAuNDczLTMuMDg4bC0xLjAyMi0xLjc3NmMuNTM3LS43NDkuOTk5LTEuNTQ5IDEuMzc3LTIuMzg5aDIuMDQ1YTIuNDg4IDIuNDg4IDAgMCAwIDIuNDQzLTEuOTU5Yy4yOTEtMS4yOTEuNDMtMi42MTUuNDMtMy45MzlzLS4xNC0yLjY1OC0uNDMtMy45MzlaIi8+PC9zdmc+DQo="
}
},
servers: [
@@ -203,6 +203,3 @@ mkdirp.sync("build/app/assets");
fs.writeFileSync("build/app/openapi.json", JSON.stringify(openapiSpecification));
npmRun.execSync("redoc-cli build openapi.json -t ../../docs.hbs", { cwd: __dirname + "/build/app" });
-
-fs.copyFileSync("alfalfa.svg", "build/app/assets/alfalfa.svg");
-fs.renameSync("build/app/redoc-static.html", "build/app/docs.html");
diff --git a/alfalfa_web/package.json b/alfalfa_web/package.json
index af9a9c18..910cc083 100644
--- a/alfalfa_web/package.json
+++ b/alfalfa_web/package.json
@@ -9,8 +9,10 @@
"build-server": "babel -d ./build ./server",
"clean": "npx rimraf build",
"start": "node ./build/index.js",
- "start-dev": "npm run build-server && env-cmd -f ../.env npm start",
- "watch": "nodemon -e js,html --ignore build/ --exec \"npm run build-app && npm run build-server && npm start\""
+ "watch": "run-p watch-app watch-docs watch-server",
+ "watch-app": "webpack --watch",
+ "watch-docs": "nodemon --watch generate-docs.js --watch server/api-v2.js --watch server/api-haystack.js generate-docs.js",
+ "watch-server": "nodemon --watch server --exec \"npm run build-server && env-cmd -f ../.env npm start\""
},
"author": "",
"license": "ISC",
diff --git a/alfalfa_web/server/api.js b/alfalfa_web/server/api.js
index 02a2b98e..ec721908 100644
--- a/alfalfa_web/server/api.js
+++ b/alfalfa_web/server/api.js
@@ -32,6 +32,7 @@ class AlfalfaAPI {
this.s3 = new S3Client({
credentials,
endpoint: process.env.S3_URL_EXTERNAL || process.env.S3_URL,
+ forcePathStyle: true,
region
});
}
diff --git a/alfalfa_web/server/index.js b/alfalfa_web/server/index.js
index 4e1b2a19..077b4874 100644
--- a/alfalfa_web/server/index.js
+++ b/alfalfa_web/server/index.js
@@ -11,6 +11,7 @@ import { createClient } from "redis";
import alfalfaServer from "./alfalfa-server";
import apiv2 from "./api-v2";
+const isProd = process.env.NODE_ENV === "production";
const redis = createClient({ host: process.env.REDIS_HOST });
MongoClient.connect(process.env.MONGO_URL, { useUnifiedTopology: true })
@@ -35,13 +36,21 @@ MongoClient.connect(process.env.MONGO_URL, { useUnifiedTopology: true })
});
});
- app.get("/redoc", (req, res) => {
- const docsPath = path.join(__dirname, "/app/docs.html");
+ let cachedDocs;
+ app.get("/redoc", async (req, res) => {
+ if (isProd && cachedDocs) {
+ return res.type("html").send(cachedDocs);
+ }
+
+ const docsPath = path.join(__dirname, "app/redoc-static.html");
- fsp
- .access(docsPath, fsp.constants.F_OK)
- .then(() => res.sendFile(docsPath))
- .catch(() => res.status(404).type("txt").send("Documentation has not been generated"));
+ try {
+ await fsp.access(docsPath, fsp.constants.F_OK);
+ res.sendFile(docsPath);
+ if (isProd) cachedDocs = await fsp.readFile(docsPath, "utf-8");
+ } catch (e) {
+ res.status(404).type("txt").send("Documentation has not been generated");
+ }
});
app.use("/api/v2/", apiv2({ api: app.locals.alfalfaServer.api }));
diff --git a/alfalfa_web/webpack.config.js b/alfalfa_web/webpack.config.js
index 077bb323..2ee4659d 100644
--- a/alfalfa_web/webpack.config.js
+++ b/alfalfa_web/webpack.config.js
@@ -25,20 +25,10 @@ const getSha = () => {
module.exports = {
mode: isProd ? "production" : "development",
devtool: isProd ? false : "source-map",
- entry: "./app.js",
+ entry: "./components/app.js",
cache: {
type: "filesystem"
},
- devServer: {
- static: {
- directory: path.join(__dirname, "public")
- },
- compress: true,
- port: 80
- },
- watchOptions: {
- ignored: ["**/node_modules", "**/scripts", "**/server"]
- },
output: {
path: path.resolve(__dirname, "build/app"),
filename: "app.bundle.js"
@@ -53,6 +43,9 @@ module.exports = {
})
]
},
+ performance: {
+ hints: false
+ },
module: {
rules: [
{