Merge pull request #1748 from thecodingmachine/pre-compile-front

Pre-compile frontend and add environment config script (2)
This commit is contained in:
David Négrier 2022-01-27 16:00:59 +01:00 committed by GitHub
commit 89ba62c898
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 126 additions and 71 deletions

View File

@ -1,23 +1,35 @@
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
WORKDIR /usr/src
WORKDIR /usr/src/messages
COPY messages . COPY messages .
RUN yarn install && yarn ts-proto RUN yarn install && yarn ts-proto
# we are rebuilding on each deploy to cope with the PUSHER_URL environment URL WORKDIR /usr/src/front
FROM thecodingmachine/nodejs:14-apache COPY front .
COPY --chown=docker:docker front . # move messages to front
COPY --from=builder --chown=docker:docker /usr/src/ts-proto-generated/protos /var/www/html/src/Messages/ts-proto-generated 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' /var/www/html/src/Messages/ts-proto-generated/messages.ts RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' src/Messages/ts-proto-generated/messages.ts
COPY --from=builder --chown=docker:docker /usr/src/JsonMessages /var/www/html/src/Messages/JsonMessages RUN cp -r ../messages/JsonMessages/* src/Messages/JsonMessages
RUN yarn install && yarn run typesafe-i18n && yarn build
# Removing the iframe.html file from the final image as this adds a XSS attack. # 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 # iframe.html is only in dev mode to circumvent a limitation
RUN rm dist/iframe.html 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_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/ ENV APACHE_DOCUMENT_ROOT=dist/

View File

@ -1,4 +1,4 @@
index.html index.html
index.tmpl.html.tmp
/js/ /js/
style.*.css style.*.css
!env-config.template.js

27
front/dist/env-config.template.js vendored Normal file
View File

@ -0,0 +1,27 @@
window.env = {
SKIP_RENDER_OPTIMIZATIONS: '${SKIP_RENDER_OPTIMIZATIONS}',
DISABLE_NOTIFICATIONS: '${DISABLE_NOTIFICATIONS}',
PUSHER_URL: '${PUSHER_URL}',
UPLOADER_URL: '${UPLOADER_URL}',
ADMIN_URL: '${ADMIN_URL}',
CONTACT_URL: '${CONTACT_URL}',
PROFILE_URL: '${PROFILE_URL}',
ICON_URL: '${ICON_URL}',
DEBUG_MODE: '${DEBUG_MODE}',
STUN_SERVER: '${STUN_SERVER}',
TURN_SERVER: '${TURN_SERVER}',
TURN_USER: '${TURN_USER}',
TURN_PASSWORD: '${TURN_PASSWORD}',
JITSI_URL: '${JITSI_URL}',
JITSI_PRIVATE_MODE: '${JITSI_PRIVATE_MODE}',
START_ROOM_URL: '${START_ROOM_URL}',
MAX_USERNAME_LENGTH: '${MAX_USERNAME_LENGTH}',
MAX_PER_GROUP: '${MAX_PER_GROUP}',
DISPLAY_TERMS_OF_USE: '${DISPLAY_TERMS_OF_USE}',
POSTHOG_API_KEY: '${POSTHOG_API_KEY}',
POSTHOG_URL: '${POSTHOG_URL}',
NODE_ENV: '${NODE_ENV}',
DISABLE_ANONYMOUS: '${DISABLE_ANONYMOUS}',
OPID_LOGIN_SCREEN_PROVIDER: '${OPID_LOGIN_SCREEN_PROVIDER}',
FALLBACK_LOCALE: '${FALLBACK_LOCALE}',
};

View File

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

View File

@ -1,31 +1,46 @@
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; declare global {
const START_ROOM_URL: string = interface Window {
process.env.START_ROOM_URL || "/_/global/maps.workadventure.localhost/Floor1/floor1.json"; env?: Record<string, string>;
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 getEnv = (key: string): string | undefined => {
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302"; if (global.window?.env) {
const TURN_SERVER: string = process.env.TURN_SERVER || ""; return global.window.env[key];
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true"; }
const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true"; if (global.process?.env) {
const TURN_USER: string = process.env.TURN_USER || ""; return global.process.env[key];
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || ""; }
const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL; return;
const JITSI_PRIVATE_MODE: boolean = process.env.JITSI_PRIVATE_MODE == "true"; };
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 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 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_USERNAME_LENGTH = parseInt(getEnv("MAX_USERNAME_LENGTH") || "") || 8;
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4"); export const MAX_PER_GROUP = parseInt(getEnv("MAX_PER_GROUP") || "4");
export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == "true"; export const DISPLAY_TERMS_OF_USE = getEnv("DISPLAY_TERMS_OF_USE") == "true";
export const NODE_ENV = process.env.NODE_ENV || "development"; export const NODE_ENV = getEnv("NODE_ENV") || "development";
export const CONTACT_URL = process.env.CONTACT_URL || undefined; export const CONTACT_URL = getEnv("CONTACT_URL") || undefined;
export const PROFILE_URL = process.env.PROFILE_URL || undefined; export const PROFILE_URL = getEnv("PROFILE_URL") || undefined;
export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || ""; export const POSTHOG_API_KEY: string = (getEnv("POSTHOG_API_KEY") as string) || "";
export const POSTHOG_URL = process.env.POSTHOG_URL || undefined; export const POSTHOG_URL = getEnv("POSTHOG_URL") || undefined;
export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true"; export const DISABLE_ANONYMOUS: boolean = getEnv("DISABLE_ANONYMOUS") === "true";
export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER; export const OPID_LOGIN_SCREEN_PROVIDER = getEnv("OPID_LOGIN_SCREEN_PROVIDER");
const FALLBACK_LOCALE = process.env.FALLBACK_LOCALE || undefined; const FALLBACK_LOCALE = getEnv("FALLBACK_LOCALE") || undefined;
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600; export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;

View File

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

View File

@ -1,4 +1,5 @@
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import fs from 'fs/promises';
import HtmlWebpackPlugin from "html-webpack-plugin"; import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin"; import MiniCssExtractPlugin from "mini-css-extract-plugin";
import NodePolyfillPlugin from "node-polyfill-webpack-plugin"; import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
@ -33,6 +34,35 @@ module.exports = {
disableDotRule: true, disableDotRule: true,
}, },
liveReload: process.env.LIVE_RELOAD != "0" && process.env.LIVE_RELOAD != "false", 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: { module: {
rules: [ rules: [
@ -173,7 +203,7 @@ module.exports = {
}), }),
new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" }), new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: "./dist/index.tmpl.html.tmp", template: "./dist/index.ejs",
minify: { minify: {
collapseWhitespace: true, collapseWhitespace: true,
keepClosingSlash: true, keepClosingSlash: true,
@ -189,33 +219,5 @@ module.exports = {
Phaser: "phaser", Phaser: "phaser",
}), }),
new NodePolyfillPlugin(), 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,
FALLBACK_LANGUAGE: null,
}),
], ],
} as Configuration & WebpackDevServer.Configuration; } as Configuration & WebpackDevServer.Configuration;