Pre-compile frontend and add environment config script

This commit is contained in:
Lukas Hass 2022-01-21 21:01:34 +01:00
parent 543ccb4fb6
commit 5dabd0b2f8
No known key found for this signature in database
GPG Key ID: 7C8CEF72C4039178
6 changed files with 98 additions and 69 deletions

View File

@ -1,23 +1,35 @@
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
WORKDIR /usr/src
WORKDIR /usr/src/messages
COPY messages .
RUN yarn install && yarn ts-proto
# we are rebuilding on each deploy to cope with the PUSHER_URL environment URL
FROM thecodingmachine/nodejs:14-apache
WORKDIR /usr/src/front
COPY front .
COPY --chown=docker:docker front .
COPY --from=builder --chown=docker:docker /usr/src/ts-proto-generated/protos /var/www/html/src/Messages/ts-proto-generated
RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' /var/www/html/src/Messages/ts-proto-generated/messages.ts
COPY --from=builder --chown=docker:docker /usr/src/JsonMessages /var/www/html/src/Messages/JsonMessages
# move messages to front
RUN cp -r ../messages/ts-proto-generated/protos/* src/Messages/ts-proto-generated
RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' src/Messages/ts-proto-generated/messages.ts
RUN cp -r ../messages/JsonMessages src/Messages/JsonMessages
RUN yarn install && yarn build
# Removing the iframe.html file from the final image as this adds a XSS attack.
# iframe.html is only in dev mode to circumvent a limitation
RUN rm dist/iframe.html
RUN yarn install
FROM thecodingmachine/nodejs:14-apache
COPY --from=builder --chown=docker:docker /usr/src/front/dist dist
COPY front/templater.sh .
USER root
RUN DEBIAN_FRONTEND=noninteractive apt-get update \
&& apt-get install -y \
gettext-base \
&& rm -rf /var/lib/apt/lists/*
USER docker
ENV NODE_ENV=production
ENV STARTUP_COMMAND_0="./templater.sh"
ENV STARTUP_COMMAND_1="yarn run build"
ENV STARTUP_COMMAND_1="envsubst < dist/env-config.template.js > dist/env-config.js"
ENV APACHE_DOCUMENT_ROOT=dist/

View File

@ -1,4 +1,3 @@
index.html
index.tmpl.html.tmp
/js/
style.*.css

View File

@ -27,7 +27,7 @@
<meta name="msapplication-TileImage" content="static/images/favicons/ms-icon-144x144.png">
<meta name="theme-color" content="#000000">
<script src="/env-config.js"></script>
<base href="/">
<link href="https://unpkg.com/nes.css@2.3.0/css/nes.min.css" rel="stylesheet" />

View File

@ -1,30 +1,46 @@
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
const START_ROOM_URL: string =
process.env.START_ROOM_URL || "/_/global/maps.workadventure.localhost/Floor1/floor1.json";
const PUSHER_URL = process.env.PUSHER_URL || "//pusher.workadventure.localhost";
export const ADMIN_URL = process.env.ADMIN_URL || "//workadventu.re";
const UPLOADER_URL = process.env.UPLOADER_URL || "//uploader.workadventure.localhost";
const ICON_URL = process.env.ICON_URL || "//icon.workadventure.localhost";
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
const TURN_SERVER: string = process.env.TURN_SERVER || "";
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true";
const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true";
const TURN_USER: string = process.env.TURN_USER || "";
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || "";
const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL;
const JITSI_PRIVATE_MODE: boolean = process.env.JITSI_PRIVATE_MODE == "true";
declare global {
interface Window {
env?: Record<string, string>;
}
}
const getEnv = (key: string): string | undefined => {
if (!window.env) {
return;
}
const value = window.env[key];
if (value === "") {
return;
}
return value;
};
const DEBUG_MODE: boolean = getEnv("DEBUG_MODE") == "true";
const START_ROOM_URL: string = getEnv("START_ROOM_URL") || "/_/global/maps.workadventure.localhost/Floor1/floor1.json";
const PUSHER_URL = getEnv("PUSHER_URL") || "//pusher.workadventure.localhost";
export const ADMIN_URL = getEnv("ADMIN_URL") || "//workadventu.re";
const UPLOADER_URL = getEnv("UPLOADER_URL") || "//uploader.workadventure.localhost";
const ICON_URL = getEnv("ICON_URL") || "//icon.workadventure.localhost";
const STUN_SERVER: string = getEnv("STUN_SERVER") || "stun:stun.l.google.com:19302";
const TURN_SERVER: string = getEnv("TURN_SERVER") || "";
const SKIP_RENDER_OPTIMIZATIONS: boolean = getEnv("SKIP_RENDER_OPTIMIZATIONS") == "true";
const DISABLE_NOTIFICATIONS: boolean = getEnv("DISABLE_NOTIFICATIONS") == "true";
const TURN_USER: string = getEnv("TURN_USER") || "";
const TURN_PASSWORD: string = getEnv("TURN_PASSWORD") || "";
const JITSI_URL: string | undefined = getEnv("JITSI_URL") === "" ? undefined : getEnv("JITSI_URL");
const JITSI_PRIVATE_MODE: boolean = getEnv("JITSI_PRIVATE_MODE") == "true";
const POSITION_DELAY = 200; // Wait 200ms between sending position events
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || "") || 8;
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4");
export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == "true";
export const NODE_ENV = process.env.NODE_ENV || "development";
export const CONTACT_URL = process.env.CONTACT_URL || undefined;
export const PROFILE_URL = process.env.PROFILE_URL || undefined;
export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || "";
export const POSTHOG_URL = process.env.POSTHOG_URL || undefined;
export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true";
export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER;
export const MAX_USERNAME_LENGTH = parseInt(getEnv("MAX_USERNAME_LENGTH") || "") || 8;
export const MAX_PER_GROUP = parseInt(getEnv("MAX_PER_GROUP") || "4");
export const DISPLAY_TERMS_OF_USE = getEnv("DISPLAY_TERMS_OF_USE") == "true";
export const NODE_ENV = getEnv("NODE_ENV") || "development";
export const CONTACT_URL = getEnv("CONTACT_URL") || undefined;
export const PROFILE_URL = getEnv("PROFILE_URL") || undefined;
export const POSTHOG_API_KEY: string = (getEnv("POSTHOG_API_KEY") as string) || "";
export const POSTHOG_URL = getEnv("POSTHOG_URL") || undefined;
export const DISABLE_ANONYMOUS: boolean = getEnv("DISABLE_ANONYMOUS") === "true";
export const OPID_LOGIN_SCREEN_PROVIDER = getEnv("OPID_LOGIN_SCREEN_PROVIDER");
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;

View File

@ -1,8 +1,7 @@
#!/usr/bin/env bash
set -x
set -o nounset errexit
template_file_index=dist/index.tmpl.html
generated_file_index=dist/index.tmpl.html.tmp
index_file=dist/index.html
tmp_trackcodefile=/tmp/trackcode
# To inject tracking code, you have two choices:
@ -21,6 +20,6 @@ if [[ "${GA_TRACKING_ID:-}" != "" || "${INSERT_ANALYTICS:-NO}" != "NO" ]]; then
sed "s#<!-- TRACKING NUMBER -->#${GA_TRACKING_ID:-}#g" "${ANALYTICS_CODE_PATH}" > "$tmp_trackcodefile"
fi
echo "Templating ${generated_file_index} from ${template_file_index}"
sed "/<!-- TRACK CODE -->/r ${tmp_trackcodefile}" "${template_file_index}" > "${generated_file_index}"
echo "Templating ${index_file}"
sed --in-place "/<!-- TRACK CODE -->/r ${tmp_trackcodefile}" "${index_file}"
rm "${tmp_trackcodefile}"

View File

@ -1,5 +1,6 @@
import type { Configuration } from "webpack";
import type WebpackDevServer from "webpack-dev-server";
import fs from 'fs/promises';
import path from "path";
import webpack from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
@ -33,6 +34,35 @@ module.exports = {
disableDotRule: true,
},
liveReload: process.env.LIVE_RELOAD != "0" && process.env.LIVE_RELOAD != "false",
before: (app) => {
let appConfigContent = '';
const TEMPLATE_PATH = path.join(__dirname, 'dist', 'env-config.template.js');
function renderTemplateWithEnvVars(content: string): string {
let result = content;
const regex = /\$\{([a-zA-Z_]+[a-zA-Z0-9_]*?)\}/g;
let matched: RegExpExecArray | null;
while ((matched = regex.exec(content))) {
result = result.replace(`\${${matched[1]}}`, process.env[matched[1]] || '');
}
return result;
}
void (async () => {
const content = (await fs.readFile(TEMPLATE_PATH)).toString();
appConfigContent = renderTemplateWithEnvVars(content);
})();
app.get('/env-config.js', (_, response) => {
response.setHeader('Content-Type', 'application/javascript; charset=utf-8');
response.setHeader('Cache-Control', 'no-cache');
response.setHeader('Content-Length', Buffer.byteLength(appConfigContent, 'utf8'));
response.send(appConfigContent);
});
},
},
module: {
rules: [
@ -168,7 +198,7 @@ module.exports = {
}),
new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" }),
new HtmlWebpackPlugin({
template: "./dist/index.tmpl.html.tmp",
template: "./dist/index.ejs",
minify: {
collapseWhitespace: true,
keepClosingSlash: true,
@ -184,32 +214,5 @@ module.exports = {
Phaser: "phaser",
}),
new NodePolyfillPlugin(),
new webpack.EnvironmentPlugin({
API_URL: null,
SKIP_RENDER_OPTIMIZATIONS: false,
DISABLE_NOTIFICATIONS: false,
PUSHER_URL: undefined,
UPLOADER_URL: null,
ADMIN_URL: null,
CONTACT_URL: null,
PROFILE_URL: null,
ICON_URL: null,
DEBUG_MODE: null,
STUN_SERVER: null,
TURN_SERVER: null,
TURN_USER: null,
TURN_PASSWORD: null,
JITSI_URL: null,
JITSI_PRIVATE_MODE: null,
START_ROOM_URL: null,
MAX_USERNAME_LENGTH: 8,
MAX_PER_GROUP: 4,
DISPLAY_TERMS_OF_USE: false,
POSTHOG_API_KEY: null,
POSTHOG_URL: null,
NODE_ENV: mode,
DISABLE_ANONYMOUS: false,
OPID_LOGIN_SCREEN_PROVIDER: null,
}),
],
} as Configuration & WebpackDevServer.Configuration;