1
0
Fork 1
mirror of https://github.com/thatmattlove/hyperglass.git synced 2026-01-17 08:48:05 +00:00

add app-level getInitialProps for full SSR; add UI build process to web app

This commit is contained in:
checktheroads 2020-01-21 17:31:35 -07:00
parent 9ca90cc2ae
commit 37f3194bbc
5 changed files with 122 additions and 30 deletions

View file

@ -54,11 +54,10 @@ async def build_ui():
"""
import asyncio
from pathlib import Path
import ujson as json
ui_dir = Path(__file__).parent.parent / "ui"
yarn_command = "yarn --silent --emoji false --json --no-progress build"
yarn_command = "yarn --silent --emoji false --no-progress build"
try:
proc = await asyncio.create_subprocess_shell(
cmd=yarn_command,
@ -68,20 +67,17 @@ async def build_ui():
)
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=60)
output_out = json.loads(stdout.decode("utf-8").split("\n")[0])
messages = stdout.decode("utf-8").strip()
errors = stderr.decode("utf-8").strip()
if proc.returncode != 0:
output_error = json.loads(stderr.decode("utf-8").strip("\n"))
raise RuntimeError(
f'Error building web assets with script {output_out["data"]}:'
f'{output_error["data"]}'
)
raise RuntimeError(f"\nMessages:\n{messages}\nErrors:\n{errors}")
await proc.wait()
except Exception as e:
raise RuntimeError(str(e))
return output_out["data"]
return messages
async def write_env(variables):
@ -158,3 +154,100 @@ async def clear_redis_cache(db, config):
except Exception as e:
raise RuntimeError(f"Error clearing cache: {str(e)}") from None
return True
async def build_frontend(dev_mode, dev_url, prod_url, params, force=False):
"""Perform full frontend UI build process.
Securely creates temporary file, writes frontend configuration
parameters to file as JSON. Then writes the name of the temporary
file to /tmp/hyperglass.env.json as {"configFile": <file_name> }.
Webpack reads /tmp/hyperglass.env.json, loads the temporary file,
and sets its contents to Node environment variables during the build
process.
After the build is successful, the temporary file is automatically
closed during garbage collection.
Arguments:
dev_mode {bool} -- Development Mode
dev_url {str} -- Development Mode URL
prod_url {str} -- Production Mode URL
params {dict} -- Frontend Config paramters
Raises:
RuntimeError: Raised if errors occur during build process.
Returns:
{bool} -- True if successful
"""
import hashlib
import tempfile
from pathlib import Path
from aiofile import AIOFile
import ujson as json
env_file = Path("/tmp/hyperglass.env.json") # noqa: S108
env_vars = {"_HYPERGLASS_CONFIG_": params}
# Set NextJS production/development mode and base URL based on
# developer_mode setting.
if dev_mode:
env_vars.update({"NODE_ENV": "development", "_HYPERGLASS_URL_": dev_url})
else:
env_vars.update({"NODE_ENV": "production", "_HYPERGLASS_URL_": prod_url})
try:
env_json = json.dumps(env_vars)
# Create SHA256 hash from all parameters passed to UI, use as
# build identifier.
build_id = hashlib.sha256(env_json.encode()).hexdigest()
# Read hard-coded environment file from last build. If build ID
# matches this build's ID, don't run a new build.
if env_file.exists() and not force:
async with AIOFile(env_file, "r") as ef:
ef_json = await ef.read()
ef_id = json.loads(ef_json).get("buildId", "empty")
if ef_id == build_id:
log.debug(
"No changes to UI parameters since last build, skipping..."
)
return True
# Create temporary file. .json file extension is added for easy
# webpack JSON parsing.
temp_file = tempfile.NamedTemporaryFile(
mode="w+", prefix="hyperglass_", suffix=".json", delete=not dev_mode
)
log.debug(
f"Created temporary UI config file: '{temp_file.name}' for build {build_id}"
)
async with AIOFile(temp_file.name, "w+") as temp:
await temp.write(env_json)
await temp.fsync()
# Write "permanent" file (hard-coded named) for Node to read.
async with AIOFile(env_file, "w+") as ef:
await ef.write(
json.dumps({"configFile": temp_file.name, "buildId": build_id})
)
await ef.fsync()
# While temporary file is still open, initiate UI build process.
if not dev_mode or force:
build_result = await build_ui()
if build_result:
log.debug("Completed UI build")
elif dev_mode and not force:
log.debug("Running in developer mode, did not run `yarn build`")
except Exception as e:
raise RuntimeError(str(e))
return True

View file

@ -1,4 +1,7 @@
const aliases = require("./.alias");
const envVars = require("/tmp/hyperglass.env.json");
const { configFile } = envVars;
const config = require(String(configFile));
module.exports = {
webpack(config) {
@ -9,5 +12,10 @@ module.exports = {
};
return config;
},
poweredByHeader: false
poweredByHeader: false,
env: {
_NODE_ENV_: config.NODE_ENV,
_HYPERGLASS_URL_: config._HYPERGLASS_URL_,
_HYPERGLASS_CONFIG_: config._HYPERGLASS_CONFIG_
}
};

View file

@ -2,12 +2,13 @@
const express = require("express");
const next = require("next");
const envVars = require("/tmp/hyperglass.env.json");
const env = envVars.NODE_ENV;
const envUrl = envVars._HYPERGLASS_URL_;
const { configFile } = envVars;
const config = require(String(configFile));
const { NODE_ENV: env, _HYPERGLASS_URL_: envUrl } = config;
const devProxy = {
"/api/config": { target: envUrl + "config", pathRewrite: { "^/api/config": "" } },
"/api/query": { target: envUrl + "query", pathRewrite: { "^/api/query": "" } },
"/api/query/": { target: envUrl + "query/", pathRewrite: { "^/api/query/": "" } },
"/images": { target: envUrl + "images", pathRewrite: { "^/images": "" } }
};

View file

@ -1,23 +1,13 @@
import React from "react";
import useAxios from "axios-hooks";
import { HyperglassProvider } from "~/components/HyperglassProvider";
import PreConfig from "~/components/PreConfig";
const config = process.env._HYPERGLASS_CONFIG_;
const Hyperglass = ({ Component, pageProps }) => {
const [{ data, loading, error }, refetch] = useAxios({
url: "/api/config",
method: "get"
});
return (
<>
{!data ? (
<PreConfig loading={loading} error={error} refresh={refetch} />
) : (
<HyperglassProvider config={data}>
<Component {...pageProps} />
</HyperglassProvider>
)}
</>
<HyperglassProvider config={config}>
<Component {...pageProps} />
</HyperglassProvider>
);
};

View file

@ -91,7 +91,7 @@ const ErrorPage = ({ msg, statusCode }) => {
ErrorPage.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
const msg = err ? err.message : res.req.url;
const msg = err ? err.message : res.req?.url || "Error";
return { msg, statusCode };
};