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: [ {