Commit 36c36e19 authored by Oleksii Savran's avatar Oleksii Savran

Embedded Genesys UI

parent 19e7f8df
......@@ -31,7 +31,8 @@
},
"workspaces": [
"workspaces/client",
"workspaces/ui-express"
"workspaces/ui-express",
"workspaces/ui-embedded"
],
"devDependencies": {
"lerna": "^3.22.0",
......
{
"presets": [
[
"env",
{
"modules": false,
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}
],
"react"
],
"plugins": [
"transform-object-rest-spread",
]
}
const webpackMerge = require('webpack-merge');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const commonConfig = require('./webpack-production.config.js');
module.exports = webpackMerge.smart(commonConfig, {
plugins: [
new BundleAnalyzerPlugin(),
],
});
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const HtmlWebpackExcludeAssetsPlugin = require('html-webpack-exclude-assets-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
// const CopyWebpackPlugin = require('copy-webpack-plugin');
// const fs = require('fs');
// devserver configuration
const HOST = process.env.HOST || 'localhost';
const PORT = process.env.PORT || 3000;
// other...
// const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const API_URL = process.env.API_URL || 'http://localhost:8080';
const CLIENT_ID = process.env.CLIENT_ID || 'defaultclient@localhost';
const CLIENT_SECRET = process.env.CLIENT_SECRET || 'changeme';
module.exports = {
stats: {
colors: true
},
devServer: {
hot: true,
inline: true,
contentBase: false,
compress: true,
port: PORT,
host: HOST,
watchOptions: {
ignored: /node_modules/
},
overlay: {
warnings: true,
errors: true
},
clientLogLevel: 'warning',
historyApiFallback: {
disableDotRule: true
},
proxy: {
'/proxy': {
target: API_URL,
logLevel: 'debug',
ws: true,
// secure: false,
pathRewrite(path, req) {
let p = path.replace('/proxy', '');
if (p.startsWith('/oauth/token')) {
const grantType = req.query.grant_type;
if (grantType === 'refresh_token') {
const refreshToken = req.query.refresh_token;
if (!refreshToken) {
// eslint-disable-next-line camelcase
req.query.grant_type = 'client_credentials';
p = p.replace('refresh_token', 'client_credentials');
}
}
if (grantType === 'client_credentials' || grantType === 'password' || grantType === 'refresh_token') {
p = `${p}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}`;
}
// We need some, if not all headers
// // remove all headers from request
// req.headers = {};
} else if (p.indexOf('/google/verify-token') === 0) {
p = `${p}&clientId=${CLIENT_ID}`;
}
// If authorization header is not provided, use access_token from cookie
// console.log('Cookies', req.headers.cookie, req);
if (! req.headers.authorization && req.headers.cookie) {
const authorization = req.headers.cookie.match(/access_token=([^;]+)/);
// console.log('Cookies', req.headers.cookie, authorization);
if (authorization && authorization.length === 2) {
console.log('Injecting Authorization header from cookies');
req.headers.authorization = 'Bearer ' + authorization[1];
}
}
// console.log('Outgoing headers ' + p, req.headers);
return p;
},
onError(err, req, res) {
console.log(err);
},
onClose(res, socket, head) {
// view disconnected websocket connections
console.log('Client disconnected', res);
}
}
},
},
entry: {
genesys: [ 'babel-polyfill', /* './entrypoints/vendor.ts', */ './entrypoints/client.tsx' ]
},
output: {
filename: '[name].[hash].js',
// chunkFilename: '[name].[hash].js',
path: path.join(process.cwd(), 'target/app/assets'),
publicPath: ''
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
modules: [path.resolve('./src'), 'node_modules'],
alias: {
'@genesys/client': path.resolve(__dirname, '../../client/src'),
'@assets': path.resolve(__dirname, '../assets'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
enforce: 'pre',
loader: 'eslint-loader',
exclude: /node_modules/,
},
{
test: /\.tsx?$/,
use: {
loader: 'awesome-typescript-loader',
},
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: false
},
},
'resolve-url-loader', {
loader: 'sass-loader',
options: {
sourceMap: true,
sassOptions: {
includePaths: ['node_modules']
}
}
}
]
},
{
test: /\.css$/,
exclude: /node_modules\/react\-toolbox/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.css$/,
include: /node_modules\/react\-toolbox/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
sourceMap: true,
localIdentName: '[path][name]__[local]'
}
}, 'resolve-url-loader', 'postcss-loader'
]
},
// fonts
{
test: /\.(woff|woff2|eot)(\?.*)?$/,
loader: 'file-loader?name=fonts/[hash].[ext]'
},
{
test: /\.ttf(\?.*)?$/,
loader: 'url-loader?limit=10000&mimetype=application/octet-stream&name=fonts/[hash].[ext]'
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
exclude: /node_modules\/leaflet/,
loader: 'url-loader?limit=100000&name=images/[name].[ext]'
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
include: /node_modules\/leaflet/,
loader: 'file-loader?limit=100000&name=images/[name].[ext]'
}
]
},
plugins: [
new ManifestPlugin({
basePath: '/html/'
}),
// Inject scripts to app's real index.html
new HtmlWebpackPlugin({
title: 'Genesys', // The title to use for the generated HTML document
filename: 'index.html', // The file to write the HTML to
// minify: {...}, // Pass a html-minifier options object to minify the output
xhtml: true,
template: './entrypoints/index.html',
// favicon: 'favicon.ico',
// localesMapping: fs.readFileSync('./generated/locales/localesMapping.json', {encoding: 'utf8'}),
}),
// Defer/Async scripts
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'async',
// async: [ 'genesys' ],
}),
// Keep CSS separate
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[name].[hash].css'
}),
new HtmlWebpackExcludeAssetsPlugin(),
// new CopyWebpackPlugin([
// { from: 'assets/images', to: 'images'},
// { from: 'assets/templates', to: 'templates'},
// { from: 'locales', to: 'locales'},
// { from: 'generated/locales', to: 'locales'},
// { from: '../../node_modules/leaflet/dist/images', to: 'images'},
// { from: '../../node_modules/ckeditor/', to: 'scripts/ckeditor'}
// ]),
],
externals: {
// Use external version of React
"react": "React",
"react-dom": "ReactDOM",
},
// optimization: {
// splitChunks: {
// chunks: 'all',
// cacheGroups: {
// vendor: {
// test: /[\\/]node_modules[\\/]/,
// name: 'vendors',
// chunks: 'all'
// }
// },
// },
// }
};
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const API_URL = process.env.API_URL || 'http://localhost:8080';
const CLIENT_ID = process.env.CLIENT_ID || 'defaultclient@localhost';
const CLIENT_SECRET = process.env.CLIENT_SECRET || 'changeme';
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const commonConfig = require('./webpack-base.config.js');
module.exports = webpackMerge.smart(commonConfig, {
mode: "development",
devtool: "source-map",
entry: {},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: false,
debug: true,
options: {
tslint: {
failOnHint: false
}
}
}),
new webpack.DefinePlugin({
'process.env': {
API_URL: JSON.stringify(API_URL),
CLIENT_ID: JSON.stringify(CLIENT_ID),
CLIENT_SECRET: JSON.stringify(CLIENT_SECRET),
GOOGLE_CLIENT_ID: JSON.stringify(GOOGLE_CLIENT_ID),
}
}),
new HtmlWebpackPlugin({
filename: 'index.html', // The file to write the HTML to
// minify: {...}, // Pass a html-minifier options object to minify the output
xhtml: true,
template: './entrypoints/index.html',
reactMode: 'development',
}),
// hot module replacement for webpack-dev-server
new webpack.HotModuleReplacementPlugin()
],
});
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MinifyPlugin = require('terser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const commonConfig = require('./webpack-base.config.js');
module.exports = webpackMerge.smart(commonConfig, {
mode: "production",
devServer: {
hot: false,
inline: false,
compress: true,
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
// https://facebook.github.io/react/docs/optimizing-performance.html#use-the-production-build
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
}
}),
new HtmlWebpackPlugin({
filename: 'index.html', // The file to write the HTML to
// minify: {...}, // Pass a html-minifier options object to minify the output
xhtml: true,
template: './entrypoints/index.html',
reactMode: 'production.min',
}),
],
optimization: {
noEmitOnErrors: true,
// splitChunks: {
// chunks: 'all',
// cacheGroups: {
// vendor: {
// test: /[\\/]node_modules[\\/]/,
// name: 'vendors',
// chunks: 'all'
// }
// },
// },
minimizer: [
//
new MinifyPlugin({
parallel: true,
terserOptions: {
ecma: undefined,
warnings: false,
parse: {},
compress: {},
mangle: true, // Note `mangle.properties` is `false` by default.
module: false,
output: null,
toplevel: false,
nameCache: null,
ie8: false,
keep_classnames: undefined,
keep_fnames: false,
safari10: false,
},
}),
// Minify CSS
new OptimizeCssAssetsPlugin({
// cssProcessor: require('cssnano'),
// cssProcessorOptions: {
// discardComments: {
// removeAll: true
// }
// },
// canPrint: true
}),
]
}
});
// pick appropriate config based on parameter
//
// We could also look at environment vars:
// const ENV = process.env.NODE_ENV === 'production' ? 'production' : 'development';
module.exports = function(env) {
return require('./webpack-' + env + '.config.js');
};
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { log } from '@genesys/client/utilities/debug';
import { configureBackendApi } from '@genesys/client/utilities/requestUtils';
import ApiInfoService from '@genesys/client/service/genesys/ApiInfoService';
import { ApiInfoDisplay } from 'ui/ApiInfoDisplay';
import { checkAccessTokens } from 'utilities';
// declare const window: Window & { devToolsExtension: any, __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any, initialLanguage: any, initialI18nStore: any, localeMapping: any };
const API_URL = process.env.API_URL || 'http://localhost:8080';
configureBackendApi({ apiUrl: API_URL });
async function init() {
try {
await checkAccessTokens(API_URL);
const apiInfo = await ApiInfoService.apiInfo();
ReactDOM.render(
<div>
<ApiInfoDisplay data={ apiInfo }/>
</div>,
document.getElementById('genesys'),
);
} catch (err) {
log('Oh, oh', err);
}
}
init();
<!DOCTYPE html>
<html>
<head>
<title>Embedded Genesys</title>
<base href="/" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes" />
<meta name="author" content="Genesys Team, helpdesk@genesys-pgr.org" />
</head>
<body>
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<div>
</div>
<div id="genesys"></div>
<!-- todo: change to minified in production -->
<script src="https://unpkg.com/react@16/umd/react.<%= htmlWebpackPlugin.options.reactMode %>.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.<%= htmlWebpackPlugin.options.reactMode %>.js" crossorigin></script>
</body>
</html>
{
"name": "@genesys/ui-embedded",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"clean": "rimraf lib",
"build": "rimraf target && yarn run build:production",
"build:production": "cross-env NODE_ENV=production webpack --config config/webpack-production.config.js",
"serve": "webpack-dev-server --config config/webpack-development.config.js",
"serve:production": "cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=8192 webpack-dev-server --config config/webpack-production.config.js",
"analyze": "webpack --config config/webpack-analyze.config.js"
},
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@types/react": "^16.9.33",
"cross-env": "^7.0.2",
"typescript": "^3.8.3",
"webpack": "^4.42.1",
"webpack-bundle-analyzer": "^3.8.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2"
}
}
import * as React from 'react';
import ApiInfo from '../../../client/src/model/ApiInfo';
export const ApiInfoDisplay = ({ data }: { data: ApiInfo }) => {
return (
<>
{ data && Object.keys(data).map((key) => (
<div key={ key }>
{ key }: { data[key] }
</div>
)) }
</>
);
};
import * as cookies from 'es-cookie';
import { LoginService } from '@genesys/client/service/LoginService';
import { clearCookies, saveCookies } from '@genesys/client/utilities';
import { configureBackendApi } from '@genesys/client/utilities/requestUtils';
import { log } from '@genesys/client/utilities/debug';
export function checkAccessTokens(apiUrl: string) {
const cookieToken: string = typeof window !== 'undefined' && cookies.get('access_token');
const applicationLogin = () =>
LoginService.loginApp()
.then((data) => {
console.log('loginApp token', data);
saveCookies(
{ access_token: data.access_token, authorities: 'ROLE_CLIENT'},
data.exp * 1000 || new Date().getTime() + data.expires_in * 1000,
apiUrl,
);
return configureBackendApi({ accessToken: data.access_token });
})
.catch((error) => {
log('Something went wrong', error);
});
if (cookieToken) {
return Promise.resolve();
} else {
clearCookies();
}
return applicationLogin();
};
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@genesys/client/*": [
"../client/src/*"
],
"*": [
"src/*",
"node_modules/*"
]
},
"outDir": "../../target",
"module": "esnext",
"target": "esnext",
"sourceMap": false,
"inlineSourceMap": true,
"experimentalDecorators": false,
"noUnusedParameters": false,
"noUnusedLocals": true,
"jsx": "react",
"moduleResolution": "node",
"importHelpers": true
},
"exclude": [
"node_modules",
"build",
"typings/main",
"typings/main.d.ts"
]
}
......@@ -1708,6 +1708,11 @@ acorn-walk@^6.0.0:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
acorn-walk@^7.1.1: