Skip to content

Commit

Permalink
feat: provide deploy-url as runtime property
Browse files Browse the repository at this point in the history
  • Loading branch information
dhhyi committed Apr 17, 2021
1 parent 8c725de commit c618d58
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ RUN node schematics/customization/service-worker ${serviceWorker} || true
COPY templates/webpack/* /workspace/templates/webpack/
ARG testing=false
ENV TESTING=${testing}
RUN npm run ng -- build -c ${configuration}
RUN npm run ng -- build -c ${configuration} --deploy-url=DEPLOY_URL_PLACEHOLDER
# synchronize-marker:pwa-docker-build:end

# ^ this part above is copied to Dockerfile_noSSR and should be kept in sync
Expand Down
5 changes: 4 additions & 1 deletion Dockerfile_noSSR
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ RUN node schematics/customization/service-worker ${serviceWorker} || true
COPY templates/webpack/* /workspace/templates/webpack/
ARG testing=false
ENV TESTING=${testing}
RUN npm run ng -- build -c ${configuration}
RUN npm run ng -- build -c ${configuration} --deploy-url=DEPLOY_URL_PLACEHOLDER
# synchronize-marker:pwa-docker-build:end

# ^ this part above is copied from the standard Dockerfile and should be kept in sync

ARG deployUrl=/
RUN npx ts-node scripts/set-deploy-url ${deployUrl}

FROM nginx:alpine
COPY templates/nginx.conf /etc/nginx/nginx.conf
COPY --from=buildstep /workspace/dist/browser /usr/share/nginx/html
Expand Down
26 changes: 22 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,32 @@ services:
# - PROXY_ICM=true
- TRUST_ICM=true
# - PROMETHEUS=on

# <CDN-Example>
# add 127.0.0.1 mypwa.net to your hosts file and
# uncomment the following lines
#
# - DEPLOY_URL=http://mypwa.net:4222
# cdn:
# build:
# context: .
# dockerfile: Dockerfile_noSSR
# args:
# configuration: production
# serviceWorker: 'false'
# deployUrl: http://mypwa.net:4222
# ports:
# - '4222:4200'
#
# </CDN-Example>

nginx:
build: nginx
depends_on:
- pwa
ports:
- '4200:80'
# - '9113:9113'
environment:
UPSTREAM_PWA: 'http://pwa:4200'
# DEBUG: 1
Expand Down Expand Up @@ -54,7 +76,3 @@ services:
application: smb-responsive
features: quoting
theme: "blue|688dc3"
ports:
- '4200:80'
# - '9113:9113'
26 changes: 26 additions & 0 deletions scripts/set-deploy-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// tslint:disable: ish-ordered-imports ban-specific-imports no-console
import * as fs from 'fs';
import * as glob from 'glob';
import * as path from 'path';
import { setDeployUrlInFile } from '../src/ssr/deploy-url';

if (process.argv.length < 3) {
console.error('required argument deployUrl missing');
process.exit(1);
}

let deployUrl = process.argv[2];

if (!deployUrl.endsWith('/')) {
deployUrl += '/';
}

glob.sync('dist/browser/*.{js,css,html}').forEach(file => {
console.log(`setting deployUrl "${deployUrl}" in`, file);

const input = fs.readFileSync(file, { encoding: 'utf-8' });

const output = setDeployUrlInFile(deployUrl, path.basename(file), input);

fs.writeFileSync(file, output);
});
38 changes: 31 additions & 7 deletions server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// tslint:disable: no-console ish-ordered-imports force-jsdoc-comments
// tslint:disable: no-console ish-ordered-imports force-jsdoc-comments ban-specific-imports
import 'zone.js/dist/zone-node';

import * as express from 'express';
import { join } from 'path';
import * as robots from 'express-robots-txt';
import * as fs from 'fs';
import * as proxy from 'express-http-proxy';
// tslint:disable-next-line: ban-specific-imports
import { AppServerModule, ICM_WEB_URL, HYBRID_MAPPING_TABLE, environment, APP_BASE_HREF } from './src/main.server';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { getDeployURLFromEnv, setDeployUrlInFile } from './src/ssr/deploy-url';

const PORT = process.env.PORT || 4200;

const DEPLOY_URL = getDeployURLFromEnv();

const DIST_FOLDER = join(process.cwd(), 'dist');

// uncomment this block to prevent ssr issues with third-party libraries regarding window, document, HTMLElement and navigator
Expand All @@ -28,8 +30,8 @@ global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
// tslint:enable:no-string-literal
*/

// The Express app is exported so that it can be used by serverless Functions.
// not-dead-code
export function app() {
const logging = /on|1|true|yes/.test(process.env.LOGGING?.toLowerCase());

Expand Down Expand Up @@ -65,7 +67,7 @@ export function app() {
if (logging) {
server.use(
require('morgan')('tiny', {
skip: req => req.originalUrl.startsWith('/INTERSHOP/static'),
skip: (req: express.Request) => req.originalUrl.startsWith('/INTERSHOP/static'),
})
);
}
Expand Down Expand Up @@ -95,6 +97,18 @@ export function app() {
);
}

server.get(/\/.*\.(js|css)$/, (req, res) => {
const path = req.originalUrl.substring(1);
fs.readFile(join(DIST_FOLDER, 'browser', path), { encoding: 'utf-8' }, (err, data) => {
if (err) {
res.sendStatus(404);
} else {
res.set('Content-Type', (path.endsWith('css') ? 'text/css' : 'application/javascript') + '; charset=UTF-8');
res.send(setDeployUrlInFile(DEPLOY_URL, path, data));
}
});
});

// Serve static files from /browser
server.get(
'*.*',
Expand All @@ -107,14 +121,22 @@ export function app() {
// file should be re-checked more frequently -> 5m
res.set('Cache-Control', 'public, max-age=300');
}
// add cors headers for required resources
if (
DEPLOY_URL.startsWith('http') &&
['manifest.webmanifest', 'woff2', 'woff', 'json'].some(match => path.endsWith(match))
) {
res.set('access-control-allow-origin', '*');
}
},
})
);

const icmProxy = proxy(ICM_BASE_URL, {
// preserve original path
proxyReqPathResolver: req => req.originalUrl,
proxyReqOptDecorator: options => {
proxyReqPathResolver: (req: express.Request) => req.originalUrl,
// tslint:disable-next-line: no-any
proxyReqOptDecorator: (options: any) => {
if (process.env.TRUST_ICM) {
// https://github.com/villadora/express-http-proxy#q-how-to-ignore-self-signed-certificates-
options.rejectUnauthorized = false;
Expand Down Expand Up @@ -173,7 +195,9 @@ export function app() {
}
newHtml = newHtml.replace(/<base href="[^>]*>/, `<base href="${baseHref}" />`);

res.status(res.statusCode).send(newHtml || html);
newHtml = setDeployUrlInFile(DEPLOY_URL, req.originalUrl, newHtml);

res.status(res.statusCode).send(newHtml);
} else {
res.status(500).send(err.message);
}
Expand Down
33 changes: 33 additions & 0 deletions src/ssr/deploy-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function getDeployURLFromEnv(): string {
const envDeployUrl = process.env.DEPLOY_URL || '/';

return `${envDeployUrl}${envDeployUrl.endsWith('/') ? '' : '/'}`;
}

export function setDeployUrlInFile(deployUrl: string, path: string, input: string): string {
if (input) {
if (path.startsWith('runtime') && path.endsWith('.js')) {
return input.replace(/DEPLOY_URL_PLACEHOLDER/g, deployUrl);
}

let newInput = input;

const cssRegex = /url\(\/?(assets.*?|[a-zA-Z].*?woff2?)\)/g;
if (cssRegex.test(newInput)) {
newInput = newInput.replace(cssRegex, (...args) => `url(${deployUrl}${args[1]})`);
}

const assetsRegex = /"\/?(assets[^"]*\.(\w{2,5}|webmanifest)|[a-z0-9]+\.css)"/g;
if (assetsRegex.test(newInput)) {
newInput = newInput.replace(assetsRegex, (...args) => `"${deployUrl}${args[1]}"`);
}

const javascriptRegex = /"(DEPLOY_URL_PLACEHOLDER|\/)?((runtime|vendor|main|polyfills|styles)[^"]*\.(js|css))"/g;
if (javascriptRegex.test(newInput)) {
newInput = newInput.replace(javascriptRegex, (...args) => `"${deployUrl}${args[2]}"`);
}

return newInput;
}
return input;
}
4 changes: 4 additions & 0 deletions src/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ declare module '@angular/common/locales/global/de';

declare module '@angular/common/locales/global/fr';

declare module 'express-http-proxy';

declare module 'express-robots-txt';

interface NodeModule {
id: string;
}
Expand Down
19 changes: 17 additions & 2 deletions templates/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ events {
}

http {
include /etc/nginx/mime.types;
include mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
Expand All @@ -26,17 +26,32 @@ http {
keepalive_timeout 65;

gzip on;
gzip_types text/plain text/xml text/css
text/javascript application/javascript;

server {
listen 4200;
listen [::]:4200;
server_name localhost;

location / {
location ~* /.*index.html {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}

location / {
root /usr/share/nginx/html;

if ($request_method = OPTIONS) {
return 204;
}

add_header Access-Control-Allow-Origin *;
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Expose-Headers Content-Length;
add_header Access-Control-Allow-Headers Range;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.all.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"outDir": "./node_modules/.out-tsc/all",
"types": ["jest", "jest-extended", "node"]
},
"include": ["src/**/*.ts", "projects/**/*.ts"]
"include": ["src/**/*.ts", "projects/**/*.ts", "server.ts", "src/**/*.d.ts"]
}
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@
],
"ignoredFiles": [
"server.ts$",
"src/ssr/.*.ts",
"src/[^/]*.ts$",
".*.spec.ts$",
"tslint-rules/",
Expand Down

1 comment on commit c618d58

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Azure Demo Servers are available:

Please sign in to comment.