Commit 4bf71237 authored by Matija Obreza's avatar Matija Obreza
Browse files

Webpack the server-side to server.js

- Updated CI for new packaging
- Removed gulp and gulpfile.js
parent 4b926b0a
Pipeline #4013 passed with stages
in 2 minutes and 40 seconds
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const commonConfig = require('./webpack-base.config.js');
module.exports = {
name: 'server',
target: 'node',
entry: {
server: ['./server/index.ts']
},
output: {
filename: '[name].js',
chunkFilename: '[name].js',
path: path.join(process.cwd(), 'target/app/server')
},
resolve: {
extensions: [
'.ts', '.tsx', '.js', '.jsx'
],
modules: [path.resolve('./src'), 'node_modules']
},
module: {
rules: [
{
enforce: 'pre',
test: /\.tsx?$/,
loader: 'tslint-loader'
},
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{
test: /\.tsx?$/,
use: ["awesome-typescript-loader"]
}
]
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
// https://facebook.github.io/react/docs/optimizing-performance.html#use-the-production-build
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new UglifyJsPlugin({
beautify: false,
mangle: {
screw_ie8: true,
keep_fnames: true
},
compress: {
screw_ie8: true,
warnings: false
},
comments: true
}),
]
};
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack-base.config.js');
var stylelint = require('stylelint');
var postcssAssets = require('postcss-assets');
var postcssNext = require('postcss-cssnext');
const commonConfig = require('./webpack-base.config.js');
module.exports = webpackMerge(commonConfig, {
devtool: "source-map",
......
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack-base.config.js');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackExcludeAssetsPlugin = require('html-webpack-exclude-assets-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const commonConfig = require('./webpack-base.config.js');
module.exports = webpackMerge(commonConfig, {
devServer: {
......
#
# Build Genesys Catalog UI server on top of node:alpine
#
FROM node:8.1-alpine
FROM node:8.4-alpine
LABEL maintainer "Matija Obreza <matija.obreza@croptrust.org>"
# Instal npm dependencies and remove package.json
RUN npm install pm2 -g
ENV USER=nobody \
APP_PATH=/var/www/genesys-catalog-ui
COPY catalogui-pm2.yml ${APP_PATH}/
# Entrypoint
COPY ssr.sh /
# Application
COPY app ${APP_PATH}/
COPY catalogui-pm2.yml ${APP_PATH}/
# List image contents
RUN chmod +x /ssr.sh \
&& chmod go+r ${APP_PATH} \
&& find ${APP_PATH} -type f -exec ls -la {} \;
WORKDIR ${APP_PATH}
# Instal npm dependencies and remove package.json
RUN npm install pm2 -g \
&& npm i --production \
&& rm package.json
WORKDIR ${APP_PATH}/
ENTRYPOINT [ "/ssr.sh" ]
apps:
- script: 'server/index.js'
- script: 'server.js'
name: 'catalogui'
exec_mode: 'cluster'
instances: 3
log_type: 'raw'
cwd: '/var/www/genesys-catalog-ui'
cwd: '/var/www/genesys-catalog-ui/server'
env:
NODE_ENV: production
# Where to resolve modules from
# Where to resolve modules from -- not used anymore
NODE_PATH: src
'use strict';
/**
* gulp is used to compile the source, webpack it and prepare the
* code to be executed by nodejs.
*/
const del = require('del');
const path = require('path');
const gulp = require('gulp');
const debug = require('gulp-debug');
const ts = require('gulp-typescript');
const gutil = require('gulp-util');
const tsProject = ts.createProject('tsconfig.json');
gulp.task('scripts', () => {
const tsResult = tsProject.src().pipe(debug({title: 'Compiling src:'})).pipe(tsProject());
return tsResult.js.pipe(debug({title: 'Copying to target/app:'})).pipe(gulp.dest('target/app'));
});
gulp.task('clean', function(cb) {
// You can use multiple globbing patterns as you would with `gulp.src`
console.log('Cleaning ', path.join(__dirname, 'target/frontend-app'));
del([path.join(__dirname, 'target/frontend-app')], cb);
});
gulp.task('clean:dev', function(cb) {
// You can use multiple globbing patterns as you would with `gulp.src`
console.log('Cleaning ', path.join(__dirname, 'target/frontend-app-dev'));
del([path.join(__dirname, 'target/frontend-app-dev')], cb);
});
gulp.task('clean:target', function(cb) {
// You can use multiple globbing patterns as you would with `gulp.src`
console.log('Cleaning ', path.join(__dirname, 'target'));
del([path.join(__dirname, 'target')], cb);
});
gulp.task('clean:index_html', function(cb) {
// You can use multiple globbing patterns as you would with `gulp.src`
console.log('Removing ', path.join(__dirname, 'target/app/assets/index.html'));
del([path.join(__dirname, 'target/app/assets/index.html')], cb);
});
gulp.task('copy:package_json', () => {
return gulp.src(['package.json']).pipe(debug({title: 'Copying package.json:'})).pipe(gulp.dest('target/app'));
});
gulp.task('clean:all', ['clean:target']);
gulp.task('build:ssr', ['scripts']);
gulp.task('build:prod', ['scripts', 'copy:package_json']);
This diff is collapsed.
......@@ -18,15 +18,14 @@
],
"main": "index.js",
"scripts": {
"clean": "gulp clean",
"clean": "rimraf target",
"build": "webpack --config config/webpack-development.config.js",
"build:production": "cross-env NODE_ENV=production SSR=true webpack --config config/webpack-production.config.js",
"postbuild:production": "gulp clean:index_html",
"build:ssr": "gulp clean:all; gulp build:prod; npm run build:production",
"server": "webpack-dev-server --config config/webpack-development.config.js",
"server:production": "cross-env NODE_ENV=production webpack-dev-server --config config/webpack-production.config.js",
"server:ssr": "gulp clean:all; gulp build:ssr; cross-env SSR=true npm run build:production; cross-env SSR=true NODE_PATH=target/app/src node target/app/server/index.js",
"server:win": "cross-env CLIENT_ID=my-trusted-client CLIENT_SECRET=my-secret-client CATALOG_API_URL=http://localhost:8080 HOST=0.0.0.0 PORT=3000 npm run server"
"build:server": "cross-env NODE_ENV=production SSR=true webpack --config config/server.config.js",
"serve": "webpack-dev-server --config config/webpack-development.config.js",
"serve:production": "cross-env NODE_ENV=production webpack-dev-server --config config/webpack-production.config.js",
"build:ssr": "rimraf target && npm run build:production && npm run build:server",
"serve:ssr": "npm run build:ssr && cd target/app/server && cross-env SSR=true node server.js"
},
"dependencies": {
"autosuggest-highlight": "^3.1.0",
......@@ -50,7 +49,7 @@
"react-autosuggest": "^9.3.1",
"react-dom": "^16.0.0",
"react-fontawesome": "^1.6.1",
"react-leaflet": "^1.6.6",
"react-leaflet": "^1.8.0",
"react-markdown": "^3.0.0",
"react-redux": "^5.0.0",
"react-router": "^3.0.0",
......@@ -80,9 +79,6 @@
"es6-promise": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.0.0",
"gulp": "^3.0.0",
"gulp-debug": "^3.0.0",
"gulp-typescript": "^3.0.0",
"html-webpack-exclude-assets-plugin": "0.0.5",
"html-webpack-plugin": "^2.0.0",
"immutable": "^3.0.0",
......@@ -97,7 +93,7 @@
"postcss-loader": "^2.0.0",
"postcss-mixins": "^6.0.0",
"precss": "^1.0.0",
"react-hot-loader": "^4.0.0-beta.14",
"react-hot-loader": "^4.0.0-beta.15",
"resolve-url-loader": "^2.0.0",
"sass-loader": "^6.0.0",
"source-map-loader": "^0.2.0",
......
const ignoredProps = [ 'component', 'childRoutes', 'indexRoute', 'path', ]
const ignoredProps = ['component', 'childRoutes', 'indexRoute', 'path'];
export default function fetchComponentData(dispatch, components, renderProps) {
// console.log('renderProps.matchContext is', renderProps.matchContext);
// renderProps.routes.forEach((r) => {
// console.log('Route:', r);
// });
// console.log('renderProps.matchContext is', renderProps.matchContext);
// renderProps.routes.forEach((r) => {
// console.log('Route:', r);
// });
const promisesWithProps = [];
const promisesWithProps = [];
components.forEach((component, i) => {
if (! component || ! component.needs) {
// console.log(`Component #${i} does not have static needs`);
return;
}
console.log(`Component #${i} has static needs`);
components.forEach((component, i) => {
if (!component || !component.needs) {
// console.log(`Component #${i} does not have static needs`);
return;
}
console.log(`Component #${i} has static needs`);
const route = renderProps.routes[i];
const route = renderProps.routes[i];
// console.log(`Component #${i}`, component, '\nWith route', route);
// console.log('\nObject.keys()', Object.keys(route));
// console.log(`Component #${i}`, component, '\nWith route', route);
// console.log('\nObject.keys()', Object.keys(route));
const routeProps = {};
const routeProps = {};
// Add route properties (that are not ignored) to props
Object.keys(route).filter((key) => ignoredProps.indexOf(key) === -1).forEach((prop) => {
// console.log(`\t route.${prop}`, route[prop]);
routeProps[prop] = route[prop];
});
// console.log(`Component #${i} route.props`, routeProps);
// Add route properties (that are not ignored) to props
Object.keys(route).filter((key) => ignoredProps.indexOf(key) === -1).forEach((prop) => {
// console.log(`\t route.${prop}`, route[prop]);
routeProps[prop] = route[prop];
});
// console.log(`Component #${i} route.props`, routeProps);
// Provide params, location and route props to static needs
component.needs.map((need) => {
// console.log(`Dispatching for #${i}`, need, routeProps);
return dispatch(need({ params: renderProps.params, location: renderProps.location, route: routeProps }));
})
// Provide params, location and route props to static needs
component.needs.map((need) => {
// console.log(`Dispatching for #${i}`, need, routeProps);
return dispatch(need({ params: renderProps.params, location: renderProps.location, route: routeProps }));
})
// a nice array of Promises
.forEach((promise) => promisesWithProps.push(promise));
// console.log('\n\n');
});
// console.log('\n\n');
});
// console.log('All promises: ', promisesWithProps);
return Promise.all(promisesWithProps);
// console.log('All promises: ', promisesWithProps);
return Promise.all(promisesWithProps);
}
......@@ -2,29 +2,30 @@ import * as proxy from 'express-http-proxy';
import config from 'config';
const httpProxy = proxy(config.apiUrl, {
parseReqBody: false,
timeout: config.apiTimeout,
parseReqBody: false,
timeout: config.apiTimeout,
filter: function(req, res) {
if (req.url.startsWith('/api')) {
console.log(req.headers['authorization']);
return typeof req.headers['authorization'] !== 'undefined';
}
return true;
},
proxyReqPathResolver(req) {
let path = req.url;
if (path.startsWith('/oauth/token')) {
const grantType = req.query['grant_type'];
if(grantType === 'client_credentials' || grantType === 'password') {
path = `${path}&client_id=${config.clientId}&client_secret=${config.clientSecret}`;
}
} else if (path.startsWith('/google/verify-token')) {
path = `${path}&clientId=${config.clientId}`;
}
console.log(`HTTP proxy to ${config.apiUrl}${path}`);
return path;
},
filter: (req, res) => {
if (req.url.startsWith('/api')) {
console.log(req.headers.authorization);
return typeof req.headers.authorization !== 'undefined';
}
return true;
},
proxyReqPathResolver: (req) => {
let path = req.url;
if (path.startsWith('/oauth/token')) {
const grantType = req.query.grant_type;
if (grantType === 'client_credentials' || grantType === 'password') {
path = `${path}&client_id=${config.clientId}&client_secret=${config.clientSecret}`;
}
} else if (path.startsWith('/google/verify-token')) {
path = `${path}&clientId=${config.clientId}`;
}
console.log(`HTTP proxy to ${config.apiUrl}${path}`);
return path;
},
});
export default httpProxy;
import * as React from 'react';
import { renderToString } from 'react-dom/server';
import {RouterContext, match} from 'react-router';
import {RouterContext, browserHistory, match} from 'react-router';
import { createLocation } from 'history';
import { Provider } from 'react-redux';
import * as serialize from 'serialize-javascript';
import { browserHistory } from 'react-router';
import { routerMiddleware } from 'react-router-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
......@@ -86,7 +85,7 @@ const prerenderer = (html) => (req, res) => {
.then((template) => res.end(template))
.catch((err) => {
console.error('Error:', err);
res.end(err.message)
res.end(err.message);
});
});
};
......
const robots = (req, res) => {
const allow = process.env.ALLOW_ROBOTS === 'true' ? 'Allow: /': 'Disallow: /';
const allow = process.env.ALLOW_ROBOTS === 'true' ? 'Allow: /' : 'Disallow: /';
res.header('Cache-Control', 'public, max-age=600');
res.type('text/plain');
......
......@@ -14,7 +14,8 @@ import robots from './robots';
const app = express();
// This reads the ssr-template.html compiled to ssr-compiled.html by webpack
const html = readFileSync(path.join(__dirname, '../assets', 'ssr-compiled.html'), {encoding: 'utf8'});
console.log(`Reading SSR template from ${path.join('../assets', 'ssr-compiled.html')}`);
const html = readFileSync(path.join('../assets', 'ssr-compiled.html'), {encoding: 'utf8'});
// Log all requests
app.use((req, res, next) => {
......@@ -28,7 +29,7 @@ app.use('/proxy', httpProxy);
// robots.txt
app.get('/robots.txt', robots);
// Serve static resources (this should be the only thing publicly accessible)
app.use(express.static(path.join(__dirname, '../assets')));
app.use(express.static(path.join('../assets')));
app.use(cookieParser());
// Relay requests to React
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment